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?

War es hilfreich?

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 occur   n-1 Zeichen von stdin. 25)

     

3 Wenn eine Laufzeit-Einschränkungsverletzung ist, wird s[0] auf den Null-Zeichensatz und Zeichen   gelesen und von stdin 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 von n angegeben   aus dem Strom, auf dem durch stdin, die in das Array von s 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 von s 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 von fgets Lohn   Aufmerksamkeit auf die Anwesenheit oder Abwesenheit eines neuen Zeilen Zeichen im Ergebnisarray. Erwägen   Verwendung fgets (zusammen mit allen benötigten Verarbeitung auf der Grundlage neuer Zeichen-line) anstelle von   gets_s.

     

25) Die gets_s Funktion, im Gegensatz zu gets, macht es eine Laufzeiteinschränkungsverletzung für eine Linie von Eingabe zu   Überlauf Der Puffer zu speichern. Im Gegensatz zu fgets hält gets_s eine Eins-zu-Eins-Beziehung zwischen   Eingangsleitungen und erfolgreiche Anrufe gets_s. Programme, die gets 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 von fgets Lohn   Aufmerksamkeit auf die Anwesenheit oder Abwesenheit eines neuen Zeilen Zeichen im Ergebnisarray. Erwägen   Verwendung fgets (zusammen mit allen benötigten Verarbeitung auf der Grundlage neuer Zeichen-line) anstelle von   gets_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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top