Wenn ich versuche, C-Code zu kompilieren, der die gets()
Funktion mit GCC verwendet, wird folgende Warnung angezeigt:
(.text + 0x34): Warnung: Die Funktion "get" ist gefährlich und sollte nicht verwendet werden.
Ich erinnere mich, dass dies etwas mit Stapelschutz und Sicherheit zu tun hat, aber ich weiß nicht genau warum.
Wie kann ich diese Warnung entfernen und warum gibt es eine solche Warnung zur Verwendung gets()
?
Wenn gets()
es so gefährlich ist, warum können wir es dann nicht entfernen?
c
fgets
buffer-overflow
gets
vinit dhatrak
quelle
quelle
gets()
Buffer_overflow_attackgets()
Antworten:
Um
gets
sicher zu verwenden , müssen Sie genau wissen, wie viele Zeichen Sie lesen werden, damit Sie Ihren Puffer groß genug machen können. Sie werden das nur wissen, wenn Sie genau wissen, welche Daten Sie lesen werden.Anstatt zu verwenden
gets
, möchten Sie verwendenfgets
, die die Signatur hat(
fgets
Wenn es eine ganze Zeile liest, bleibt das'\n'
in der Zeichenfolge; Sie müssen sich darum kümmern.)Es blieb ein offizieller Teil der Sprache bis zur ISO C-Norm von 1999, wurde jedoch durch die Norm von 2011 offiziell entfernt. Die meisten C-Implementierungen unterstützen es weiterhin, aber zumindest gibt gcc eine Warnung für jeden Code aus, der es verwendet.
quelle
gets()
, das den Compiler veranlasst, bei Verwendung eine Warnung auszugeben.Warum ist
gets()
gefährlichDer erste Internet-Wurm (der Morris-Internet-Wurm ) ist vor etwa 30 Jahren (1988-11-02) entkommen
gets()
und hat einen Pufferüberlauf als eine seiner Methoden zur Ausbreitung von System zu System verwendet. Das Grundproblem besteht darin, dass die Funktion nicht weiß, wie groß der Puffer ist. Daher liest sie weiter, bis sie eine neue Zeile findet oder auf EOF stößt und möglicherweise die Grenzen des Puffers überschreitet, den sie erhalten hat.Sie sollten vergessen, dass Sie jemals gehört haben, dass
gets()
es das gibt.Die C11-Norm ISO / IEC 9899: 2011 wurde
gets()
als Standardfunktion gestrichen. Dies ist A Good Thing ™ (sie wurde in ISO / IEC 9899: 1999 / Cor.3: 2007 - Technische Berichtigung offiziell als „veraltet“ und „veraltet“ gekennzeichnet 3 für C99 und dann in C11 entfernt). Leider wird es aus Gründen der Abwärtskompatibilität viele Jahre (dh "Jahrzehnte") in Bibliotheken verbleiben. Wenn es nach mirgets()
ginge, würde die Implementierung von :Da Ihr Code früher oder später ohnehin abstürzt, ist es besser, die Probleme eher früher als später zu lösen. Ich wäre bereit, eine Fehlermeldung hinzuzufügen:
Moderne Versionen des Linux-Kompilierungssystems generieren Warnungen, wenn Sie eine Verknüpfung herstellen
gets()
- und auch für einige andere Funktionen, bei denen ebenfalls Sicherheitsprobleme auftreten (mktemp()
,…).Alternativen zu
gets()
fgets ()
Wie alle anderen sagten, besteht die kanonische Alternative dazu
gets()
darinfgets()
,stdin
als Dateistream anzugeben .Was noch niemand erwähnt hat, ist, dass
gets()
es die Newline nicht enthält, aberfgets()
tut. Daher müssen Sie möglicherweise einen Wrapper verwendenfgets()
, der die neue Zeile löscht:Oder besser:
Auch als caf Punkte in einem Kommentar und aus paxdiablo zeigt in seiner Antwort mit
fgets()
Ihnen Daten auf einer Linie übrig haben könnten. Mein Wrapper-Code lässt diese Daten beim nächsten Mal gelesen werden. Sie können es leicht ändern, um den Rest der Datenzeile zu verschlingen, wenn Sie es vorziehen:Das verbleibende Problem besteht darin, wie die drei verschiedenen Ergebniszustände gemeldet werden - EOF oder Fehler, Zeilen lesen und nicht abgeschnitten und Teilzeile lesen, aber Daten wurden abgeschnitten.
Dieses Problem tritt nicht auf,
gets()
weil es nicht weiß, wo Ihr Puffer endet und fröhlich über das Ende hinaus trampelt, was Ihr wunderschön gepflegtes Speicherlayout verwüstet und häufig den Rückgabestapel (einen Stapelüberlauf) durcheinander bringt, wenn der Puffer zugewiesen ist den Stapel oder das Trampeln über die Steuerinformationen, wenn der Puffer dynamisch zugewiesen ist, oder das Kopieren von Daten über andere wertvolle globale (oder Modul-) Variablen, wenn der Puffer statisch zugewiesen ist. Nichts davon ist eine gute Idee - sie verkörpern den Ausdruck "undefiniertes Verhalten".Es gibt auch den TR 24731-1 (Technischer Bericht des C-Standardausschusses), der sicherere Alternativen zu einer Vielzahl von Funktionen bietet, darunter
gets()
:Die Microsoft Visual Studio-Compiler implementieren eine Annäherung an den TR 24731-1-Standard, es gibt jedoch Unterschiede zwischen den von Microsoft implementierten Signaturen und denen im TR.
Die C11-Norm ISO / IEC 9899-2011 enthält TR24731 in Anhang K als optionalen Teil der Bibliothek. Leider wird es auf Unix-ähnlichen Systemen selten implementiert.
getline()
- POSIXPOSIX 2008 bietet auch eine sichere Alternative zu
gets()
aufgerufengetline()
. Es weist der Zeile dynamisch Speicherplatz zu, sodass Sie sie am Ende freigeben müssen. Dadurch wird die Beschränkung der Zeilenlänge aufgehoben. Es gibt auch die Länge der gelesenen Daten zurück oder-1
(und nichtEOF
!), Was bedeutet, dass Null-Bytes in der Eingabe zuverlässig verarbeitet werden können. Es gibt auch eine ‚wählen Sie Ihre eigenen Einzel Zeichenbegrenzer‘ Variation genanntgetdelim()
; Dies kann nützlich sein, wenn Sie sich mit der Ausgabe befassen, bei derfind -print0
die Enden der Dateinamen beispielsweise mit einem ASCII-NUL-'\0'
Zeichen markiert sind .quelle
fgets()
Ihrefgets_wrapper()
Version den nachfolgenden Teil einer überlangen Zeile im Eingabepuffer belässt, um von der nächsten Eingabefunktion gelesen zu werden. In vielen Fällen möchten Sie diese Zeichen lesen und verwerfen.getline()
und sein Verwandtergetdelim()
, die die Länge der von den Befehlen gelesenen 'Zeile' zurückgeben und nach Bedarf Speicherplatz zuweisen, um die gesamte Zeile speichern zu können. Selbst das kann zu Problemen führen, wenn Sie eine einzeilige JSON-Datei mit einer Größe von mehreren Gigabyte erhalten. Kannst du dir all diese Erinnerung leisten? (Und wenn wir schon dabei sind, können wir habenstrcpy()
undstrcat()
Varianten , die einen Zeiger auf das Null - Byte am Ende zurückkehren Etc.?)fgets()
ist, dass wenn die Datei ein Null-Byte enthält, Sie nicht sagen können, wie viele Daten nach dem Null-Byte bis zum Zeilenende (oder EOF) vorhanden sind.strlen()
kann nur bis zum Null-Byte in den Daten melden; danach ist es Vermutung und daher mit ziemlicher Sicherheit falsch.gets()
es das gibt." Wenn ich das mache, stoße ich wieder darauf und komme hierher zurück. Hacken Sie Stackoverflow, um Upvotes zu erhalten?Weil
gets
es keine Überprüfung durchführt, während Bytes von stdin abgerufen und irgendwo abgelegt werden. Ein einfaches Beispiel:Zunächst dürfen Sie eingeben, wie viele Zeichen Sie möchten,
gets
ohne sich darum zu kümmern. Zweitensarray1
überschreiben die Bytes über der Größe des Arrays, in das Sie sie eingefügt haben (in diesem Fall ), alles, was sie im Speicher finden, weilgets
sie geschrieben werden. Im vorherigen Beispiel bedeutet dies, dass bei einer"abcdefghijklmnopqrts"
unvorhersehbaren Eingabe aucharray2
oder was auch immer überschrieben wird .Die Funktion ist unsicher, da sie eine konsistente Eingabe voraussetzt. NIEMALS VERWENDEN!
quelle
gets
völlig unbrauchbar macht , ist, dass es keinen Parameter für die Länge / Anzahl des Arrays gibt, den es braucht. Wäre es dort gewesen, wäre es nur eine andere gewöhnliche C-Standardfunktion.gets
war und warum keine Standard-Fgets-Variante für Anwendungsfälle so praktisch gemacht wurde, in denen der Zeilenumbruch nicht als Teil der Eingabe gewünscht wird.gets
war, wie der Name schon sagt, entworfen von einer Zeichenfolge zu bekommenstdin
, aber die Gründe für nicht mit Größe Parameter kann aus gewesen sein Geist von C : der Programmierer Trust. Diese Funktion wurde in C11 entfernt und die angegebene Ersetzunggets_s
nimmt die Größe des Eingabepuffers an. Ich habe jedoch keine Ahnung von demfgets
Teil.gets
, der entschuldbar sein könnte, wäre, wenn man ein Hardware-Zeilen-gepuffertes E / A-System verwendet, das physisch nicht in der Lage ist, eine Zeile über eine bestimmte Länge und die beabsichtigte Lebensdauer des Programms zu senden war kürzer als die Lebensdauer der Hardware. In diesem Fall könnte es gerechtfertigt sein, wenn Hardware nicht in der Lage ist, Leitungen mit einer Länge von mehr als 127 Byte zu senden,gets
in einen 128-Byte-Puffer zu wechseln, obwohl ich denke, dass die Vorteile der Angabe eines kürzeren Puffers bei der Erwartung kleinerer Eingaben dies mehr als rechtfertigen würden kosten.gets
undstrcat
sicher zu akzeptieren, wie es passt.Sie sollten es nicht verwenden,
gets
da es keine Möglichkeit gibt, einen Pufferüberlauf zu stoppen. Wenn der Benutzer mehr Daten eingibt, als in Ihren Puffer passen, werden Sie höchstwahrscheinlich beschädigt oder schlimmer.Tatsächlich hat ISO den Schritt unternommen ,
gets
den C-Standard zu entfernen (ab C11, obwohl er in C99 veraltet war), was angesichts der hohen Bewertung der Abwärtskompatibilität ein Hinweis darauf sein sollte, wie schlecht diese Funktion war.Das Richtige ist, die
fgets
Funktion mit demstdin
Dateihandle zu verwenden, da Sie die vom Benutzer gelesenen Zeichen einschränken können.Dies hat aber auch folgende Probleme:
Zu diesem Zweck wird fast jeder C-Codierer irgendwann in seiner Karriere auch einen nützlicheren Wrapper herumschreiben
fgets
. Hier ist meins:mit etwas Testcode:
Es bietet den gleichen Schutz wie
fgets
insofern, als es Pufferüberläufe verhindert, aber es benachrichtigt den Anrufer auch darüber, was passiert ist, und löscht die überschüssigen Zeichen, damit sie Ihre nächste Eingabeoperation nicht beeinträchtigen.Fühlen Sie sich frei, es zu verwenden, wie Sie möchten, ich veröffentliche es hiermit unter der Lizenz "Mach was du verdammt gut willst" :-)
quelle
gets()
weder in Abschnitt 7.19.7.7, in dem er definiert ist, noch in Abschnitt 7.26.9 Zukünftige Bibliotheksanweisungen und dem Unterabschnitt für explizit abgelehnt<stdio.h>
. Es gibt nicht einmal eine Fußnote darüber, dass es gefährlich ist. (Allerdings sehe ich in der Antwort von Yu Hao "Es ist in ISO / IEC 9899: 1999 / Cor.3: 2007 (E) veraltet)" .) Aber C11 hat es aus dem Standard entfernt - und nicht vorzeitig !int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
verbirgt diesize_t
zurint
Umwandlung vonsz
.sz > INT_MAX || sz < 2
würde seltsame Werte von fangensz
.if (buff[strlen(buff)-1] != '\n') {
ist ein Hacker-Exploit, da das erste eingegebene Zeichen des bösen Benutzers ein eingebettetes Nullzeichen sein könnte, dasbuff[strlen(buff)-1]
UB rendert.while (((ch = getchar())...
hat Probleme, wenn ein Benutzer ein Nullzeichen eingibt.fgets .
Aus dem Standard lesen:
quelle
Sie können API-Funktionen nicht entfernen, ohne die API zu beschädigen. Wenn Sie dies tun würden, würden viele Anwendungen überhaupt nicht mehr kompiliert oder ausgeführt.
Dies ist der Grund, den eine Referenz gibt:
quelle
Ich habe kürzlich in einem USENET-Beitrag
comp.lang.c
gelesen , dass diesgets()
aus dem Standard entfernt wird. WOOHOOquelle
gcc -std=c2012 -pedantic ...
get () kompilieren, werden Sie nicht durchkommen. (Ich habe gerade den-std
ParameterIn C11 (ISO / IEC 9899: 201x)
gets()
wurde entfernt. (Es ist in ISO / IEC 9899: 1999 / Cor.3: 2007 (E) veraltet.)Darüber hinaus
fgets()
stellt C11 eine neue sichere Alternative vorgets_s()
:Im Abschnitt Empfohlene Praxis
fgets()
wird dies jedoch weiterhin bevorzugt.quelle
gets()
ist gefährlich, da der Benutzer das Programm abstürzen kann, indem er zu viel in die Eingabeaufforderung eingibt. Das Ende des verfügbaren Speichers kann nicht erkannt werden. Wenn Sie also eine zu kleine Speichermenge für diesen Zweck zuweisen, kann dies zu einem Seg-Fehler und einem Absturz führen. Manchmal scheint es sehr unwahrscheinlich, dass ein Benutzer 1000 Buchstaben in eine Eingabeaufforderung eingibt, die für den Namen einer Person bestimmt ist. Als Programmierer müssen wir unsere Programme jedoch kugelsicher machen. (Es kann auch ein Sicherheitsrisiko darstellen, wenn ein Benutzer ein Systemprogramm durch Senden zu vieler Daten zum Absturz bringen kann.)fgets()
Mit dieser Option können Sie angeben, wie viele Zeichen aus dem Standardeingabepuffer entfernt werden sollen, damit die Variable nicht überschritten wird.quelle
Ich möchte alle C-Bibliotheksbetreuer, die noch
gets
in ihre Bibliotheken aufgenommen haben, ernsthaft einladen, "nur für den Fall, dass noch jemand davon abhängig ist": Bitte ersetzen Sie Ihre Implementierung durch das Äquivalent vonDies wird dazu beitragen, dass niemand mehr davon abhängig ist. Danke dir.
quelle
Die C-Funktion ist gefährlich und ein sehr kostspieliger Fehler. Tony Hoare hebt es in seinem Vortrag "Null References: The Billion Dollar Mistake" besonders hervor:
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Die ganze Stunde ist sehenswert, aber für seine Kommentare bekommt die Ansicht ab 30 Minuten mit der spezifischen Kritik rund 39 Minuten.
Hoffentlich macht dies Appetit auf das ganze Gespräch, was darauf aufmerksam macht, wie wir formellere Korrektheitsnachweise in Sprachen benötigen und wie Sprachdesigner für die Fehler in ihren Sprachen verantwortlich gemacht werden sollten, nicht für den Programmierer. Dies scheint der zweifelhafte Grund für Designer schlechter Sprachen gewesen zu sein, Programmierern die Schuld unter dem Deckmantel der "Programmiererfreiheit" zu geben.
quelle