100 votes

Comment empêcher scanf de provoquer un buffer overflow en C?

J'utilise ce code:

 while ( scanf("%s", buf) == 1 ){
 

Quel serait le meilleur moyen d'éviter un débordement de mémoire tampon afin que des chaînes de longueurs aléatoires puissent lui être transmises?

Je sais que je peux limiter la chaîne d'entrée en appelant par exemple:

 while ( scanf("%20s", buf) == 1 ){
 

Mais je préférerais pouvoir traiter toutes les entrées de l'utilisateur. Ou est-ce que cela ne peut pas être fait en toute sécurité avec scanf et que je devrais utiliser fgets?

83voto

Jonathan Leffler Points 299946

Dans leur livre"la Pratique de La Programmation"(ce 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 à l' scanf() famille de fonctions. En effet:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

Remarque, 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.

35voto

John Ledbetter Points 5840

Si vous utilisez gcc, vous pouvez utiliser le spécificateur a GNU-extension pour que scanf () alloue de la mémoire afin que vous puissiez conserver l'entrée:

 int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}
 

Edit: Comme Jonathan l’a souligné, vous devriez consulter les pages de manuel scanf car le spécificateur peut être différent ( %m ) et vous devrez peut-être activer certaines définitions lors de la compilation.

8voto

dirkgently Points 56879

La plupart du temps une combinaison de fgets et sscanf 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é:

#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */ 
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);

Le ci-dessus rejette le flux d'entrée jusqu'à, mais pas y compris le retour à la ligne,\n) de caractère. Vous aurez besoin d'ajouter un getchar() de la consommer. Également vérifier si vous avez atteint la fin-de-rivière:

if (!feof(stdin)) { ...

et c'est à ce sujet.

4voto

DigitalRoss Points 80400

Directement à l'aide d' 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 vous 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...

1voto

Mark Bessey Points 13931

Limiter la longueur de l'entrée est certainement plus facile. Vous pouvez accepter une entrée de longueur arbitraire en utilisant une boucle, lisant petit à petit, réaffectant l'espace nécessaire à la chaîne si nécessaire ...

Mais c'est beaucoup de travail, donc la plupart des programmeurs C coupent simplement l'entrée à une longueur quelconque. Je suppose que vous le savez déjà, mais utiliser fgets () ne vous autorisera pas à accepter des quantités arbitraires de texte - vous devrez toujours définir une limite.

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