2010-06-04 8 views
5

Von man gets:wenn man sich über gets() beschwert, warum nicht dasselbe mit scanf ("% s", ...) machen?

Niemals gets(). Da ist es unmöglich, ohne zu sagen, die Daten im Voraus zu wissen, wie viele Zeichen gets() wird gelesen, und weil gets() wird auch weiterhin Zeichen über das Ende des Puffers speichern, es ist extrem gefährlich zu verwenden . Es wurde verwendet, um Computer Sicherheit zu brechen. Verwenden Sie stattdessen fgets().

Fast überall sehe ich scanf in einer Weise verwendet werden, die das gleiche Problem haben sollte (buffer overflow/buffer overrun): scanf("%s",string). Dieses Problem besteht in diesem Fall? Warum gibt es keine Referenzen in der Manpage scanf? Warum gcc nicht warnt, wenn das mit -Wall kompiliert wird?

ps: Ich weiß, dass es eine Möglichkeit ist im Format-String die maximale Länge der Zeichenfolge mit scanf angeben:

char str[10]; 
scanf("%9s",str); 

edit: Ich bitte nicht Determe, wenn der vorhergehende Code richtig ist oder nicht. Meine Frage ist: Wenn scanf("%s",string) immer falsch ist, warum gibt es keine Warnungen und es gibt nichts darüber in der man-Seite?

+1

Ihr Wikipedia-Link sagt 'scanf' ist * unsicher *. – aviraldg

+0

@aviraldg Du hast recht und ich habe es schon gelesen, aber ich fand keine gute Möglichkeit, den Titel zu schreiben. Ich habe es bearbeitet. – dbarbosa

+0

Wie alle antworteten: 'scanf ("% s ", ...)' ist unsicher. Eine weitere Referenz dazu: http://c-faq.com/stdio/scanfprobs.html. Ich verstehe immer noch nicht, wie es in der Manpage nichts gibt. – dbarbosa

Antwort

5

Die Antwort ist einfach, dass niemand den Code in GCC geschrieben hat, um diese Warnung zu erzeugen.

Wie Sie darauf hinweisen, ist eine Warnung für den speziellen Fall "%s" (ohne Feldbreite) durchaus angemessen.

jedoch zu beachten, dass dies nur der Fall für den Fall scanf(), vscanf(), fscanf() und vfscanf(). Dieser Formatbezeichner kann mit sscanf() und absolut sicher sein, daher sollte die Warnung in diesem Fall nicht ausgegeben werden. Dies bedeutet, dass Sie es nicht einfach zum vorhandenen "scanf-style-format-string" -Analysecode hinzufügen können. Sie müssen das in die Optionen "fscanf-style-format-string" und "sscanf-style-format-string" aufteilen.

Ich bin mir sicher, wenn Sie einen Patch für die neueste Version von GCC erstellen, besteht eine gute Chance, akzeptiert zu werden (und natürlich müssen Sie auch Patches für die glibc-Header-Dateien einreichen).

+0

Warum sagen Sie, dass es mit 'sscanf()' und 'vsscanf()' absolut sicher sein kann? Liegt es daran, dass Sie die ursprüngliche Zeichenkette (das erste Argument) überprüfen können, wo scanf gerade liest und sicher sein soll, dass es passt?(Zum Beispiel, wenn beide Größen gleich sind, wenn Sie nach Platzpositionen suchen, usw.) – dbarbosa

+0

Ja, genau. Es ist * möglich *, es sicher zu verwenden, da Sie die Eingabe steuern. – caf

4

Mit gets() ist nie sicher. scanf() kann sicher verwendet werden, wie Sie in Ihrer Frage gesagt haben. Das Bestimmen, ob Sie es sicher verwenden, ist jedoch ein schwierigeres Problem für den Compiler (z. B. wenn Sie scanf() in einer Funktion aufrufen, in der Sie den Puffer übergeben und ein Zeichen als Argumente zählen, wird es nicht sein fähig zu erzählen); in diesem Fall muss es davon ausgehen, dass Sie wissen, was Sie tun.

+0

Ja, Sie haben Recht, aber gcc warnt Sie, wenn die Anzahl oder die Art der Argumente nicht stimmt. In diesen Fällen geht es nicht davon aus, dass Sie wissen, was Sie tun. – dbarbosa

+0

@dbarbosa: Alle Informationen stehen dem Compiler zur Verfügung, um zu überprüfen, ob es die gleiche Anzahl von optionalen Argumenten gibt, wie Formatbezeichner in der Formatzeichenfolge sind. Auf die gleiche Weise hat es auch genug Informationen, um zu überprüfen, ob, wenn ein '% d' in der Formatzeichenfolge (zum Beispiel) ist, das entsprechende Argument eine ganze Zahl ist. –

+0

Es kann nicht wissen, ob die angegebene Größe richtig ist, aber es kann wissen, dass es nur falsch ist, wenn es keine Größenangabe hat. 'scanf ("% 5s ", string)' kann richtig oder falsch sein, abhängig von der Größe von 'string' und kann es nicht wie gesagt sagen. 'Scanf ("% s ", string)' ist jedoch wegen des Pufferüberlaufproblems immer falsch. – dbarbosa

-4

Es kann einfach sein, dass scanf Speicherplatz auf dem Heap basierend darauf zuweist, wie viele Daten eingelesen werden. Da es den Puffer nicht zuweist und dann liest, bis das Nullzeichen gelesen wird, riskiert es nicht das zu überschreiben Puffer. Stattdessen liest es in seinen eigenen Puffer, bis das Nullzeichen gefunden wird, und kopiert vermutlich den Puffer in einen anderen der richtigen Größe am Ende des Lesevorgangs.

+3

Nein; scanf weist keinen Speicherplatz auf dem Heap zu. Es ist die Aufgabe des Programmierers, die Puffer bereitzustellen. Scanf verwendet als dbarbosa sagte, ist unsicher. –

+0

Werfen Sie einen Blick auf dieses Tutorial, das das, was ich gerade gesagt habe, genauer und genauer erklärt. http://crasseux.com/books/ctutorial/String-overflows-with-scanf.html – Graham

+0

'scanf' weist keinen eigenen Speicher zu. Das 'a'-Flag, das Sie verlinkt haben, ist eine GNU-Erweiterung, die nicht Teil des C-Standards ist. –

3

Wenn der Compiler die Formatierungszeichenfolge scanf betrachtet, sieht er eine Zeichenfolge! Angenommen, der Formatierungsstring wird zur Laufzeit nicht eingegeben. Einige Compiler wie GCC verfügen über zusätzliche Funktionen zum Analysieren der Formatierungszeichenfolge, wenn sie zur Kompilierungszeit eingegeben werden. Diese zusätzliche Funktionalität ist nicht umfassend, da in einigen Situationen ein Laufzeitaufwand erforderlich ist, der für Sprachen wie C ein NEIN ist. Zum Beispiel können Sie in diesem Fall eine unsichere Verwendung ohne zusätzlichen versteckten Code erkennen:

Natürlich gibt es Möglichkeiten, sicheren Code mit scanf zu schreiben, wenn Sie die Anzahl der zu lesenden Zeichen angeben, und dass genügend Speicher für die Zeichenfolge vorhanden ist. Im Fall von gets gibt es keine Möglichkeit, die Anzahl der zu lesenden Zeichen anzugeben.

+1

Es ist sehr schwierig zu bestimmen, ob dies ein sicherer Anruf ist, aber es ist leicht zu sehen ein 'scanf ("% s ", str)' und warnt den Benutzer, sogar mehr als die Überprüfung, dass es bereits mit der Nummer und tut Der Typ der Argumente, die an "scanf" übergeben werden. (Gcc wird übrigens "warning: zu wenig Argumente für das Format" für Ihren Code sagen, da es kein Argument für die '% 9s' im Format gibt). – dbarbosa