function gets () in C

Ich brauche wieder Hilfe! Ich dachte, es ist ziemlich cool, die gets() -function zu verwenden, weil es wie scanf() wo ich eine Eingabe mit Leerzeichen bekommen könnte. Aber ich lese in einem der Threads ( Student Info File Handling ), dass es nicht gut zu benutzen ist, weil es nach ihnen ein Teufelswerkzeug ist, um Pufferüberläufe zu erzeugen (was ich nicht verstehe)

Wenn ich die function gets() verwende, könnte ich dies tun. Gib deinen Namen ein: Keanu Reeves .

Wenn ich scanf() , könnte ich das nur tun. Gib deinen Namen ein: Keanu

Also habe ich ihren Rat fgets() und meinen ganzen gets() Code durch fgets() . Das Problem ist jetzt, dass einige meiner Codes nicht mehr funktionieren … gibt es andere functionen als gets() und fgets() die die ganze Zeile lesen könnten und die Whitespace ignorieren.

Es ist ein teuflisches Werkzeug zum Erstellen von Pufferüberläufen

Da gets keinen gets benötigt, weiß er nicht, wie groß der Eingabepuffer ist. Wenn Sie einen 10-stelligen Puffer übergeben und der Benutzer 100 Zeichen eingibt – nun, Sie bekommen den Punkt.

fgets ist eine sicherere Alternative zu gets da es die fgets als Parameter benötigt, also kann man es so nennen:

 fgets(str, 10, stdin); 

und es wird höchstens 9 Zeichen einlesen.

Das Problem ist jetzt, dass einige meiner Codes nicht mehr funktionieren

Dies liegt möglicherweise daran, dass fgets auch das letzte Newline-Zeichen ( \n ) in Ihrem Puffer speichert. Wenn Ihr Code dies nicht erwartet, sollten Sie ihn manuell entfernen:

 int len = strlen(str); if (len > 0 && str[len-1] == '\n') str[len-1] = '\0'; 

Wie andere Antworten bemerkt haben, überprüft gets() Pufferraum nicht. Zusätzlich zu zufälligen Überlaufproblemen kann diese Schwäche von böswilligen Benutzern verwendet werden, um alle Arten von Chaos zu erzeugen.

Einer der ersten weitverbreiteten Würmer, der 1988 veröffentlicht wurde, verwendete gets() , um sich im Internet zu verbreiten. Hier ist ein interessanter Auszug aus der Expert C-Programmierung von Peter Van Der Linden, der erklärt, wie es funktioniert:

Der Early Bug bekommt () den Internet Worm

Die Probleme in C beschränken sich nicht nur auf die Sprache. Einige Routinen in der Standardbibliothek haben unsichere Semantik. Dies wurde im November 1988 dramatisch durch das Wurmprogramm demonstriert, das sich durch Tausende von Maschinen im Internetnetz schlängelte. Als sich der Rauch geglättet hatte und die Untersuchungen abgeschlossen waren, wurde festgestellt, dass eine Art, wie der Wurm sich verbreitet hatte, eine Schwachstelle im finger Daemon war, der Anfragen über das Netzwerk annimmt, wer gerade eingeloggt ist. Der finger Daemon, in.fingerd , verwendet die Standard-E / A-Routine gets() .

Die nominale Aufgabe von gets() besteht darin, eine Zeichenfolge aus einem Stream einzulesen. Der Anrufer teilt ihm mit, wo er die eingehenden Zeichen ablegen soll. Aber gets() überprüft nicht den Pufferraum; Tatsächlich kann es den Pufferraum nicht überprüfen. Wenn der Aufrufer einen pointers auf den Stapel und mehr Eingabe als Pufferspeicherplatz bereitstellt, überschreibt gets() den Stapel. Der finger Daemon enthielt den Code:

 main(argc, argv) char *argv[]; { char line[512]; ... gets(line); 

Hier ist line ein 512-Byte-Array, das automatisch auf dem Stapel zugewiesen wird. Wenn ein Benutzer dem finger Daemon mehr Eingaben als diese zur Verfügung stellt, wird die Routine gets() ihn weiterhin auf dem Stapel speichern. Die meisten Architekturen sind anfällig dafür, einen vorhandenen Eintrag in der Mitte des Stapels mit etwas Größerem zu überschreiben, das auch benachbarte Einträge überschreibt. Die Kosten für die Überprüfung jedes Stack-Zugriffs auf Größe und Berechtigung wären in der Software unerschwinglich. Ein sachkundiger Übeltäter kann die Rückkehradresse in dem Prozeduraktivierungsdatensatz auf dem Stapel ändern, indem er die richtigen Binärmuster in der Argumentkette speichert. Dadurch wird der Ablauf der Ausführung nicht execv() zurückgeführt, wo er execv() , sondern in einer speziellen Befehlssequenz (ebenfalls sorgfältig auf dem Stapel abgelegt), die execv() , um das laufende Bild durch eine Shell zu ersetzen. Voilà, Sie sprechen jetzt mit einer Shell auf einem Remote-Computer statt mit dem finger Daemon, und Sie können Befehle ausgeben, um über eine Kopie des Virus auf einen anderen Computer zu ziehen.

Ironischerweise ist die Routine gets() eine veraltete function, die Kompatibilität mit der allerersten Version der portablen I / O-Bibliothek bot und vor mehr als einem Jahrzehnt durch Standard-I / O ersetzt wurde. Die Manpage empfiehlt sogar dringend, stattdessen fgets() zu verwenden. Die Routine fgets() begrenzt die Anzahl der gelesenen Zeichen, so dass die Größe des Puffers nicht überschritten wird. Der finger Daemon wurde mit einem Zwei-Zeilen-Fix gesichert, der Folgendes ersetzt:

 gets(line); 

durch die Linien:

 if (fgets(line, sizeof(line), stdin) == NULL) exit(1); 

Dies schluckt eine begrenzte Menge an Eingaben und kann daher nicht zum Überschreiben wichtiger Orte durch jemanden, der das Programm ausführt, manipuliert werden. Der ANSI C-Standard hat jedoch gets() aus der Sprache entfernt. Während dieses spezielle Programm gesichert wurde, wurde der zugrunde liegende Defekt in der C-Standardbibliothek nicht entfernt.

Sie könnten diese Frage betrachten: Sichere Alternative zu gets() . Es gibt eine Reihe nützlicher Antworten.

Sie sollten genauer fgets() warum Ihr Code nicht mit fgets() . Wie die Antworten in der anderen Frage erklären, müssen Sie sich mit dem Zeilenumbruch befassen, gets() auslässt.

Um alle Wörter mit scanf zu lesen, können Sie es so machen

Beispiel:

 printf("Enter name: "); scanf("%[^\n]s",name); //[^\n] is the trick 

Sie können scanf , um gets zu imitieren. Es ist aber nicht schön.

 #include  #define S_HELPER(X) # X #define STRINGIZE(X) S_HELPER(X) #define MAX_NAME_LEN 20 int flushinput(void) { int ch; while (((ch = getchar()) != EOF) && (ch != '\n')) /* void */; return ch; } int main(void) { char name[MAX_NAME_LEN + 1] = {0}; while (name[0] != '*') { printf("Enter a name (* to quit): "); fflush(stdout); scanf("%" STRINGIZE(MAX_NAME_LEN) "[^\n]", name); /* safe gets */ if (flushinput() == EOF) break; printf("Name: [%s]\n", name); puts(""); } return 0; } 

Es ist viel besser, mit fgets zu fgets und (wenn nötig) mit sscanf zu sscanf .


BEARBEITEN, um den Scanf-Aufruf und den umgebenden Code zu erklären.

Die Konvertierungsspezifikation “% [” von scanf akzeptiert eine maximale scanf die den scanf nicht enthält. Daher muss das Array, das die Eingabe enthält, 1 Zeichen mehr als mit scanf gelesen haben.

Um das mit nur einer einzigen Konstante zu tun, habe ich das Makro STRINGIZE benutzt. Mit diesem Makro kann ich eine # defined’d-Konstante sowohl als Array-Größe (für die Variablendefinition) als String (für den Spezifizierer) verwenden.

Es gibt noch einen weiteren Aspekt, der Erwähnung verdient: der flushinput . Wenn gets verwendet gets , werden alle Daten in den Speicher geschrieben (auch wenn der Puffer überläuft), bis auf den Zeilenumbruch, aber nicht. Um dies nachzuahmen, liest der scanf eine begrenzte Anzahl von Zeichen bis zu, jedoch nicht einschließlich der Zeilenschaltung, und behält im Gegensatz zu gets die Zeilenschaltung im Eingabepuffer bei . So dass Newline entfernt werden muss und das ist was flushinput tut.

Der Rest des Codes bestand hauptsächlich darin, eine Testumgebung einzurichten.

Sie können mehr als ein Feld mit scanf() lesen, damit Sie scanf() tun können:

 scanf("%s %s\n", first_name, last_name); 

Ich denke jedoch, es wäre besser, eine Zeichenfolge zu lesen und sie dann selbst zu teilen, da sie möglicherweise nicht nur einen Vornamen eingegeben haben, oder ersten / mittleren / letzten.

Was sind die Probleme, die Sie mit fgets() ?

Das Problem mit gets() ist, dass es so viele Zeichen zurückgibt, wie der Benutzer eingibt – Sie als der Anrufer haben keine Kontrolle darüber. Sie können also 80 Zeichen zuweisen, der Benutzer kann 100 Zeichen eingeben und die letzten 20 werden am Ende des Speichers, den Sie zugewiesen haben, geschrieben, und stampfen auf wer weiß was.