Lorsque j'essaie de compiler du code C qui utilise la gets()
fonction avec GCC, j'obtiens cet avertissement:
(.text + 0x34): avertissement: la fonction `gets 'est dangereuse et ne doit pas être utilisée.
Je me souviens que cela a quelque chose à voir avec la protection et la sécurité de la pile, mais je ne sais pas exactement pourquoi.
Comment puis-je supprimer cet avertissement et pourquoi existe-t-il un tel avertissement concernant l'utilisation gets()
?
Si gets()
c'est si dangereux, alors pourquoi ne pouvons-nous pas le supprimer?
c
fgets
buffer-overflow
gets
vinit dhatrak
la source
la source
gets()
Buffer_overflow_attackgets()
Réponses:
Pour utiliser en
gets
toute sécurité, vous devez savoir exactement combien de caractères vous lirez, afin de pouvoir créer un tampon suffisamment grand. Vous saurez seulement que si vous savez exactement quelles données vous lirez.Au lieu d'utiliser
gets
, vous voulez utiliserfgets
, qui a la signature(
fgets
, s'il lit une ligne entière, laissera le'\n'
dans la chaîne; vous devrez y faire face.)Il est resté une partie officielle du langage jusqu'à la norme ISO C de 1999, mais il a été officiellement supprimé par la norme de 2011. La plupart des implémentations C le supportent toujours, mais au moins gcc émet un avertissement pour tout code qui l'utilise.
la source
gets()
lequel le compilateur émet un avertissement lorsqu'il est utilisé.Pourquoi est
gets()
dangereuxLe premier ver Internet (le Morris Internet Worm ) s'est échappé il y a environ 30 ans (1988-11-02), et il a utilisé
gets()
un débordement de tampon comme l'une de ses méthodes de propagation d'un système à l'autre. Le problème de base est que la fonction ne sait pas quelle est la taille du tampon, donc elle continue de lire jusqu'à ce qu'elle trouve une nouvelle ligne ou rencontre EOF, et peut déborder les limites du tampon qui lui a été donné.Vous devez oublier que vous avez déjà entendu dire que cela
gets()
existait.La norme C11 ISO / IEC 9899: 2011 a été éliminée en
gets()
tant que fonction standard, qui est A Good Thing ™ (elle était formellement marquée comme «obsolète» et «obsolète» dans ISO / IEC 9899: 1999 / Cor.3: 2007 - Rectificatif technique 3 pour C99, puis supprimé dans C11). Malheureusement, il restera dans les bibliothèques pendant de nombreuses années (ce qui signifie «décennies») pour des raisons de rétrocompatibilité. Si cela ne tenait qu'à moi, la mise en œuvre degets()
deviendrait:Étant donné que votre code se bloquera de toute façon, tôt ou tard, il est préférable de régler le problème plus tôt que tard. Je serais prêt à ajouter un message d'erreur:
Les versions modernes du système de compilation Linux génèrent des avertissements si vous établissez un lien
gets()
- et également pour certaines autres fonctions qui ont également des problèmes de sécurité (mktemp()
,…).Alternatives à
gets()
fgets ()
Comme tout le monde l' a déjà dit, l'alternative canonique
gets()
est enfgets()
spécifiantstdin
que le flux de fichiers.Ce que personne d'autre n'a encore mentionné, c'est qu'il
gets()
n'inclut pas la nouvelle ligne mais lefgets()
fait. Ainsi, vous devrez peut-être utiliser un wrapperfgets()
qui supprime la nouvelle ligne:Ou mieux:
De plus, comme le souligne caf dans un commentaire et paxdiablo le montre dans sa réponse,
fgets()
vous pourriez avoir des données sur une ligne. Mon code wrapper laisse ces données à lire la prochaine fois; vous pouvez facilement le modifier pour engloutir le reste de la ligne de données si vous préférez:Le problème résiduel est de savoir comment signaler les trois états de résultat différents - EOF ou erreur, ligne lue et non tronquée, et ligne partielle lue mais les données ont été tronquées.
Ce problème ne se pose pas
gets()
car il ne sait pas où se termine votre tampon et piétine joyeusement au-delà de la fin, faisant des ravages sur votre disposition de mémoire magnifiquement entretenue, gâchant souvent la pile de retour (un débordement de pile ) si le tampon est alloué sur la pile, ou le piétinement sur les informations de contrôle si le tampon est alloué dynamiquement, ou la copie de données sur d'autres précieuses variables globales (ou module) si le tampon est alloué statiquement. Rien de tout cela n'est une bonne idée - ils incarnent l'expression «comportement indéfini».Il existe également le TR 24731-1 (rapport technique du comité de normalisation C) qui offre des alternatives plus sûres à une variété de fonctions, notamment
gets()
:Les compilateurs Microsoft Visual Studio implémentent une approximation de la norme TR 24731-1, mais il existe des différences entre les signatures implémentées par Microsoft et celles du TR.
La norme C11, ISO / IEC 9899-2011, inclut TR24731 dans l'annexe K en tant que partie facultative de la bibliothèque. Malheureusement, il est rarement implémenté sur des systèmes de type Unix.
getline()
- POSIXPOSIX 2008 fournit également une alternative sûre aux
gets()
appelsgetline()
. Il alloue de l'espace de manière dynamique à la ligne, vous avez donc besoin de la libérer. Il supprime donc la limitation de la longueur de ligne. Il renvoie également la longueur des données lues, ou-1
(et nonEOF
!), Ce qui signifie que les octets nuls dans l'entrée peuvent être traités de manière fiable. Il existe également une variante «choisissez votre propre délimiteur à caractère unique» appeléegetdelim()
; cela peut être utile si vous traitez la sortie d'find -print0
où les extrémités des noms de fichiers sont marquées avec un caractère ASCII NUL'\0'
, par exemple.la source
fgets()
et votrefgets_wrapper()
version laissera la partie de fin d'une ligne trop longue dans le tampon d'entrée, pour être lue par la prochaine fonction d'entrée. Dans de nombreux cas, vous souhaiterez lire et supprimer ces caractères.getline()
et son parentgetdelim()
, qui renvoient la longueur de la «ligne» lue par les commandes, en allouant l'espace nécessaire pour pouvoir stocker la ligne entière. Même cela peut poser des problèmes si vous vous retrouvez avec un fichier JSON à une seule ligne de plusieurs gigaoctets; pouvez-vous vous permettre toute cette mémoire? (Et pendant que nous y sommes, nous pouvons avoirstrcpy()
et lesstrcat()
variantes qui renvoient un pointeur sur l'octet nul à la fin etc.?)fgets()
est que si le fichier contient un octet nul, vous ne pouvez pas dire combien de données il y a après l'octet nul jusqu'à la fin de la ligne (ou EOF).strlen()
peut uniquement signaler jusqu'à l'octet nul dans les données; après cela, c'est une conjecture et donc presque certainement une erreur.gets()
existait." Quand je fais cela, je le rencontre à nouveau et je reviens ici. Piratez-vous stackoverflow pour obtenir des votes positifs?Parce
gets
qu'il ne fait aucune vérification en récupérant des octets de stdin et en les plaçant quelque part. Un exemple simple:Maintenant, tout d'abord, vous êtes autorisé à entrer le nombre de caractères que vous voulez,
gets
cela ne vous importera pas. Deuxièmement, les octets sur la taille du tableau dans lequel vous les placez (dans ce casarray1
) écraseront tout ce qu'ils trouveront en mémoire cargets
ils les écriront. Dans l'exemple précédent, cela signifie que si vous saisissez"abcdefghijklmnopqrts"
peut-être, de manière imprévisible, il sera également écraséarray2
ou autre.La fonction n'est pas sûre car elle suppose une entrée cohérente. NE L'UTILISEZ JAMAIS!
la source
gets
carrément inutilisable, c'est qu'il n'a pas de paramètre de longueur / nombre de tableaux qu'il prend; s'il avait été là, ce serait juste une autre fonction standard C ordinaire.gets
servait l' usage et pourquoi aucune variante standard de fgets n'a été aussi pratique pour les cas d'utilisation où la nouvelle ligne n'est pas souhaitée dans le cadre de l'entrée?gets
été, comme son nom l'indique, conçu pour obtenir une chaînestdin
, mais la raison pour ne pas avoir de paramètre de taille peut provenir de l'esprit de C : Trust the programmer. Cette fonction a été supprimée en C11 et le remplacement donnégets_s
prend la taille du tampon d'entrée. Je n'ai cependant aucune idée de lafgets
partie.gets
pourrait être excusable serait si l'on utilisait un système d'E / S à mémoire tampon matérielle qui était physiquement incapable de soumettre une ligne sur une certaine longueur, et la durée de vie prévue du programme était plus courte que la durée de vie du matériel. Dans ce cas, si le matériel est incapable de soumettre des lignes de plus de 127 octets, cela peut être justifiégets
dans un tampon de 128 octets, bien que je pense que les avantages de pouvoir spécifier un tampon plus court lorsque vous attendez une entrée plus petite justifieraient plus que Coût.gets
etstrcat
d'accepter en toute sécurité autant que cela conviendrait.Vous ne devez pas l'utiliser
gets
car il n'a aucun moyen d'arrêter un débordement de tampon. Si l'utilisateur tape plus de données que ne peut en contenir votre tampon, vous vous retrouverez très probablement avec une corruption ou pire.En fait, l'ISO a en fait pris la décision de retirer
gets
de la norme C (à partir de C11, bien qu'elle soit déconseillée en C99) qui, étant donné le taux élevé de compatibilité ascendante, devrait être une indication de la gravité de cette fonction.La bonne chose à faire est d'utiliser la
fgets
fonction avec lestdin
descripteur de fichier car vous pouvez limiter les caractères lus par l'utilisateur.Mais cela a aussi ses problèmes tels que:
À cette fin, presque tous les codeurs C à un moment donné de leur carrière écriront également un wrapper plus utile
fgets
. Voici la mienne:avec du code de test:
Il offre les mêmes protections que
fgets
dans la mesure où il empêche les débordements de tampon mais il informe également l'appelant de ce qui s'est passé et efface les caractères en excès afin qu'ils n'affectent pas votre prochaine opération d'entrée.N'hésitez pas à l'utiliser comme vous le souhaitez, je le libère sous la licence "faites ce que vous voulez bien" :-)
la source
gets()
ni dans la section 7.19.7.7 où elle est définie, ni dans la section 7.26.9 Orientations futures de la bibliothèque et la sous-section pour<stdio.h>
. Il n'y a même pas de note de bas de page indiquant qu'il est dangereux. (Cela dit, je vois "C'est obsolète dans ISO / IEC 9899: 1999 / Cor.3: 2007 (E))" dans la réponse de Yu Hao .) Mais C11 l'a supprimé de la norme - et pas avant l'heure!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
cache lasize_t
à laint
conversion desz
.sz > INT_MAX || sz < 2
attraperait des valeurs étranges desz
.if (buff[strlen(buff)-1] != '\n') {
est un exploit de pirate, car le premier caractère saisi par l'utilisateur malveillant pourrait être un caractère nul intégré rendantbuff[strlen(buff)-1]
UB.while (((ch = getchar())...
a des problèmes si un utilisateur entre un caractère nul.fgets .
Pour lire à partir du stdin:
la source
Vous ne pouvez pas supprimer les fonctions de l'API sans casser l'API. Si vous le faites, de nombreuses applications ne seront plus compilées ou exécutées du tout.
C'est la raison pour laquelle une référence donne:
la source
J'ai lu récemment, dans un article USENET à
comp.lang.c
, quigets()
est en train d'être supprimé de la norme. WOOHOOla source
gcc -std=c2012 -pedantic ...
gets () ne passera pas. (Je viens de composer le-std
paramètre)Dans C11 (ISO / IEC 9899: 201x),
gets()
a été supprimé. (Il est déconseillé dans ISO / IEC 9899: 1999 / Cor.3: 2007 (E))De plus
fgets()
, C11 présente une nouvelle alternative sûregets_s()
:Cependant, dans la section Pratique recommandée ,
fgets()
est toujours préférable.la source
gets()
est dangereux car il est possible pour l'utilisateur de planter le programme en tapant trop dans l'invite. Il ne peut pas détecter la fin de la mémoire disponible, donc si vous allouez une quantité de mémoire trop petite à cet effet, cela peut provoquer une erreur de segmentation et un crash. Parfois, il semble très improbable qu'un utilisateur tape 1000 lettres dans une invite destinée au nom d'une personne, mais en tant que programmeurs, nous devons rendre nos programmes à l'épreuve des balles. (cela peut également représenter un risque pour la sécurité si un utilisateur peut planter un programme système en envoyant trop de données).fgets()
vous permet de spécifier le nombre de caractères à retirer du tampon d'entrée standard, afin qu'ils ne dépassent pas la variable.la source
Je voudrais adresser une invitation sérieuse à tous les responsables de bibliothèques C qui sont toujours inclus
gets
dans leurs bibliothèques "juste au cas où quelqu'un en dépendrait encore": veuillez remplacer votre implémentation par l'équivalent deCela vous aidera à vous assurer que personne n'en dépend encore. Je vous remercie.
la source
La fonction C gets est dangereuse et a été une erreur très coûteuse. Tony Hoare le souligne pour une mention spécifique dans son discours "Null References: The Billion Dollar Mistake":
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Toute l'heure mérite d'être regardée, mais pour ses commentaires, la vue à partir de 30 minutes avec le détail obtient une critique d'environ 39 minutes.
J'espère que cela vous mettra en appétit pour toute la discussion, ce qui attire l'attention sur la façon dont nous avons besoin de preuves de correction plus formelles dans les langues et sur la façon dont les concepteurs de langues devraient être blâmés pour les erreurs dans leurs langues, pas sur le programmeur. Cela semble avoir été la raison douteuse pour les concepteurs de mauvais langages de rejeter la faute sur les programmeurs sous le couvert de la «liberté du programmeur».
la source