Welche Funktionen aus der Standardbibliothek müssen (sollten) vermieden werden?

90

Ich habe auf Stack Overflow gelesen, dass einige C-Funktionen "veraltet" sind oder "vermieden werden sollten". Können Sie mir bitte einige Beispiele für diese Art von Funktion und den Grund dafür geben?

Welche Alternativen zu diesen Funktionen gibt es?

Können wir sie sicher verwenden - irgendwelche guten Praktiken?

Andrei Ciobanu
quelle
3
Ich glaube, es ist sehr gut zu wissen. Einige Funktionen von C sollten wirklich vermieden und nur für Bildungszwecke verwendet werden.
INS
@Felix, es wäre in Zukunft einfacher, eine einzelne (als richtig markierte) Antwort zu bearbeiten. Es liegt wirklich in der Luft, welche Antworten sich im Laufe der Zeit wahrscheinlich ändern werden. Wer weiß, vielleicht wird Jeff Leuten, die die Antworten in den nächsten Jahren auf dem neuesten Stand halten, ein Hausmeisterabzeichen geben.
Tim Post
1
@ Tim Post: Ok, ich werde meine Kommentare löschen.
Felix Kling
3
Ich würde es nicht strncpy()als allgemeinen Ersatz für verwenden strcpy(), und ich würde es nie verwenden strncat(), weil es die unintuitivste Schnittstelle hat, die man sich vorstellen kann - wissen SIE, was der Längenparameter angibt?
Jonathan Leffler
2
Die Verwendung von strncpy und strncat ist fast immer ein Fehler. Sie sollten auf keinen Fall anstelle von strcpy und strcat verwendet werden !! scanf und sprintf sind auch perfekt verwendbar, wenn Sie wissen, wie man sie benutzt ...
R .. GitHub STOP HELPING ICE

Antworten:

58

Veraltete Funktionen
unsicher
Ein perfektes Beispiel für eine solche Funktion ist get () , da es keine Möglichkeit gibt, festzustellen, wie groß der Zielpuffer ist. Folglich weist jedes Programm, das Eingaben mit get () liest, eine Sicherheitsanfälligkeit bezüglich Pufferüberlauf auf . Aus ähnlichen Gründen sollte man strncpy () anstelle von strcpy () und strncat () anstelle von strcat () verwenden .

Weitere Beispiele sind die Funktionen tmpfile () und mktemp () aufgrund möglicher Sicherheitsprobleme beim Überschreiben temporärer Dateien, die durch die sicherere Funktion mkstemp () ersetzt werden.

Nicht wiedereintrittsfähig
Andere Beispiele sind gethostbyaddr () und gethostbyname (), die nicht wiedereintrittsfähig sind (und daher nicht garantiert threadsicher sind) und durch die wiedereintrittsberechtigten getaddrinfo () und freeaddrinfo () ersetzt wurden .

Möglicherweise bemerken Sie hier ein Muster ... entweder mangelnde Sicherheit (möglicherweise weil nicht genügend Informationen in die Signatur aufgenommen wurden, um sie möglicherweise sicher zu implementieren) oder Nicht-Wiedereintritt sind häufige Ursachen für Missbilligung.

Veraltet,
nicht portierbar Einige andere Funktionen werden einfach veraltet, weil sie die Funktionalität duplizieren und nicht so portabel sind wie andere Varianten. Zum Beispiel ist bzero () zugunsten von memset () veraltet .

Thread-Sicherheit und Wiedereintritt
Sie haben in Ihrem Beitrag nach Thread-Sicherheit und Wiedereintritt gefragt. Es gibt einen kleinen Unterschied. Eine Funktion ist wiedereintrittsfähig, wenn sie keinen gemeinsamen, veränderlichen Status verwendet. Wenn beispielsweise alle benötigten Informationen an die Funktion übergeben werden und alle benötigten Puffer auch an die Funktion übergeben werden (anstatt von allen Aufrufen der Funktion gemeinsam genutzt zu werden), ist sie wiedereintrittsfähig. Das bedeutet, dass verschiedene Threads durch die Verwendung unabhängiger Parameter nicht das Risiko eingehen, versehentlich den Status zu teilen. Wiedereintritt ist eine stärkere Garantie als Gewindesicherheit. Eine Funktion ist threadsicher, wenn sie von mehreren Threads gleichzeitig verwendet werden kann. Eine Funktion ist threadsicher, wenn:

  • Es ist wiedereintrittsfähig (dh es teilt keinen Status zwischen Anrufen) oder:
  • Es ist nicht wiedereintrittsfähig, verwendet jedoch die für den gemeinsam genutzten Status erforderliche Synchronisierung / Sperrung.

Im Allgemeinen ist in der Single UNIX-Spezifikation und in IEEE 1003.1 (dh "POSIX") nicht garantiert, dass eine Funktion, deren Wiedereintritt nicht garantiert wird, threadsicher ist. Mit anderen Worten, in Multithread-Anwendungen (ohne externe Sperre) können nur Funktionen portabel verwendet werden, deren Wiedereintritt garantiert ist. Dies bedeutet jedoch nicht, dass Implementierungen dieser Standards sich nicht dafür entscheiden können, eine nicht wiedereintretende Funktion threadsicher zu machen. Beispielsweise fügt Linux häufig nicht wiedereintretenden Funktionen häufig eine Synchronisierung hinzu, um eine Garantie (über die der Single UNIX-Spezifikation hinaus) für die Thread-Sicherheit hinzuzufügen.

Strings (und Speicherpuffer im Allgemeinen)
Sie haben auch gefragt, ob bei Strings / Arrays ein grundlegender Fehler vorliegt. Einige mögen argumentieren, dass dies der Fall ist, aber ich würde argumentieren, dass es keinen grundlegenden Fehler in der Sprache gibt. In C und C ++ müssen Sie die Länge / Kapazität eines Arrays separat übergeben (dies ist keine ".length" -Eigenschaft wie in einigen anderen Sprachen). Dies ist per se kein Fehler. Jeder C- und C ++ - Entwickler kann korrekten Code schreiben, indem er bei Bedarf einfach die Länge als Parameter übergibt. Das Problem ist, dass mehrere APIs, für die diese Informationen erforderlich waren, diese nicht als Parameter angeben konnten. Oder angenommen, dass eine MAX_BUFFER_SIZE-Konstante verwendet wird. Solche APIs sind jetzt veraltet und werden durch alternative APIs ersetzt, mit denen die Array- / Puffer- / Zeichenfolgengrößen angegeben werden können.

Scanf (als Antwort auf Ihre letzte Frage)
Ich persönlich verwende die C ++ - iostreams-Bibliothek (std :: cin, std :: cout, die Operatoren << und >>, std :: getline, std :: istringstream, std :: ostringstream) usw.), damit beschäftige ich mich normalerweise nicht. Wenn ich jedoch gezwungen wäre, reines C zu verwenden, würde ich persönlich nur fgetc () oder getchar () in Kombination mit strtol () , strtoul () usw. verwenden und die Dinge manuell analysieren, da ich kein großer Fan von bin varargs oder Formatstrings. Nach meinem besten Wissen gibt es jedoch kein Problem mit [f] scanf () , [f] printf ()usw. Solange Sie die Formatzeichenfolgen selbst erstellen, übergeben Sie niemals beliebige Formatzeichenfolgen oder lassen zu, dass Benutzereingaben als Formatzeichenfolgen verwendet werden, und verwenden Sie gegebenenfalls die in <inttypes.h> definierten Formatierungsmakros . (Beachten Sie, dass snprintf () anstelle von sprintf () verwendet werden sollte. Dies hat jedoch damit zu tun, dass die Größe des Zielpuffers nicht angegeben wird und keine Formatzeichenfolgen verwendet werden.) Ich sollte auch darauf hinweisen, dass boost :: format in C ++ eine printf-ähnliche Formatierung ohne varargs bietet.

Michael Aaron Safyan
quelle
4
"Veraltet" ist ein starkes Wort, das bei der Erörterung des C ++ - Standards eine bestimmte Bedeutung hat. In diesem Sinne sind get (), strcpy () usw. nicht veraltet.
4
Nur solange Sie zwischen "vom C-Standard veraltet", "von Michael Aaron Safyan veraltet" und "von unbekannten oder unbekannten Personen veraltet, die hoffentlich wissen, wovon sie sprechen [Zitieren erforderlich]" unterscheiden. Die Frage ist etwa bevorzugte Art der Codierung nicht über den C - Standard, so dass die zweiten zwei geeignet sind. Aber wie Neil brauchte ich eine doppelte Aufnahme, bevor mir klar wurde, dass Ihre Aussagen nicht die erste Bedeutung implizieren wollten.
Steve Jessop
11
strncpysollte generell auch vermieden werden. Es macht nicht das, was die meisten Programmierer annehmen. Es garantiert keine Beendigung (was zu Pufferüberläufen führt) und füllt kürzere Zeichenfolgen auf (was in einigen Fällen möglicherweise die Leistung beeinträchtigt).
Adrian McCarthy
2
@Adrian: Ich stimme dir zu - weder strncpy()noch noch schlimmer strncat()ist ein vernünftiger Ersatz für die n-weniger Varianten.
Jonathan Leffler
4
Diese Antwort verbreitet ein Unsinn-Dogma über "Ersetze strcpy durch strncpy - ich weiß nicht warum, aber Microsoft sagt es mir." strncpy sollte niemals eine sichere Version von strcpy sein! Im Gesicht ist es weitaus unsicherer. Siehe Warum werden strlcpy und strlcat als unsicher angesehen? .
Lundin
24

Wieder wiederholen die Leute mantraartig die lächerliche Behauptung, dass die "n" -Version von str-Funktionen sichere Versionen sind.

Wenn dies das war, wofür sie bestimmt waren, würden sie die Zeichenfolgen immer mit Null beenden.

Die "n" -Versionen der Funktionen wurden für die Verwendung mit Feldern fester Länge (z. B. Verzeichniseinträge in frühen Dateisystemen) geschrieben, bei denen der nul-Terminator nur erforderlich ist, wenn die Zeichenfolge das Feld nicht ausfüllt. Dies ist auch der Grund, warum die Funktionen seltsame Nebenwirkungen haben, die sinnlos ineffizient sind, wenn sie nur als Ersatz verwendet werden - nehmen Sie zum Beispiel strncpy ():

Wenn das Array, auf das s2 zeigt, eine Zeichenfolge ist, die kürzer als n Bytes ist, werden Null-Bytes an die Kopie in dem Array angehängt, auf das s1 zeigt, bis insgesamt n Bytes geschrieben sind.

Da die für die Verarbeitung von Dateinamen zugewiesenen Puffer normalerweise 4 KB groß sind, kann dies zu einer massiven Verschlechterung der Leistung führen.

Wenn Sie "angeblich" sichere Versionen wünschen, erhalten oder schreiben Sie Ihre eigenen strl-Routinen (strlcpy, strlcat usw.), die die Strings immer nicht beenden und keine Nebenwirkungen haben. Bitte beachten Sie jedoch, dass diese nicht wirklich sicher sind, da sie die Zeichenfolge stillschweigend abschneiden können - dies ist selten die beste Vorgehensweise in einem realen Programm. Es gibt Fälle, in denen dies in Ordnung ist, aber es gibt auch viele Umstände, in denen dies zu katastrophalen Ergebnissen führen kann (z. B. Ausdrucken von medizinischen Rezepten).

Ölmessstab
quelle
1
Sie haben Recht strncpy(), aber Unrecht strncat(). strncat()wurde nicht für die Verwendung mit Feldern fester Länge entwickelt - es wurde tatsächlich so konzipiert, strcat()dass die Anzahl der verketteten Zeichen begrenzt wird. Es ist recht einfach, dies als "sicher strcat()" zu verwenden, indem der im Puffer verbleibende Speicherplatz bei mehreren Verkettungen verfolgt wird, und es ist noch einfacher, ihn als "sicher strcpy()" zu verwenden (indem das erste Zeichen des Zielpuffers auf " '\0'Vorher" gesetzt wird nenne es). Beendet strncat() immer die Zielzeichenfolge und schreibt keine zusätzlichen '\0's.
Café
2
@caf - ja, aber strncat () ist völlig nutzlos, da als Parameter die maximal zu kopierende Länge verwendet wird - nicht die Länge des Zielpuffers. Um einen Pufferüberlauf zu vermeiden, müssen Sie die Länge der aktuellen Zielzeichenfolge kennen. Wenn Sie wissen, warum sollten Sie strncat () verwenden, um die Ziellänge erneut zu ermitteln, anstatt nur die Quellzeichenfolge strlcat () zu verwenden das Ende der Zielzeichenfolge.
Ölmessstab
Das ändert nichts an der Tatsache, dass Sie implizieren, dass strncat()das Ziel nicht immer nicht terminiert wird und dass es für die Verwendung mit Feldern fester Länge entwickelt wurde, die beide falsch sind.
Café
2
@chrisharris: Funktioniert strncat()unabhängig von der Länge der Quellzeichenfolge ordnungsgemäß, strcat()jedoch nicht. Das Problem strlcat()dabei ist, dass es sich nicht um eine Standard-C-Funktion handelt.
David Thornley
@caf - Sie haben Recht mit strncat (). Ich habe es nie benutzt, weil es - wie ich oben erwähnt habe - völlig nutzlos ist. Es sollte daher immer noch vermieden werden.
Ölmessstab
19

Einige Antworten hier schlagen vor, strncat()over zu verwenden strcat(); Ich würde vorschlagen, dass strncat()(und strncpy()) auch vermieden werden sollten. Es gibt Probleme, die die korrekte Verwendung erschweren und zu Fehlern führen:

  • Der Längenparameter to strncat()bezieht sich auf die maximale Anzahl von Zeichen, die auf das Ziel kopiert werden können (aber nicht ganz genau - siehe 3. Punkt) und nicht auf die Größe des Zielpuffers. Dies macht strncat()die Verwendung schwieriger als es sein sollte, insbesondere wenn mehrere Elemente mit dem Ziel verknüpft werden.
  • Es kann schwierig sein festzustellen, ob das Ergebnis abgeschnitten wurde (was wichtig sein kann oder nicht).
  • Es ist leicht, einen Fehler nach dem anderen zu haben. Wie der C99-Standard feststellt, ist "Die maximale Anzahl von Zeichen, die in dem Array landen können, auf das gezeigt s1wird, strlen(s1)+n+1" für einen Anruf, der aussiehtstrncat( s1, s2, n)

strncpy()Es gibt auch ein Problem, das zu Fehlern führen kann, die Sie versuchen, es auf intuitive Weise zu verwenden. Es garantiert nicht, dass das Ziel nullterminiert ist. Um sicherzustellen, dass Sie sicherstellen müssen, dass Sie diesen Eckfall speziell behandeln, indem '\0'Sie a (zumindest in bestimmten Situationen) selbst an der letzten Stelle des Puffers ablegen.

Ich würde vorschlagen, etwas wie OpenBSDs zu verwenden strlcat()und strlcpy()(obwohl ich weiß, dass einige Leute diese Funktionen nicht mögen; ich glaube, sie sind viel einfacher sicher zu verwenden als strncat()/ strncpy()).

Hier ist ein wenig von dem, was Todd Miller und Theo de Raadt über Probleme mit strncat()und zu sagen hatten strncpy():

Es treten verschiedene Probleme auf, wenn strncpy()und strncat()als sichere Versionen von strcpy()und verwendet werden strcat(). Beide Funktionen behandeln die NUL-Terminierung und den Längenparameter auf unterschiedliche und nicht intuitive Weise, was selbst erfahrene Programmierer verwirrt. Sie bieten auch keine einfache Möglichkeit, zu erkennen, wann eine Kürzung auftritt. ... Von all diesen Problemen sind die Verwirrung durch die Längenparameter und das damit verbundene Problem der NUL-Terminierung am wichtigsten. Als wir den OpenBSD-Quellbaum auf potenzielle Sicherheitslücken überprüften, stellten wir einen weit verbreiteten Missbrauch von strncpy()und fest strncat(). Obwohl nicht alle zu ausnutzbaren Sicherheitslücken führten, machten sie deutlich, dass die Regeln für die Verwendung strncpy()und strncat()für sichere Zeichenfolgenoperationen weitgehend missverstanden werden.

Die Sicherheitsüberprüfung von OpenBSD ergab, dass Fehler mit diesen Funktionen "weit verbreitet" waren. Im Gegensatz zu gets(), diese Funktionen können sicher verwendet werden, aber in der Praxis gibt es viele Probleme gibt , da die Schnittstelle ist verwirrend, nicht intuitiv und schwer richtig zu verwenden. Ich weiß, dass Microsoft auch Analysen durchgeführt hat (obwohl ich nicht weiß, wie viele ihrer Daten sie möglicherweise veröffentlicht haben) und infolgedessen das Verbot verboten (oder zumindest sehr stark entmutigt - das "Verbot" ist möglicherweise nicht absolut) hat Verwendung von strncat()und strncpy()(unter anderem).

Einige Links mit weiteren Informationen:

Michael Burr
quelle
1
Es ist weitaus besser, die Länge der Saiten außerhalb der Saite selbst zu verfolgen. Auf diese Weise wird das sichere Verketten von zwei Zeichenfolgen (-mit-Länge) zu einer einfachen Berechnung (um die erforderliche Puffergröße zu erhalten), einer möglichen Neuzuweisung und a memmove(). (Nun, Sie können memcpy()im Normalfall verwenden, wenn die Zeichenfolgen unabhängig sind.)
Donal Fellows
1
Ihr zweiter Punkt ist absolut falsch - beendet strncat() immer die Zielzeichenfolge.
Café
1
Ihr zweiter Punkt ist falsch für strncat(). Es ist jedoch richtig für strncpy(), was einige andere Probleme hat. strncat()ist ein angemessener Ersatz für strcat(), aber strncpy()kein angemessener Ersatz für strcpy().
David Thornley
2
caf und David haben 100% Recht mit meiner Behauptung, strncat()die nicht immer null endet. Ich habe das Verhalten von strncat()und strncpy()ein bisschen verwirrt (ein weiterer Grund, warum sie zu vermeidende Funktionen sind - sie haben Namen, die ähnliche Verhaltensweisen implizieren, sich aber in wichtigen Punkten tatsächlich anders verhalten ...). Ich habe meine Antwort geändert, um dies zu korrigieren und zusätzliche Informationen hinzuzufügen.
Michael Burr
1
Beachten Sie, dass dies char str[N] = ""; strncat(str, "long string", sizeof(str));ein Pufferüberlauf ist, wenn N nicht groß genug ist. Die strncat()Funktion ist zu leicht zu missbrauchen. es sollte nicht verwendet werden. Wenn Sie strncat()sicher verwenden können, könnten Sie memmove()oder memcpy()stattdessen verwendet haben (und diese wären effizienter).
Jonathan Leffler
9

Standardbibliotheksfunktionen, die niemals verwendet werden sollten:

setjmp.h

  • setjmp(). Zusammen mit longjmp()diesen Funktionen wird allgemein als unglaublich gefährlich angesehen: Sie führen zu Spaghetti-Programmierung, sie weisen zahlreiche Formen undefinierten Verhaltens auf und können unbeabsichtigte Nebenwirkungen in der Programmumgebung verursachen, z. B. die Beeinflussung der auf dem Stapel gespeicherten Werte. Referenzen: MISRA-C: 2012 Regel 21.4, CERT C MSC22-C .
  • longjmp(). Siehe setjmp().

stdio.h

  • gets(). Die Funktion wurde aus der C-Sprache entfernt (gemäß C11), da sie gemäß Design unsicher war. Die Funktion wurde bereits in C99 als veraltet markiert. Verwenden Sie fgets()stattdessen. Referenzen: ISO 9899: 2011 K.3.5.4.1, siehe auch Anmerkung 404.

stdlib.h

  • atoi()Funktionsfamilie. Diese haben keine Fehlerbehandlung, rufen jedoch bei Auftreten von Fehlern undefiniertes Verhalten auf. Völlig überflüssige Funktionen, die durch die ersetzt werden könnenstrtol() Funktionsfamilie ersetzt werden können. Referenzen: MISRA-C: 2012 Regel 21.7.

string.h

  • strncat(). Hat eine umständliche Oberfläche, die oft missbraucht wird. Es ist meist eine überflüssige Funktion. Siehe auch Anmerkungen zustrncpy() .
  • strncpy(). Die Absicht dieser Funktion war niemals, eine sicherere Version von zu sein strcpy(). Sein einziger Zweck war immer, ein altes String-Format auf Unix-Systemen zu verarbeiten, und dass es in die Standardbibliothek aufgenommen wurde, ist ein bekannter Fehler. Diese Funktion ist gefährlich, da die Zeichenfolge möglicherweise ohne Nullterminierung bleibt und Programmierer sie häufig falsch verwenden. Referenzen: Warum werden strlcpy und strlcat als unsicher angesehen? .

Standardfunktionen der Bibliothek, die mit Vorsicht verwendet werden sollten:

assert.h

  • assert(). Kommt mit Overhead und sollte im Allgemeinen nicht im Produktionscode verwendet werden. Es ist besser, einen anwendungsspezifischen Fehlerbehandler zu verwenden, der Fehler anzeigt, aber nicht unbedingt das gesamte Programm schließt.

signal.h

stdarg.h

  • va_arg()Funktionsfamilie. Das Vorhandensein von Funktionen variabler Länge in einem C-Programm ist fast immer ein Hinweis auf ein schlechtes Programmdesign. Sollte vermieden werden, es sei denn, Sie haben sehr spezielle Anforderungen.

stdio.h Im
Allgemeinen wird diese gesamte Bibliothek nicht für Produktionscode empfohlen , da es zahlreiche Fälle von schlecht definiertem Verhalten und schlechter Typensicherheit gibt.

  • fflush(). Perfekt für Ausgabestreams. Ruft undefiniertes Verhalten auf, wenn es für Eingabestreams verwendet wird.
  • gets_s(). Sichere Version der gets()in C11 enthaltenen Schnittstelle zur Überprüfung der Grenzen. Es wird bevorzugt, fgets()stattdessen gemäß der C-Standardempfehlung zu verwenden. Referenzen: ISO 9899: 2011 K.3.5.4.1.
  • printf()Funktionsfamilie. Ressourcenintensive Funktionen, die mit viel undefiniertem Verhalten und schlechter Typensicherheit einhergehen. sprintf()hat auch Schwachstellen. Diese Funktionen sollten im Produktionscode vermieden werden. Referenzen: MISRA-C: 2012 Regel 21.6.
  • scanf()Funktionsfamilie. Siehe Anmerkungen zu printf(). Außerdem ist - scanf()anfällig für Pufferüberläufe, wenn es nicht richtig verwendet wird. fgets()wird bevorzugt verwendet, wenn möglich. Referenzen: CERT C INT05-C , MISRA-C: 2012 Regel 21.6.
  • tmpfile()Funktionsfamilie. Kommt mit verschiedenen Schwachstellenproblemen. Referenzen: CERT C FIO21-C .

stdlib.h

  • malloc()Funktionsfamilie. Perfekt für gehostete Systeme geeignet. Beachten Sie jedoch die bekannten Probleme in C90 und geben Sie das Ergebnis nicht weiter . Die malloc()Funktionsfamilie sollte niemals in freistehenden Anwendungen verwendet werden. Referenzen: MISRA-C: 2012 Regel 21.3.

    Beachten Sie auch, dass dies realloc()gefährlich ist, wenn Sie den alten Zeiger mit dem Ergebnis von überschreiben realloc(). Wenn die Funktion fehlschlägt, erstellen Sie ein Leck.

  • system(). Mit viel Aufwand verbunden und obwohl portabel, ist es oft besser, stattdessen systemspezifische API-Funktionen zu verwenden. Kommt mit verschiedenen schlecht definierten Verhaltensweisen. Referenzen: CERT C ENV33-C .

string.h

  • strcat(). Siehe Anmerkungen zustrcpy() .
  • strcpy(). Perfekt zu verwenden, es sei denn, die Größe der zu kopierenden Daten ist unbekannt oder größer als der Zielpuffer. Wenn die Größe der eingehenden Daten nicht überprüft wird, kann es zu Pufferüberläufen kommen. Was nicht an sich strcpy()selbst schuld ist , sondern an der aufrufenden Anwendung - das strcpy()ist unsicher, ist meistens ein Mythos, der von Microsoft erstellt wurde .
  • strtok(). Ändert die Aufruferzeichenfolge und verwendet interne Statusvariablen, wodurch sie in einer Umgebung mit mehreren Threads unsicher werden kann.
Lundin
quelle
Ich empfehle static_assert()anstelle von zu empfehlen assert(), ob die Bedingung zur Kompilierungszeit behoben werden kann. Auch sprintf()kann fast immer ausgetauscht werden, snprintf()was etwas sicherer ist.
user694733
1
Die Verwendung strtok()in einer Funktion A bedeutet, dass (a) die Funktion keine andere Funktion aufrufen kann, die ebenfalls verwendet wird, strtok()während A sie verwendet, und (b) bedeutet, dass keine Funktion, die A aufruft, verwendet werden kann, strtok()wenn sie A aufruft. Mit anderen Worten: using strtok()vergiftet die Anrufkette; Es kann nicht sicher im Bibliothekscode verwendet werden, da es dokumentieren muss strtok(), dass andere Benutzer strtok()den Bibliothekscode nicht aufrufen können.
Jonathan Leffler
Das strncpyist die richtige Funktion zu verwenden , wenn ein Schreiben von null- gepolsterten String - Puffern mit Daten aus entweder einer Null-terminierte Zeichenkette oder ein mit Nullen aufgefüllt Puffern , deren Größe genommen ist mindestens so groß wie das Ziel. Null-gepolsterte Puffer sind nicht besonders häufig, aber auch nicht gerade dunkel.
Supercat
7

Einige Leute würden das behaupten strcpyund strcatsollten vermieden werden, zugunsten von strncpyund strncat. Das ist meiner Meinung nach etwas subjektiv.

Sie sollten beim Umgang mit Benutzereingaben unbedingt vermieden werden - hier kein Zweifel.

Im Code "weit weg vom Benutzer", wenn Sie nur wissen, dass die Puffer lang genug sind strcpyund strcatmöglicherweise etwas effizienter sind, da die Berechnung der nWeitergabe an ihre Cousins ​​möglicherweise überflüssig ist.

Eli Bendersky
quelle
4
Und falls verfügbar, noch besser strlcatund strlcpy, da die 'n'-Version keine NULL-Beendigung der Zielzeichenfolge garantiert.
Dan Andreatta
Klingt für mich nach vorzeitiger Optimierung. Aber IIRC strncpy()schreibt genau Schreibbytes nund verwendet bei Bedarf keine Zeichen. Als Dan ist die Verwendung einer sicheren Version die beste Wahl, IMO.
Bastien Léonard
@ Dan, diese Funktionen sind leider nicht Standard und gehören daher nicht zu dieser Diskussion
Eli Bendersky
@Eli: aber die Tatsache, dass strncat()es schwierig sein kann, richtig und sicher zu verwenden (und vermieden werden sollte), ist ein Thema.
Michael Burr
@ Michael: Ich würde nicht sagen, dass es schwierig ist, richtig und sicher zu verwenden. Mit Sorgfalt funktioniert es gut
Eli Bendersky
6

Vermeiden

  • strtok für Multithread-Programme als nicht threadsicher.
  • gets da dies einen Pufferüberlauf verursachen könnte
Codaddict
quelle
2
Es sind etwas andere Fälle. Sie können strtok sicher verwenden, wenn Sie wissen, dass Ihr Programm kein Multithreading ist oder Sie den Zugriff darauf sperren, aber Sie können get nicht sicher verwenden, sollten es also niemals verwenden.
Jcoder
9
Das Problem mit strtok()geht ein wenig über die Thread-Sicherheit hinaus - es ist selbst in einem einzelnen Thread-Programm nicht sicher, es sei denn, Sie wissen mit Sicherheit, dass keine Funktionen, die Ihr Code während der Verwendung strtok()aufruft, diese auch nicht verwenden (oder den strtok()Status des Benutzers durcheinander bringen) von unter dir). Tatsächlich kümmern sich die meisten Compiler, die auf Multithread-Plattformen abzielen, um strtok()potenzielle Probleme in Bezug auf Threads, indem sie threadlokalen Speicher für strtok()statische Daten verwenden. Aber das behebt immer noch nicht das Problem, dass andere Funktionen es verwenden, während Sie (im selben Thread) sind.
Michael Burr
In der Tat und ich werde jetzt die Klappe halten, da ich niemanden ermutigen möchte, strtok zu verwenden, da es unter vielen Problemen leidet. Ich wollte nur darauf hinweisen , dass es anders aus bekommt obwohl , wie es ist möglich , es zu benutzen sicher , während es zu benutzen unmöglich ist , bekommt sicher.
Jcoder
@ Jimmy: Es gibt oft nicht standardmäßige Bibliothekserweiterungen, oder Sie könnten Ihre eigenen schreiben. Das große Problem dabei strtok()ist, dass genau ein Puffer zum Arbeiten vorhanden ist, sodass für die meisten guten Ersetzungen ein Pufferwert beibehalten und übergeben werden muss.
David Thornley
strcspnerledigt das meiste, was Sie brauchen - finden Sie das nächste Token-Trennzeichen. Sie können damit eine vernünftige Variante erneut implementieren strtok.
Bluss
5

Es lohnt sich wahrscheinlich, noch einmal hinzuzufügen, dass dies strncpy()nicht der allgemeine Ersatz für iststrcpy() den Namen ist. Es wurde für Felder mit fester Länge entwickelt, für die kein Nullterminator erforderlich ist (es wurde ursprünglich für die Verwendung mit UNIX-Verzeichniseinträgen entwickelt, kann jedoch beispielsweise für Verschlüsselungsschlüsselfelder hilfreich sein).

Es ist jedoch einfach strncat()als Ersatz für strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

(Der ifTest kann natürlich im allgemeinen Fall abgebrochen werden, wenn Sie wissen, dass dies dest_sizedefinitiv ungleich Null ist).

caf
quelle
5

Lesen Sie auch die Liste der gesperrten APIs von Microsoft . Hierbei handelt es sich um APIs (einschließlich vieler hier bereits aufgelisteter APIs), die aus Microsoft-Code verbannt sind, da sie häufig missbraucht werden und zu Sicherheitsproblemen führen.

Sie sind vielleicht nicht mit allen einverstanden, aber sie sind alle eine Überlegung wert. Sie fügen der Liste eine API hinzu, wenn deren Missbrauch zu einer Reihe von Sicherheitslücken geführt hat.

Adrian McCarthy
quelle
2

Fast jede Funktion, die sich mit NUL-terminierten Zeichenfolgen befasst, ist möglicherweise unsicher. Wenn Sie Daten von außen empfangen und über die Funktionen str * () bearbeiten, bereiten Sie sich auf eine Katastrophe vor

rep_movsd
quelle
2

Sprintf nicht vergessen - es ist die Ursache vieler Probleme. Dies ist richtig, da die Alternative snprintf manchmal unterschiedliche Implementierungen hat, die dazu führen können, dass Sie nicht portierbar sind.

  1. Linux: http://linux.die.net/man/3/snprintf

  2. Windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

In Fall 1 (Linux) ist der Rückgabewert die Datenmenge, die zum Speichern des gesamten Puffers benötigt wird (wenn er kleiner als die Größe des angegebenen Puffers ist, wurde die Ausgabe abgeschnitten).

In Fall 2 (Fenster) ist der Rückgabewert eine negative Zahl, falls die Ausgabe abgeschnitten wird.

Im Allgemeinen sollten Sie Funktionen vermeiden, die nicht:

  1. Pufferüberlauf sicher (viele Funktionen werden hier bereits erwähnt)

  2. thread sicher / nicht wiedereintrittsfähig (strtok zum Beispiel)

Im Handbuch der einzelnen Funktionen sollten Sie nach Schlüsselwörtern wie "Safe", "Sync", "Async", "Thread", "Buffer" und "Bugs" suchen

INS
quelle
2
_sprintf()wurde von Microsoft erstellt, bevor der Standard snprintf()IIRC eintraf. ´StringCbPrintf () ´ ist allerdings ziemlich ähnlich snprintf().
Bastien Léonard
Sie verwenden können , sprintfin einigen Fällen irgendwie sicher: sprintf(buffer,"%10s",input);begrenzt die kopiert nb bis 10 Bytes (wenn bufferist char buffer[11]es sicher ist, selbst wenn die Daten abgeschnitten aufzuwickeln kann.
Jean-François Fabre
2

Es ist sehr schwer scanfsicher zu verwenden . Durch eine gute Verwendung von scanfkönnen Pufferüberläufe vermieden werden. Sie sind jedoch weiterhin anfällig für undefiniertes Verhalten, wenn Sie Zahlen lesen, die nicht in den angeforderten Typ passen. In den meisten Fällen , fgetsgefolgt von Selbst Parsing (unter Verwendung sscanf, strchretc.) ist eine bessere Option.

Aber ich würde nicht sagen "die scanfganze Zeit vermeiden ". scanfhat seine Verwendung. Angenommen, Sie möchten Benutzereingaben in einem char10 Byte langen Array lesen . Sie möchten gegebenenfalls die nachfolgende neue Zeile entfernen. Wenn der Benutzer mehr als 9 Zeichen vor einer neuen Zeile eingibt, möchten Sie die ersten 9 Zeichen im Puffer speichern und alles bis zur nächsten neuen Zeile verwerfen. Du kannst tun:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Sobald Sie sich an diese Redewendung gewöhnt haben, ist sie kürzer und in gewisser Weise sauberer als:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
Alok Singhal
quelle
0

In allen Szenarien zum Kopieren / Verschieben von Zeichenfolgen - strcat (), strncat (), strcpy (), strncpy () usw. - läuft es viel besser ( sicherer ), wenn ein paar einfache Heuristiken erzwungen werden:

   1. Immer NUL-füllen Ihre Puffer vor dem Hinzufügen von Daten.
   2. Deklarieren Sie Zeichenpuffer als [GRÖSSE + 1] mit einer Makrokonstante.

Zum Beispiel gegeben:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

Wir können Code verwenden wie:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

relativ sicher. Das memset () sollte vor dem strncpy () erscheinen, obwohl wir Buffer zur Kompilierungszeit initialisiert haben, da wir nicht wissen, welchen Müll anderer Code darin abgelegt hat, bevor unsere Funktion aufgerufen wurde. Strncpy () schneidet die kopierten Daten auf "1234567890" ab und beendet sie nicht mit NUL. Da wir jedoch bereits den gesamten Puffer mit NUL gefüllt haben - sizeof (Buffer) anstelle von BUFSIZE -, wird garantiert, dass NUL endgültig "außerhalb des Gültigkeitsbereichs" endet, solange wir unsere Schreibvorgänge mit BUFSIZE einschränken konstant anstelle von sizeof (Buffer).

Buffer und BUFSIZE funktionieren ebenfalls gut für snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Obwohl snprintf () speziell nur BUFIZE-1-Zeichen gefolgt von NUL schreibt, funktioniert dies sicher. Wir "verschwenden" also ein fremdes NUL-Byte am Ende von Buffer ... wir verhindern sowohl Pufferüberlauf als auch nicht abgeschlossene String-Bedingungen für einen relativ geringen Speicheraufwand.

Mein Aufruf von strcat () und strncat () ist schwieriger: Verwenden Sie sie nicht. Es ist schwierig, strcat () sicher zu verwenden, und die API für strncat () ist so kontraintuitiv, dass der Aufwand für die ordnungsgemäße Verwendung jeden Vorteil zunichte macht. Ich schlage folgendes Drop-In vor:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

Es ist verlockend, ein Drop-In für strcat () zu erstellen, aber keine gute Idee:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

da das Ziel möglicherweise ein Zeiger ist (daher gibt sizeof () nicht die benötigten Informationen zurück). Ich habe keine gute "universelle" Lösung für Instanzen von strcat () in Ihrem Code.

Ein Problem, auf das ich häufig von "strFunc () - fähigen" Programmierern stoße, ist der Versuch, mit strlen () vor Pufferüberläufen zu schützen. Dies ist in Ordnung, wenn garantiert ist, dass der Inhalt NUL-terminiert ist. Andernfalls kann strlen () selbst einen Pufferüberlauffehler verursachen (der normalerweise zu einer Segmentierungsverletzung oder einer anderen Core-Dump-Situation führt), bevor Sie jemals den "problematischen" Code erreichen, den Sie schützen möchten.

TLR
quelle
-2

atoi ist nicht threadsicher. Ich verwende stattdessen strtol gemäß der Empfehlung auf der Manpage.

Fred
quelle
5
Dies scheint für eine bestimmte Implementierung zu gelten. Es gibt keinen Grund, warum strtol()es threadsicher atoi()wäre und nicht.
David Thornley
2
Die Empfehlung, strtol zu verwenden, hat nichts mit der Thread-Sicherheit zu tun, sondern mit der Fehlerbehandlung. Ich bin mir nicht sicher, von welcher Manpage Sie diese Informationen erhalten haben - ich kann unten keine Empfehlung finden man atoi(sollte es aber geben).
Lundin