89 votes

Inconvénients de scanf

Je veux savoir quel désavantage de scanf() existe.

Dans de nombreux sites, j'ai lu que l'utilisation de scanf entraînerait parfois un débordement de tampon. Quelle en est la raison et y a-t-il d'autres inconvénients avec scanf ?

85voto

AndreyT Points 139512

La plupart des réponses semblent se concentrer sur la chaîne de débordement de la mémoire tampon d'émission. En réalité, les spécificateurs de format qui peut être utilisé avec scanf les fonctions de soutien explicite de la largeur de champ , ce qui limite la taille maximale de l'entrée et de prévenir le débordement de la mémoire tampon. Ce qui rend le populaire accusations de chaîne-dépassement de la mémoire tampon les dangers présents dans scanf pratiquement sans fondement. Affirmant qu' scanf est en quelque sorte analogue à l' gets dans le respect n'est plus tout à fait incorrecte. Il y a une importante différence qualitative entre scanf et gets: scanf ne fournir à l'utilisateur avec de la ficelle-buffer-overflow-la prévention des fonctionnalités, en gets ne le fait pas.

On peut dire que ces scanf caractéristiques sont difficiles à utiliser, car la largeur de champ doit être intégré dans la chaîne de format (il n'y a pas moyen de passer au travers d'un variadic argument, comme cela peut être fait en printf). Qui est réellement vrai. scanf est en effet plutôt mal conçue à cet égard. Mais néanmoins, toute réclamation scanf est en quelque sorte irrémédiablement brisé à l'égard de la chaîne-buffer overflow de sécurité sont complètement faux et généralement faites par des programmeurs paresseux.

Le vrai problème avec scanf a une nature totalement différente, même si elle est aussi à propos de débordement. Lors de l' scanf fonction est utilisée pour la conversion de décimal représentations des nombres dans les valeurs de l'arithmétique types, il n'offre aucune protection de dépassement de capacité arithmétique. Si dépassement de capacité se produit, scanf produit un comportement indéfini. Pour cette raison, la seule bonne façon d'effectuer la conversion de la bibliothèque standard C est des fonctions de strto... de la famille.

Donc, pour résumer ce qui précède, le problème avec scanf , c'est qu'il est difficile (mais possible) de l'utiliser correctement et en toute sécurité avec des tampons de chaîne. Et il est impossible de l'utiliser en toute sécurité pour l'arithmétique d'entrée. Ce dernier est le véritable problème. Le premier est juste un inconvénient.

P. S. ci-dessus dans le but de permettre à toute la famille de scanf fonctions (y compris aussi l' fscanf et sscanf). Avec scanf plus précisément, le problème évident est que l'idée même de l'aide d'une stricte fonction formaté pour la lecture potentiellement interactif d'entrée est plutôt discutable.

71voto

paxdiablo Points 341644

Les problèmes avec scanf (au minimum):

  • à l'aide de %s pour obtenir une chaîne de caractères à partir de l'utilisateur, ce qui conduit à la possibilité que la chaîne peut être plus longue que votre tampon, causant un débordement.
  • la possibilité d'un échec de l'analyse de quitter votre pointeur de fichier dans une durée indéterminée emplacement.

Je préfère de beaucoup l'aide d' fgets à lire les lignes de sorte que vous pouvez limiter la quantité de données à lire. Si vous avez un 1K de mémoire tampon, et vous lire une ligne dans il avec fgets vous pouvez dire si la ligne est trop longue par le fait il n'y a pas de résiliation de caractère de saut de ligne (dernière ligne d'un fichier sans un retour à la ligne malgré).

Alors vous pouvez vous plaindre à l'utilisateur, ou d'allouer plus d'espace pour le reste de la ligne (en continu si nécessaire jusqu'à ce que vous disposez de suffisamment d'espace). Dans les deux cas, il n'y a pas de risque de débordement de la mémoire tampon.

Une fois que vous avez lu la ligne, vous savez que vous êtes positionné à la ligne suivante, donc il n'y a pas de problème. Vous pouvez alors sscanf votre chaîne au contenu de votre coeur sans avoir à les enregistrer et de restaurer le pointeur de fichier pour la re-lecture.

Voici un extrait de code que j'utilise fréquemment pour s'assurer qu'aucun dépassement de la mémoire tampon lors de la demande de l'utilisateur pour plus d'informations.

Il pourrait être facilement ajustés pour utiliser un autre fichier que l'entrée standard si nécessaire et vous pourriez aussi avoir d'attribuer ses propres tampon (et continuer à augmenter jusqu'à ce qu'il est assez grand) avant de donner que de retour à l'appelant (bien que l'appelant serait alors responsable de la libération, bien sûr).

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

 

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        // Extra NL since my system doesn't output that on EOF.
        printf ("\nNo input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long [%s]\n", buff);
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

Un essai:

$ ./tstprg
Enter string>[CTRL-D]
No input

$ ./tstprg
Enter string> a
OK [a]

$ ./tstprg
Enter string> hello
OK [hello]

$ ./tstprg
Enter string> hello there
Input too long [hello the]

$ ./tstprg
Enter string> i am pax
OK [i am pax]

22voto

jamesdlin Points 13455

À partir de la comp.lang.c FAQ: Pourquoi tout le monde dis de ne pas utiliser scanf? Que dois-je utiliser à la place?

scanf a un certain nombre de problèmes-voir les questions 12.17, 12.18 un, et 12.19. Aussi, son %s format a le même problème que gets() a (voir la question 12.23)-il est difficile de garantir que le tampon de réception ne débordera pas. [note de bas de page]

Plus généralement, scanf est conçu pour être relativement structurée, mise en forme d'entrée (son nom est en fait un dérivé de "scan formaté"). Si vous prêtez attention, il vous dira s'il a réussi ou échoué, mais il peut vous dire, seulement approximativement à l'endroit où il a échoué, et pas du tout comment ou pourquoi. Vous avez très peu de chances d'en faire toute erreur de récupération.

Pourtant, l'utilisateur interactif d'entrée est le moins structuré d'entrée, il est. Une interface utilisateur conçue pour permettre la possibilité pour l'utilisateur de taper juste au sujet de n'importe quoi-et pas seulement des lettres ou des signes de ponctuation lorsque les chiffres étaient attendus, mais aussi plus ou moins de caractères que prévu, ou aucun caractère à tous (c'est à dire, juste le RETOUR de la clé), prématuré ou expressions du FOLKLORE ou de quoi que ce soit. Il est presque impossible de s'occuper gracieusement avec tous ces problèmes potentiels lors de l'utilisation d' scanf; il est beaucoup plus facile de lire les lignes entières (avec fgets par exemple), puis de les interpréter, soit à l'aide d' sscanf ou d'autres techniques. (Fonctions comme strtol, strtok, et atoi sont souvent utiles; voir également les questions de 12,16 et 13.6.) Si vous en utilisez scanf variante, assurez-vous de vérifier la valeur de retour pour s'assurer que le nombre d'éléments ont été trouvés. Aussi, si vous utilisez %s, assurez-vous de la garde contre le dépassement de la mémoire tampon.

Noter, en passant, que les critiques de l' scanf ne sont pas nécessairement mises en accusation de fscanf et sscanf. scanf lit à partir de stdin, ce qui est généralement interactif clavier et qui est donc le moins de contraintes, conduisant à la plupart des problèmes. Quand un fichier de données est un format connu, d'autre part, il peut être approprié de le lire avec fscanf. Il est parfaitement approprié pour analyser les chaînes avec sscanf (tant que la valeur de retour est cochée), parce que c'est tellement facile de reprendre le contrôle, redémarrez la recherche, jetez-le d'entrée si elle n'a pas de match, etc.

Autres liens:

Références: K&R2 Secondes de 7,4 p. 159

6voto

Alok Singhal Points 33073

Il est très difficile d'obtenir des scanf pour faire la chose que vous voulez. Bien sûr, vous pouvez, mais des choses comme scanf("%s", buf); sont aussi dangereux que gets(buf);, comme tout le monde l'a dit.

Comme un exemple, ce que paxdiablo est en train de faire dans sa fonction de lecture peut être fait avec quelque chose comme:

scanf("%10[^\n]%*[^\n]", buf));
getchar();

Permettra de lire une ligne, stocker les 10 premiers non-caractères de saut de ligne en buf, puis de supprimer tout jusqu'à (et y compris) un retour à la ligne. Donc, paxdiablo de la fonction peut être écrite à l'aide d' scanf de la façon suivante:

#include <stdio.h>

enum read_status {
    OK,
    NO_INPUT,
    TOO_LONG
};

static int get_line(const char *prompt, char *buf, size_t sz)
{
    char fmt[40];
    int i;
    int nscanned;

    printf("%s", prompt);
    fflush(stdout);

    sprintf(fmt, "%%%zu[^\n]%%*[^\n]%%n", sz-1);
    /* read at most sz-1 characters on, discarding the rest */
    i = scanf(fmt, buf, &nscanned);
    if (i > 0) {
        getchar();
        if (nscanned >= sz) {
            return TOO_LONG;
        } else {
            return OK;
        }
    } else {
        return NO_INPUT;
    }
}

int main(void)
{
    char buf[10+1];
    int rc;

    while ((rc = get_line("Enter string> ", buf, sizeof buf)) != NO_INPUT) {
        if (rc == TOO_LONG) {
            printf("Input too long: ");
        }
        printf("->%s<-\n", buf);
    }
    return 0;
}

L'un des autres problèmes avec scanf est son comportement en cas de dépassement. Par exemple, lors de la lecture des int:

int i;
scanf("%d", &i);

le ci-dessus ne peuvent pas être utilisés en toute sécurité en cas de dépassement. Même pour le premier cas, la lecture d'une chaîne de caractères est beaucoup plus simple de le faire avec fgets plutôt qu'avec scanf.

5voto

codaddict Points 154968

Oui, vous avez raison. Il y a une faille de sécurité importante en scanf de la famille(scanf,sscanf, fscanf..etc) esp lors de la lecture d'une chaîne de caractères, car elles ne prennent pas la longueur de la mémoire tampon (dans lequel ils sont en lecture) en compte.

Exemple:

char buf[3];
sscanf("abcdef","%s",buf);

clairement le tampon buf peut contenir MAX 3 char. Mais l' sscanf va essayer de mettre des "abcdef" provoquant en elle débordement de la mémoire tampon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X