Warum ist die bekommt Funktion so gefährlich, dass es sollte nicht verwendet werden?
-
18-09-2019 - |
Frage
Wenn ich versuche, C-Code zu kompilieren, der die gets()
Funktion mit GCC verwendet, erhalte ich diese Warnung:
(. Text + 0x34): Warnung:. Die `bekommt Funktion ist gefährlich und sollte nicht verwendet werden
Ich erinnere mich, das hat etwas mit Stack-Schutz und Sicherheit zu tun, aber ich bin nicht sicher, warum.
Wie kann ich diese Warnung entfernen, und warum ist es so eine Warnung über gets()
mit?
Wenn gets()
so gefährlich ist, warum dann können wir es nicht entfernen?
Lösung
Um gets
sicher zu nutzen, müssen Sie genau wissen, wie viele Zeichen, die Sie lesen werden, so dass Sie Ihre Puffer groß genug machen kann. Sie werden nur wissen, dass, wenn Sie genau wissen, welche Daten Sie lesen werden.
Statt gets
verwenden, möchten Sie fgets
verwenden, die das hat Unterschrift
char* fgets(char *string, int length, FILE * stream);
(fgets
, wenn es eine ganze Zeile liest, wird den '\n'
im String verlassen;. Sie werden damit zu tun haben)
Es blieb einen offiziellen Teil der Sprache bis zu 1999 ISO-C-Standard, aber es wurde von dem 2011-Standard offiziell entfernt. Die meisten Implementierungen C noch unterstützen, aber zumindest gcc gibt eine Warnung für jeden Code, verwendet es.
Andere Tipps
Warum ist gets()
gefährlich
Der erste Internet-Wurm (der Morris Internet Worm ) flüchtete vor etwa 30 Jahren (1988- 11-02), und es verwendet gets()
und einen Pufferüberlauf als eine seiner Methoden der von System zu System ausbreiten. Das Grundproblem ist, dass die Funktion nicht weiß, wie groß der Puffer ist, so dass er weiter las, bis es eine neue Zeile oder Begegnungen findet EOF, und kann die Grenzen des Puffers gegeben überlaufen wurde.
Sie sollten vergessen Sie jemals gehört, dass gets()
existierte.
Der C11-Standard ISO / IEC 9899: 2011 eliminiert gets()
als Standardfunktion, die eine gute Sache ™ ist (es wurde als 'veraltet' formal markiert und 'veraltet' in ISO / IEC 9899: 1999 / Cor.3: 2007 - Technical Corrigendum 3 für C99 und dann in C11 entfernt). Leider wird es seit vielen Jahren in Bibliotheken bleibt aus Gründen der Abwärtskompatibilität ( ‚Jahrzehnte‘ bedeutet). Wenn es nach mir ginge, würde die Umsetzung der gets()
werden:
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
Da Ihr Code sowieso abstürzen wird, früher oder später, ist es besser, die Probleme aus eher früher als später zu gehen. Ich bereit sein würde eine Fehlermeldung hinzuzufügen:
fputs("obsolete and dangerous function gets() called\n", stderr);
Moderne Versionen des Linux-Kompilierung System generiert Warnungen, wenn Sie gets()
verknüpfen - und auch für einige andere Funktionen, die auch Sicherheitsprobleme haben (mktemp()
, ...)
Alternativen zu gets()
fgets ()
Wie alle anderen gesagt, die kanonische Alternative zu gets()
fgets()
Angabe stdin
als Dateistrom.
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
Was niemand sonst noch erwähnt, dass gets()
die Newline nicht enthalten aber fgets()
tut. Also, müssen Sie einen Wrapper um fgets()
verwenden, die die Neue-Zeile löscht:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
return buffer;
}
return 0;
}
Oder besser:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
return buffer;
}
return 0;
}
Auch als caf in einem Kommentar weist darauf hin, und paxdiablo zeigt in seiner Antwort mit fgets()
Sie Daten übrig bleiben auf einer Linie haben könnte. Mein Wrapper-Code lässt, dass die Daten beim nächsten Mal gelesen werden; Sie können es leicht modifizieren den Rest der Datenzeile zu verschlingen, wenn Sie bevorzugen:
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n')
;
}
Das verbleibende Problem ist, wie die drei anderen Ergebnis Zustände melden -. EOF oder Fehler, Zeile gelesen und nicht abgeschnitten, und teilweise Zeile gelesen, aber die Daten abgeschnitten wurde,
Dieses Problem mit gets()
entsteht nicht, weil er nicht weiß, wo Ihre Puffer enden und trampeln fröhlich über das Ende, Chaos auf Ihrem gepflegtes Speicherlayout anrichtet, oft die Rückkehr Stapel vermasseln (a Stack über~~POS=TRUNC ), wenn der Puffer auf den Stapel oder Füßen treten über den Steuerinformationen, wenn der Puffer dynamisch zugeordnet, und Kopieren von Daten über andere wertvolle globalen (oder Modul zugeordnet ist) Variablen, wenn der Puffer statisch zugeordnet ist. Keiner von ihnen ist eine gute Idee -. Sie verkörpern die Phrase ‚undefined behaviour`
Es gibt auch die TR 24731-1 (Technischer Bericht des C Standard Committee), die auf eine Vielzahl von Funktionen sicherere Alternativen, einschließlich gets()
:
§6.5.4.1 Die
gets_s
FunktionÜberblick
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n);
Runtime-Einschränkungen
s
ist kein Null-Zeiger sein.n
ist weder gleich Null noch größer sein als RSIZE_MAX. Eine neue Zeilenwechselzeichen, end-of-Datei oder Lesefehler sind innerhalb von Lese occurn-1
Zeichen vonstdin
. 25)3 Wenn eine Laufzeit-Einschränkungsverletzung ist, wird
s[0]
auf den Null-Zeichensatz und Zeichen gelesen und vonstdin
verworfen, bis ein Zeichen new-line gelesen wird, oder End-of-Datei oder einem Lesefehler auftreten.Beschreibung
4 Die
gets_s
Funktion liest höchstens eins kleiner als die Anzahl der Zeichen vonn
angegeben aus dem Strom, auf dem durchstdin
, die in das Array vons
spitz. Keine zusätzliche Zeichen werden nach einem neuen Zeilen gelesene Zeichen (der verworfen wird) oder nach dem Ende der Datei. Die verworfen new-line Zeichen tragen nicht zu Anzahl der gelesenen Zeichen. EIN Null-Zeichen geschrieben wird, unmittelbar nachdem das letzte Zeichen in das Array zu lesen.5 Wenn End-of-Datei angetroffen wird und keine Zeichen in dem Array gelesen wurden, oder wenn eine Lese Fehler treten während des Betriebes, so wird
s[0]
zum Null-Zeichen gesetzt, und die andere Elemente vons
nehmen nicht spezifizierte Werte.Empfohlene Praxis
6 Die
fgets
Funktion erlaubt es richtig geschriebene Programme sicher Eingangsleitungen zu verarbeiten lang in der Folge-Array zu speichern. In der Regel erfordert dies, dass Anrufer vonfgets
Lohn Aufmerksamkeit auf die Anwesenheit oder Abwesenheit eines neuen Zeilen Zeichen im Ergebnisarray. Erwägen Verwendungfgets
(zusammen mit allen benötigten Verarbeitung auf der Grundlage neuer Zeichen-line) anstelle vongets_s
.25) Die
gets_s
Funktion, im Gegensatz zugets
, macht es eine Laufzeiteinschränkungsverletzung für eine Linie von Eingabe zu Überlauf Der Puffer zu speichern. Im Gegensatz zufgets
hältgets_s
eine Eins-zu-Eins-Beziehung zwischen Eingangsleitungen und erfolgreiche Anrufegets_s
. Programme, diegets
erwarten eine solche Beziehung verwenden.
Die Microsoft Visual Studio Compiler eine Annäherung an den 24731-1 Standard TR implementieren, aber es gibt Unterschiede zwischen den durchgeführten Signaturen von Microsoft und die in der TR.
Der C11-Standard ISO / IEC 9899 bis 2011 umfasst TR24731 in Anhang K als optionalen Bestandteil der Bibliothek. Leider ist es selten auf Unix-ähnlichen Systemen umgesetzt werden.
getline()
- POSIX
POSIX 2008 bietet auch eine sichere Alternative zu gets()
genannt getline()
. Es ordnet Platz für die Linie dynamisch, so dass Sie am Ende brauchen sie zu befreien. Es entfernt die Beschränkung auf Leitungslänge, also. Es gibt auch die Länge der Daten, die gelesen wurden, oder -1
(und nicht EOF
!), Was bedeutet, dass Null-Bytes im Eingang zuverlässig gehandhabt werden kann. Es gibt auch eine ‚wählen Sie Ihre eigenen Einzel Zeichenbegrenzer‘ Variation genannt getdelim()
; Dies kann nützlich sein, wenn Sie mit der Ausgabe von find -print0
, wo die Enden der Dateinamen gekennzeichnet sind, mit einem ASCII-NUL '\0'
Charakter, zum Beispiel handeln.
Da gets
macht nicht jede Art von Kontrolle, während Bytes immer von stdin und sie irgendwo setzen. Ein einfaches Beispiel:
char array1[] = "12345";
char array2[] = "67890";
gets(array1);
Nun, zunächst einmal sind Sie zur Eingabe erlaubt, wie viele Zeichen Sie wollen, gets
werden darüber nicht. Zweitens die Bytes über die Größe des Arrays, in dem Sie sie setzen (in diesem Fall array1
) überschreiben, was sie im Speicher zu finden, weil gets
sie schreiben. Im vorherigen Beispiel bedeutet dies, dass vielleicht, wenn Sie Eingabe "abcdefghijklmnopqrts"
, unvorhersehbar, überschreibt es auch array2
oder was auch immer.
Die Funktion ist unsicher, da es im Einklang Eingabe annimmt. NIEMALS IT!
Sie sollten nicht gets
verwenden, da es keine Möglichkeit, einen Pufferüberlauf zu stoppen hat. Wenn der Benutzer in mehr Daten als in Ihrem Puffer paßt, werden Sie höchstwahrscheinlich mit Korruption am Ende oder noch schlimmer.
In der Tat, ISO hat den Schritt vollzogen worden Entfernen gets
aus dem C-Standard (Stand C11, obwohl es in C99 veraltet war), die, gegeben, wie hoch sie die Abwärtskompatibilität zu bewerten, sollte ein Indiz dafür, wie schlecht diese Funktion war.
Die richtige Sache zu tun ist, um die fgets
Funktion mit dem stdin
Datei-Handle zu verwenden, da Sie die Zeichen aus dem Benutzer lesen begrenzen kann.
Aber das hat auch seine Probleme wie:
- zusätzliche Zeichen durch den Benutzer eingegeben werden beim nächsten Mal abgeholt werden.
- gibt es keine schnelle Benachrichtigung, dass der Benutzer zu viele Daten eingegeben werden.
Zu diesem Zweck fast jeden C-Codierer zu einem bestimmten Zeitpunkt in ihrer Karriere werden auch einen nützlichen Wrapper um fgets
zu schreiben. Hier ist meins:
#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;
}
mit einigem Testcode:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
printf ("No input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long\n");
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Es bietet den gleichen Schutz wie fgets
, dass sie Pufferüberlauf verhindert, aber es meldet auch den Anrufer darüber, was passiert ist, und die überschüssigen Zeichen löscht, so dass sie nicht Ihren nächsten Eingabevorgang beeinflussen.
Fühlen Sie sich frei, es zu benutzen, wie Sie wollen, ich es hiermit veröffentlichen unter dem „tun, was du verdammt gut wollen“ Lizenz: -)
fgets .
vom stdin zu lesen:
char string[512];
fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */
Sie können keine API-Funktionen, ohne die API entfernen. Wenn Sie würden, würden viele Anwendungen nicht mehr kompilieren oder überhaupt ausgeführt werden.
Dies ist der Grund, dass eine Referenz gibt:
Lesen einer Zeile, die überläuft Array auf den s führt undefiniertes Verhalten. Die Verwendung von fgets () empfohlen wird.
Vor kurzem las ich in einem USENET Beitrag comp.lang.c
, dass gets()
wird immer von der Norm entfernt. WOOHOO
werden Sie glücklich zu wissen, dass die Ausschuss stimmt einfach (einstimmig, wie es stellt sich heraus) wird zu entfernen () aus der Entwurf als auch.
In C11 (ISO / IEC 9899: 201x) hat gets()
entfernt. (Es ist veraltet in ISO / IEC 9899: 1999 / Cor.3: 2007 (E))
Neben fgets()
, C11 stellt eine neue sichere Alternative gets_s()
:
C11 K.3.5.4.1 Die
gets_s
Funktion#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n);
Doch in der Empfohlene Praxis Abschnitt wird fgets()
noch bevorzugt.
Die
fgets
Funktion erlaubt es richtig geschriebene Programme sicher Eingangsleitungen zu verarbeiten lang in der Folge-Array zu speichern. In der Regel erfordert dies, dass Anrufer vonfgets
Lohn Aufmerksamkeit auf die Anwesenheit oder Abwesenheit eines neuen Zeilen Zeichen im Ergebnisarray. Erwägen Verwendungfgets
(zusammen mit allen benötigten Verarbeitung auf der Grundlage neuer Zeichen-line) anstelle vongets_s
.
Ich möchte eine ernsthafte Einladung zu allen C-Bibliothek Maintainer dort erweitern, die noch darunter gets
in ihren Bibliotheken „nur falls jemand noch abhängig von ihm“: Bitte ersetzen Sie die Implementierung mit dem Äquivalent von
char *gets(char *str)
{
strcpy(str, "Never use gets!");
return str;
}
Dies wird dazu beitragen sicherzustellen, dass niemand wird je nach wie vor darauf. Danke.
gets()
ist gefährlich, weil es möglich ist, für die der Benutzer das Programm zum Absturz durch zu viel in die Eingabeaufforderung eingeben. Es kann nicht das Ende des verfügbaren Speichers, erkennt also, wenn Sie eine Menge an Speicher zu klein für den Zweck zuordnen, kann es einen seg Fehler und Absturz führen. Manchmal scheint es sehr unwahrscheinlich, dass ein Benutzer 1000 Zeichen in eine Eingabeaufforderung eingeben, wird für den Namen einer Person gemeint, sondern als Programmierer, müssen wir unsere Programme kugelsicher machen. (Es kann auch ein Sicherheitsrisiko sein, wenn ein Benutzer ein Systemprogramm durch zu viele Daten zu senden abstürzen kann).
fgets()
können Sie festlegen, wie viele Zeichen werden aus dem Standard-Eingangspuffer genommen, so dass sie überrannt die Variable nicht.
Die C bekommt Funktion ist gefährlich und hat ein sehr teurer Fehler gewesen. Tony Hoare Singles es aus für besondere Erwähnung in seinem Vortrag "Null Referenzen: Die Milliarden-Dollar-Fehler":
http: //www.infoq. com / Präsentationen / Null-Referenzen-The-Milliarden-Dollar-Fehler-Tony-Hoare
Die ganze Stunde ist sehenswert, sondern für seine Kommentare von 30 Minuten auf dem spezifischen sehen bekommt Kritik rund 39 Minuten.
Hoffentlich macht Lust auf die ganze Rede, die Aufmerksamkeit auf sich zieht, wie wir mehr formale Richtigkeit Beweise in Sprachen benötigen und wie die Sprache Designer sollten für die Fehler in ihren Sprachen verantwortlich gemacht werden, nicht den Programmierer. Dies scheint der ganze fragwürdige Grund für Designer von schlechten Sprachen zu haben, die Schuld an Programmierer in der Gestalt ‚Programmierer Freiheit‘ zu drücken.