Pour lire les lignes d'un fichier, il y a les fonctions getline()
et fgets()
POSIX (en ignorant les redoutables gets()
). Il est logique que getline()
soit préféré à fgets()
car il alloue le tampon de ligne selon les besoins.
Ma question est: N'est-ce pas dangereux? Que se passe-t-il si par accident ou intention malveillante quelqu'un crée un fichier de 100 Go sans octet '\n'
- cela ne fera-t-il pas que mon appel getline()
allouera une quantité insensée de mémoire?
Ma question est: N'est-ce pas dangereux? Que se passe-t-il si par accident ou intention malveillante quelqu'un crée un fichier de 100 Go sans octet "\ n" - ne fera-t-il pas que mon appel getline () alloue une quantité folle de mémoire?
Oui, ce que vous décrivez est un risque plausible. cependant,
getline()
d'essayer de le faire n'est pas intrinsèquement plus risqué que d'écrire votre propre code pour le faire avec fgets()
; etsetrlimit()
pour limiter la quantité totale de mémoire (virtuelle) qu'il peut réserver. Cela peut être utilisé pour provoquer son échec au lieu d'allouer avec succès suffisamment de mémoire pour interférer avec le reste du système.Le mieux dans l'ensemble, je dirais, est d'écrire du code qui ne nécessite pas d'entrée en unités de lignes complètes (en une seule fois) en premier lieu, mais une telle approche a ses propres complexités.
Cela peut être dangereux, oui. Je ne sais pas comment cela fonctionnerait sur d'autres ordinateurs, mais l'exécution du code ci-dessous a gelé mon ordinateur au point d'avoir besoin d'une réinitialisation matérielle:
/* DANGEROUS CODE */
#include <stdio.h>
int main(void)
{
FILE *f;
char *s;
size_t n = 0;
f = fopen("/dev/zero", "r");
getline(&s, &n, f);
return 0;
}
La fonction getline
utilise malloc
et realloc
en interne et renvoie -1 en cas d'échec, le résultat n'est donc pas différent de celui que vous avez tenté d'appeler malloc(100000000000)
. À savoir, errno
est défini sur ENOMEM
et getline
renvoie -1.
Vous auriez donc le même problème si vous utilisiez getline
ou tentiez de faire la même chose avec fgets
et l'allocation de mémoire manuelle pour vous assurer de lire une ligne complète.
Certaines directives de codage (comme MISRA C) peuvent vous empêcher d'utiliser des allocations dynamiques de mémoire (comme getline()
). Il y a des raisons à cela, par exemple en évitant les fuites de mémoire.
Si vous connaissez la taille maximale de toutes les lignes acceptables, vous pouvez éviter les allocations de mémoire en utilisant fgets()
au lieu de getline()
, et ainsi supprimer un point de fuite mémoire potentiel.
Cela dépend vraiment de la façon dont vous voulez gérer les lignes trop longues.
fgets
avec un tampon de taille décente fonctionnera généralement, et vous pouvez détecter qu'il a "échoué" - la fin du tampon n'a pas de caractère de nouvelle ligne. Il est possible d'éviter de toujours faire un strlen () pour confirmer si le tampon est débordé, mais c'est une question différente.
Peut-être que votre stratégie consiste simplement à ignorer les lignes qui ne peuvent pas être traitées, ou peut-être que le reste de la ligne n'est qu'un commentaire que vous ignoreriez de toute façon, auquel cas, il est facile de mettre ensuite fgets
dans une boucle pour éliminer le reste de la ligne sans pénalité d'allocation.
Si vous voulez lire la ligne entière, alors getline
peut être la meilleure stratégie pour vous. L'utilisateur malveillant aurait besoin de beaucoup d'espace disque pour provoquer le mauvais comportement que vous décrivez, ou peut-être passer/dev/random ou similaire comme nom de fichier d'entrée.
Encore une fois, si getline
ne peut pas être réaffecté, il échouera de manière à ce que vous puissiez récupérer, bien que si vous réutilisez le tampon pour plusieurs lectures de ligne, vous souhaiterez peut-être libérer le tampon qu'il possède après un erreur avant d'essayer de lire davantage, car elle est toujours allouée et peut avoir augmenté autant qu'elle le pouvait avant d'échouer.
getline()
réaffectez le tampon pour vous permettre d'alléger un peu la gestion de la mémoire dans votre programme.
Mais en effet, cela pourrait entraîner l'allocation d'une grande partie de la mémoire. Si c'est un problème, vous devez prendre des mesures supplémentaires pour utiliser des fonctions qui n'allouent pas de mémoire implicitement.