Comment prévenir scanf causant un débordement de mémoire tampon dans C?
J'utilise ce code:
while ( scanf("%s", buf) == 1 ){
Quelle serait la meilleure façon de prévenir un éventuel débordement de la mémoire tampon, de sorte qu'il peut être passé des chaînes de longueurs irrégulières?
Je sais que je peux limite de la chaîne d'entrée en appelant par exemple:
while ( scanf("%20s", buf) == 1 ){
Mais je préfère être en mesure de traiter ce que les entrées de l'utilisateur.
Ou peut-il pas être fait en toute sécurité à l'aide de scanf et que je devrais utiliser fgets?
Vous devez vous connecter pour publier un commentaire.
Dans leur livre La Pratique de la Programmation (qui est bien la peine de lire), Kernighan et le Brochet de discuter de ce problème, et de le résoudre en utilisant
snprintf()
pour créer la chaîne avec la bonne taille de la mémoire tampon pour passer à lascanf()
famille de fonctions. En effet:Note, ce qui limite encore l'entrée à la taille fournie comme "tampon". Si vous avez besoin de plus d'espace, alors vous devez faire de l'allocation de mémoire, ou de l'utilisation non-standard de la fonction de bibliothèque qui ne l'allocation de mémoire pour vous.
Noter que la POSIX 2008 (2013) la version de la
scanf()
famille de fonctions prend en charge un format de modificateurm
(affectation de l'allocation de caractères) pour la chaîne entrées (%s
,%c
,%[
). Au lieu de prendre unechar *
argument, il faut unchar **
argument, et il alloue l'espace nécessaire pour la valeur qu'elle lit:Si le
sscanf()
fonction ne parvient pas à satisfaire à toutes les spécifications de conversion, puis toute la mémoire qui lui est allouée pour%ms
-comme les conversions est libéré avant que la fonction retourne.buflen-1
— je Vous Remercie. Ensuite, vous avez à vous soucier de unsigned underflow (emballage pour un assez grand nombre), d'où laif
test. Je serais bien tenté de la remplacer avec uneassert()
, ou de le sauvegarder avec unassert()
avant laif
que les feux de cours du développement, si quelqu'un est assez imprudente pour passer de 0 à la taille. Je n'ai pas examiné attentivement la documentation pour savoir ce qu'%0s
moyens desscanf()
— le test peut-être mieux commeif (buflen < 2)
.snprintf
écrit des données dans un tampon de chaîne, etsscanf
lit de qui a créé la chaîne. Où, exactement, est-ce à remplacerscanf
en ce qu'il lit l'entrée standard stdin?snprintf
pourtant, ce n'est pas le paramètre format.data
et doncsscanf()
est approprié. Si vous voulez lire l'entrée standard au lieu de cela, déposez ledata
de paramètres et appelscanf()
à la place. Comme pour le choix du nomformat
pour la variable, qui devient la chaîne de format dans l'appel àsscanf()
, vous avez le droit de le renommer si vous le souhaitez, mais son nom n'est pas inexact. Je ne suis pas sûr de ce que la variante a du sens; ne serait -in_format
le rendre plus clair? Je ne suis pas la planification de la changer dans ce code, vous pouvez, si vous utilisez cette idée dans votre propre code.snprintf()
prend le nombre debuflen
(disons 256), et crée une chaîne de caractères dans la variableformat
qui est équivalent à"%255s"
. Puissscanf()
lit à partir de la chaîne dedata
à l'aide deformat
comme chaîne de format (spécification de conversion) pour contrôler la façon dont les données sont interprétées, avec le résultat étant écrite dans la variablebuffer
. Ainsi, le premier espace blanc dansdata
est ignoré et le premier " mot " est copié dansbuffer
sans risque de débordement de la mémoire tampon, tant que la zone pointée parbuffer
a au moins autant d'espace quebuflen
dit qu'il a fait.scanf()
sur macOS n'est pas documentée que le soutien%ms
, si utiles qu'il serait.Si vous utilisez gcc, vous pouvez utiliser la GNU-extension
a
spécificateur d'avoir scanf() alloue de la mémoire pour que vous maintenez enfoncé le d'entrée:Edit: Que Jonathan l'a souligné, vous devriez consulter le
scanf
homme de pages que le prescripteur peut être différent (%m
) et vous pourriez avoir besoin pour permettre à certaines définit lors de la compilation.m
modificateur de faire le même travail. Voirscanf()
. Vous aurez besoin de vérifier si les systèmes que vous utilisez ne l'appui de cette modification.%ms
. La notation%a
est synonyme de%f
(sur la production, il demande hexadécimal de données à virgule flottante). La GNU page de man pourscanf()
dit: _ Il n'est pas disponible si le programme est compilé avecgcc -std=c99
ou gcc -D_ISOC99_SOURCE (sauf_GNU_SOURCE
est également spécifié), auquel cas lea
est interprété comme un rédacteur de devis pour les nombres à virgule flottante (voir ci-dessus)._La plupart du temps une combinaison de
fgets
etsscanf
fait le travail. L'autre chose serait d'écrire votre propre analyseur, si l'entrée est bien formaté. Notez également votre deuxième exemple a besoin d'un peu de modification pour être utilisé en toute sécurité:Ci-dessus rejette le flux d'entrée jusqu'à, mais pas y compris le retour à la ligne (
\n
) caractère. Vous aurez besoin d'ajouter ungetchar()
à consommer. Également vérifier si vous avez atteint la fin-de-stream:et c'est à ce sujet.
feof
code dans un contexte plus large? Je me demande depuis que la fonction est souvent utilisée à tort.array
doit êtrechar array[LENGTH+1];
Directement à l'aide de
scanf(3)
et de ses variantes, qui pose un certain nombre de problèmes. Généralement, les utilisateurs et les non-interactive de cas d'utilisation sont définis en termes de lignes de commentaires. Il est rare de voir un cas où, si suffisamment d'objets ne sont pas trouvés, d'autres lignes permettra de résoudre le problème, mais c'est le mode par défaut pour le scanf. (Si un utilisateur ne savais pas pour entrer un numéro sur la première ligne, de deuxième et de troisième ligne ne sera probablement pas aider.)Au moins si vous
fgets(3)
vous savez combien de lignes d'entrée de votre programme aura besoin, et vous n'aurez pas de dépassements de la mémoire tampon...Limitation de la longueur de l'entrée n'est certainement plus facile. Vous pouvez accepter un arbitrairement au long de la saisie par l'utilisation d'une boucle, la lecture d'un bit à la fois, ré-affectation de l'espace pour la chaîne de caractères que nécessaire...
Mais c'est beaucoup de travail, de sorte que la plupart des programmeurs C juste couper l'entrée à une certaine longueur arbitraire. Je suppose que vous le savez déjà, mais l'utilisation de fgets() ne va pas vous permettre d'accepter l'arbitraire quantités de texte - vous allez encore avoir besoin de fixer une limite.
realloc()
ing de votre tampon.Ce n'est pas que beaucoup de travail pour rendre une fonction de l'allocation de la mémoire nécessaire pour votre chaîne.
C'est un peu c-la fonction que j'ai écrit il y a quelques temps, j'ai toujours l'utiliser pour lire dans les chaînes.
Il sera de retour le lire une chaîne ou si une erreur de mémoire se produit NULLE.
Mais soyez conscient que vous avez à free() de votre chaîne et de toujours vérifier pour la valeur de retour.
sizeof (char)
est, par définition,1
. Vous n'avez pas besoin de lui ici.strerror(3)
) ou de s'attendre à un pré-alloués chaîne transmise (commestrerror_r(3)
- ouscanf(3)
) ...