====== gets() ====== ''gets()'' ist in der ''[[c:lib:stdio:start|stdio]]'' definiert, die in C über ''stdio.h'', bzw in C++ über ''cstdio'' eingebunden wird.\\ \\ :!: **gets() wurde mit C11 aus dem C-Standard entfernt. Details finden sich am Ende dieser Seite.** ===== Funktion ===== ''gets()'' liest einen String durch Benutzereingaben ein. **Da sie aber für ihre Pufferüberlaufsschwächen gefürchtet ist, sollte sie nie benutzt werden**. Stattdessen sollte auf die Funktion ''[[fgets()]]'' ausgewichen werden. Dieser Artikel beschreibt der Vollständigkeit halber die Anwendung, soll aber vor allem zeigen, warum diese Funktion so gefährlich ist. ===== Signatur ===== #include char * gets( char * str ); **str**: Ein ''char''-Array, welches der String geschrieben werden soll.\\ **"Return Value"**: ''NULL'' im Fehlerfall, ansonsten der übergebene Parameter ''str''. ===== Fehlerquellen ===== Die Funktion ''gets()'' ist schon fast ein Symbol für Pufferüberläufe. Sie ist die bekannteste und gefürchtetste aller von Bufferoverflows gefährdeten Funktionen, weil sie, wie ihre die anderen, die Länge der Eingabe nicht überprüft. Im folgenden Beispiel darf man maximal 15 Zeichen eingeben. Was passiert aber intern, wenn man mehr als 15 Zeichen eingibt? Die nachfolgenden Werte werden dann in einem anderen Speicherbereich überschrieben, weil ''str'' nur ein für 16 Zeichen (inklusive binärem Nullzeichen) reserviertes Array ist. Leider kommt es noch häufig vor, dass einige (meist veraltete) Lehrbücher ohne jegliche Warnung ''gets()'' als normal nutzbare Eingabefunktion beschreiben. Es ist sogar noch in der heutigen Zeit nicht selten, dass diese Funktion in Anwendungen implementiert wird. Umstrittene Programmierstile, wie z.B. ''goto'', können noch in einigen wenigen Fällen nützlich sein. Das kann man von der Nutzung von ''gets()'' nicht behaupten. Es gibt keinen sinnvollen Anwendungszweck dafür. ''gets()'' sollte also ohne Ausnahme **niemals** benutzt werden. Eine sichere Alternative zu ''gets()'' ist die Funktion ''[[c:lib:stdio:fgets|fgets()]]''. Außerdem wäre es eine Überlegung wert, sofern es der vorgesehene Programmablauf zulässt, die Eingabe vor dem Programmstart über [[c:func:main:parameter|Kommandozeilenparameter]] zu verwalten. ===== Beispiel ===== #include #include int main() { char third[16] = "Third"; char str[16]; char second[16] = "Second"; printf("Enter String: "); gets(str); printf( "Eingabe: %x - %.10s\n", (unsigned int)str, str ); printf( "Second : %x - %.10s\n", (unsigned int)second, second ); printf( "Third : %x - %.10s\n", (unsigned int)third, third ); return EXIT_SUCCESS; } Das binäre Nullzeichen '\0' wird automatisch angefügt. Das bedeutet automatisch, dass die einzulesende Zeichenkette nur 9 Zeichen groß sein darf. Ansonsten gibt es unter Umständen die Fehlermeldung 'Segmentation fault' (Unix). Beim Kompilieren erzeugt der GCC-Compiler bereits eine Warnung: xin@trinity:~/proggen.org/clib/stdio$ gcc gets.c /tmp/ccWHKm2Q.o: In function `main': gets.c:(.text+0x4e): warning: the `gets' function is dangerous and should not be used. **Ausgabe**: Enter String: First Eingabe: 184ef7e0 - First Second : 184ef7d0 - Second Third : 184ef7f0 - Third ==== Buffer Overflow ===== Wir haben am vorherigen Beispiel gesehen, dass die Strings 0x10 (also 16) Bytes auseinanderliegen. Also geben wir zuerst 16 Bytes ein, um den gültigen Buffer zu füllen und anschließend überfüllen wir den Buffer mit unserer Eingabe: Enter String: 0123456789012345First Eingabe: 2a451580 - 0123456789 Second : 2a451570 - Second Third : 2a451590 - First Die Variable ''third'' liegt im Speicher hinter ''string'', also der Eingabe. Sie wird daher überschrieben und erhält den neuen Wert "First", obwohl ''third'' niemals hätte überschrieben werden sollen. Eine detailliertere Erklärung zu Bufferoverflows und wie sie zu vermeiden sind findet sich im [[security:memory-corruption:start]]. ===== Entfernung aus dem Standard (C11) ===== In der Version C11 wurde ''gets()'' schließlich aufgrund der oben genannten Probleme komplett aus C entfernt. Stattdessen wurde eine sicherere Variante mit dem Namen ''gets_s()'' eingeführt, die als zusätzlichen Parameter noch die maximale Anzahl an zu lesenden Zeichen (inklusive '\0') übergeben bekommt: char *gets_s( char *str, rsize_t n ); ''gets_s()'' entspricht somit der Anwendung von [[fgets()]] auf ''stdin'': fgets( str, n, stdin ); ===== siehe auch ===== [[c:lib:stdio:start|stdio]]: [[c:lib:stdio:fgets()]]