Welche Funktionen in der C-Standardbibliothek fördern häufig schlechte Praktiken? [closed]

Lesezeit: 10 Minuten

Dies wird durch diese Frage und die Kommentare zu einer bestimmten Antwort inspiriert, in denen ich das gelernt habe strncpy ist keine sehr sichere String-Handling-Funktion in C und füllt Nullen auf, bis sie erreicht nwas mir nicht bewusst war.

Konkret, um R. zu zitieren.

strncpy endet nicht mit Null und füllt den gesamten Rest des Zielpuffers mit Null auf, was eine enorme Zeitverschwendung ist. Ersteres können Sie umgehen, indem Sie Ihr eigenes Null-Padding hinzufügen, letzteres jedoch nicht. Es war nie für die Verwendung als “sichere Zeichenfolgenbehandlungsfunktion” gedacht, sondern für die Arbeit mit Feldern fester Größe in Unix-Verzeichnistabellen und Datenbankdateien. snprintf(dest, n, “%s”, src) ist das einzig korrekte “sichere strcpy” in Standard-C, aber es ist wahrscheinlich viel langsamer. Übrigens kann das Abschneiden an sich ein großer Fehler sein und in einigen Fällen zu einer Erhöhung der Berechtigungen oder DoS führen. Daher ist das Auslösen von “sicheren” Zeichenfolgenfunktionen, die ihre Ausgabe bei einem Problem abschneiden, keine Möglichkeit, es “sicher” oder ” sicher”. Stattdessen sollten Sie sicherstellen, dass der Zielpuffer die richtige Größe hat, und einfach strcpy verwenden (oder noch besser memcpy, wenn Sie die Länge des Quellstrings bereits kennen).

Und von Jonathan Leffler

Beachten Sie, dass strncat() in seiner Schnittstelle noch verwirrender ist als strncpy() – was genau ist das Längenargument noch einmal? Es ist nicht das, was Sie erwarten würden, basierend auf dem, was Sie strncpy() usw. bereitstellen – daher ist es sogar fehleranfälliger als strncpy(). Beim Kopieren von Strings bin ich zunehmend der Meinung, dass es ein starkes Argument dafür gibt, dass man nur memmove() braucht, weil man immer alle Größen im Voraus kennt und dafür sorgt, dass genügend Platz im Voraus vorhanden ist. Verwenden Sie memmove() gegenüber strcpy(), strcat(), strncpy(), strncat(), memcpy().

Ich bin also eindeutig ein wenig eingerostet in Bezug auf die C-Standardbibliothek. Daher möchte ich die Frage stellen:

Welche C-Standardbibliotheksfunktionen werden unangemessen/in einer Weise verwendet, die Sicherheitsprobleme/Codedefekte/Ineffizienzen verursachen/führen kann?

Im Interesse der Objektivität habe ich eine Reihe von Kriterien für eine Antwort:

  • Bitte geben Sie, wenn möglich, gestalterische Gründe für die betreffende Funktion an, dh ihren Verwendungszweck.
  • Bitte markieren Sie den Missbrauch, dem der Code derzeit ausgesetzt ist.
  • Bitte geben Sie an, warum dieser Missbrauch zu einem Problem führen kann. Ich weiß, das sollte offensichtlich sein, aber es verhindert sanfte Antworten.

Bitte vermeiden Sie:

  • Debatten über Namenskonventionen von Funktionen (außer wenn dies eindeutig zu Verwirrung führt).
  • “Ich bevorzuge x gegenüber y” – Präferenz ist in Ordnung, wir alle haben sie, aber ich interessiere mich für tatsächliche unerwartete Nebenwirkungen und wie man sich davor schützt.

Da dies wahrscheinlich als subjektiv angesehen wird und keine eindeutige Antwort hat, melde ich mich sofort für das Community-Wiki an.

Ich arbeite auch nach C99.

  • Jede Funktion kann unangemessen und auf eine Weise verwendet werden, die zu Sicherheitslücken führen kann.

    – Famarri

    3. Januar 2011 um 21:36 Uhr

  • @Falmarri – aber einige werden häufig unangemessen verwendet, wo andere dies nicht tun, einige scheinen den Missbrauch zu fördern, wo andere dies nicht tun.

    Benutzer180247

    3. Januar 2011 um 21:41 Uhr

Welche C-Standardbibliotheksfunktionen werden unangemessen/in einer Weise verwendet, die Sicherheitsprobleme/Codedefekte/Ineffizienzen verursachen/führen kann?

Ich werde mit dem Offensichtlichen gehen:

char *gets(char *s);

Mit seiner bemerkenswerten Besonderheit, dass es einfach unmöglich ist, es angemessen zu verwenden.

  • MacOS X gibt tatsächlich eine Laufzeitwarnung aus, wenn Sie es verwenden.

    – eine Masse

    3. Januar 2011 um 21:53 Uhr

  • gets(): der absolute Nullpunkt der Softwaresicherheit.

    – j_random_hacker

    3. Januar 2011 um 21:58 Uhr

  • Beachten Sie, dass C0x entfernt wird gets() von der Norm. Leider wird es weitere 10 bis 20 Jahre dauern, nachdem dies abgeschlossen ist, bevor es aus den meisten Implementierungen entfernt wird – die Abwärtskompatibilität mit Unsicherheit diktiert dies.

    – Jonathan Leffler

    3. Januar 2011 um 23:23 Uhr

  • @onemasse: wirklich? Ich hatte es nicht bemerkt (aber ich benutze es nicht einmal in Wegwerfcode!). Viel besser, dass davor gewarnt wird, als davor mktemp()die ich regelmäßig in einigen Codes sehe, an denen ich arbeite.

    – Jonathan Leffler

    3. Januar 2011 um 23:24 Uhr

  • MSVC könnte. Ihr Deal mit dem Komitee ist, dass sie den neuen Standard unterstützen werden, wenn das Komitee all ihre abscheulichen Dinge hinzufügt *_s “sichere” Funktionen zum Standard, um *nix-Implementierungen zu zwingen, sich damit zu verschmutzen. 😉

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    4. Januar 2011 um 2:45 Uhr

Ein häufiger Fallstrick bei der strtok() Funktion soll davon ausgehen, dass die geparste Zeichenfolge unverändert bleibt, während sie tatsächlich das Trennzeichen durch ersetzt '\0'.

Ebenfalls, strtok() wird durch nachfolgende Aufrufe verwendet, bis die gesamte Zeichenfolge tokenisiert ist. Einige Bibliotheksimplementierungen speichern strtok()den internen Status von in einer globalen Variablen, was zu bösen Überraschungen führen kann, wenn strtok() wird von mehreren Threads gleichzeitig aufgerufen.

Das CERT C Secure Coding Standard listet viele dieser Fallstricke auf, nach denen Sie gefragt haben.

  • +1 Für das Spiegeln meiner Gedanken zu strtok() und für die Erwähnung des CERT C Secure Coding Standard.

    – Jonathan Leffler

    3. Januar 2011 um 22:28 Uhr

  • Technisch gesehen ist es eher die Bibliotheksfunktion als der Compiler, der den Zustand speichert. Das große Problem ist, wenn Sie ein Token in Ihrem String isolieren und dann eine Funktion aufrufen, die, ohne dass Sie es wissen, selbst aufruft strtok().

    – Jonathan Leffler

    3. Januar 2011 um 22:34 Uhr

  • strtok ist erforderlich um seinen internen Status auch mit Threads global beizubehalten, zumindest in einer POSIX-Umgebung, in der Threads angegeben sind. Dies liegt daran, dass ein konformes Programm mit dem Parsen in einem Thread beginnen und in einem anderen enden könnte. Natürlich hat MS seine eigene Version von Threads, in der sie das unterschiedliche (thread-lokale) Verhalten wie sie angeben können, aber es steht im Konflikt mit POSIX.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    4. Januar 2011 um 2:47 Uhr

  • Dies ist jetzt ein Community-Wiki, das gut ist, aber es sieht immer noch so aus, als müsste ich eine Antwort akzeptieren, also akzeptiere ich diese für den CERT C Secure Coding Standard, der Unmengen nützlicher Informationen bereitstellt.

    Benutzer257111

    4. Januar 2011 um 22:42 Uhr

  • Ich bin verwirrt, warum niemand erwähnt hat strtok_r als (etwas) weniger verwirrend, da es den globalen Zustand nicht beibehält.

    – Licht

    11. Januar 2015 um 21:46 Uhr

In fast allen Fällen atoi() nicht verwendet werden (gilt auch für atof(), atol() und atoll()).

Dies liegt daran, dass diese Funktionen Out-of-Range-Fehler überhaupt nicht erkennen – sagt der Standard einfach “Wenn der Wert des Ergebnisses nicht dargestellt werden kann, ist das Verhalten undefiniert.”. Sie können also nur dann sicher verwendet werden, wenn Sie nachweisen können, dass die Eingabe sicherlich innerhalb des Bereichs liegt (z. B. wenn Sie eine Zeichenfolge der Länge 4 oder weniger an übergeben atoi()es darf nicht außerhalb des Bereichs liegen).

Verwenden Sie stattdessen eine der strtol() Familie von Funktionen.

  • +1 für den Hinweis auf die (meist theoretische, aber immer noch) Gefahr atoi und U.B.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    4. Januar 2011 um 3:10 Uhr

  • Ausgezeichneter Punkt. Es gibt keinen Grund zu verwenden ato*.

    – Stefan Kanon

    4. Januar 2011 um 19:09 Uhr

  • Es ist eigentlich ziemlich praktisch, wenn Sie wissen, auf welcher Plattform Ihr Code ausgeführt wird, was Sie wahrscheinlich tun. Sagt zB MSVC Der Rückgabewert ist 0 für atoi und _wtoiwenn die Eingabe nicht in einen Wert dieses Typs konvertiert werden kann., also ist es ziemlich gut verteidigt. (Außerdem ist dies ein weiteres Beispiel, bei dem „undefiniert“ und „implementierungsdefiniert“ eigentlich nicht genau unterschiedlich sind – sie können beide durch die Implementierung definiert werden.)

    – Benutzer541686

    12. November 2011 um 5:09 Uhr


Erweitern wir die Frage auf Schnittstellen im weiteren Sinne.

errno:

technisch ist nicht einmal klar, was es ist, eine Variable, ein Makro, ein impliziter Funktionsaufruf? In der Praxis auf modernen Systemen ist es meistens ein Makro, das sich in einen Funktionsaufruf verwandelt, um einen Thread-spezifischen Fehlerzustand zu haben. Es ist böse:

  • da es für den Aufrufer zu einem Overhead führen kann, auf den Wert zuzugreifen, um den “Fehler” zu überprüfen (der möglicherweise nur ein außergewöhnliches Ereignis ist).
  • weil es an einigen Stellen sogar vorschreibt, dass der Aufrufer diese “Variable” löscht, bevor er eine Bibliothek aufruft
  • weil es eine einfache Fehlerrückgabe implementiert, indem es einen globalen Zustand der Bibliothek festlegt.

Der kommende Standard erhält die Definition von errno ein bisschen gerader, aber diese Hässlichkeiten bleiben

Es gibt oft ein strtok_r.

Wenn Sie für realloc den alten Zeiger verwenden müssen, ist es nicht so schwierig, eine andere Variable zu verwenden. Wenn Ihr Programm mit einem Zuordnungsfehler fehlschlägt, ist das Aufräumen des alten Zeigers oft nicht wirklich notwendig.

  • Ich wollte sagen, dass dies ein Kommentar sein sollte, keine Antwort, aber Sie können nicht ohne Wiederholung kommentieren, also hier, haben Sie welche.

    – Stefan Kanon

    3. Januar 2011 um 23:09 Uhr

  • An dem Punkt, an dem Sie sagen: “Oft gibt es strtok_r()“, stoßen Sie auf „gelegentlich gibt es nicht“ und „was werden Sie tun, wenn es nicht verfügbar ist?“. Das sekundäre Problem ist die angenommene Plattform – die Frage bezieht sich auf C99, wo strtok_r() ist nicht verfügbar (auch nicht strtok_s() allgemein – aus TR 24731-1).

    – Jonathan Leffler

    4. Januar 2011 um 0:02 Uhr

ich würde setzen printf und scanf ziemlich weit oben auf dieser Liste. Die Tatsache, dass Sie die Formatierungsspezifizierer genau richtig erhalten müssen, macht diese Funktionen schwierig zu verwenden und extrem leicht falsch zu machen. Es ist auch sehr schwierig, Pufferüberläufe beim Auslesen von Daten zu vermeiden. Darüber hinaus hat die “String-Schwachstelle im printf-Format” wahrscheinlich unzählige Sicherheitslücken verursacht, wenn gut gemeinte Programmierer Client-spezifizierte Strings als erstes Argument für printf angeben, nur um viele Jahre später festzustellen, dass der Stack zerstört und die Sicherheit kompromittiert ist.

  • Ich wollte sagen, dass dies ein Kommentar sein sollte, keine Antwort, aber Sie können nicht ohne Wiederholung kommentieren, also hier, haben Sie welche.

    – Stefan Kanon

    3. Januar 2011 um 23:09 Uhr

  • An dem Punkt, an dem Sie sagen: “Oft gibt es strtok_r()“, stoßen Sie auf „gelegentlich gibt es nicht“ und „was werden Sie tun, wenn es nicht verfügbar ist?“. Das sekundäre Problem ist die angenommene Plattform – die Frage bezieht sich auf C99, wo strtok_r() ist nicht verfügbar (auch nicht strtok_s() allgemein – aus TR 24731-1).

    – Jonathan Leffler

    4. Januar 2011 um 0:02 Uhr

Jede der Funktionen, die den globalen Zustand manipulieren, wie z gmtime() oder localtime(). Diese Funktionen können einfach nicht sicher in mehreren Threads verwendet werden.

BEARBEITEN: rand() ist in der gleichen Kategorie, wie es scheint. Zumindest gibt es keine Garantien für Thread-Sicherheit, und auf meinem Linux-System warnt die Manpage, dass es nicht-reentrant und nicht-threadsicher ist.

  • Soweit ich weiß, die einzige konforme Möglichkeit zu machen rand Thread-sicher wäre es, es mit einem Mutex zu synchronisieren, was die Leistung ziemlich beeinträchtigen würde. Für einen bestimmten Startwert soll er immer dieselbe Folge von Pseudozufallszahlen zurückgeben, sodass die Verwendung eines Thread-lokalen Zustands diese Semantik in konformen Anwendungen brechen könnte, die ihren eigenen Mutex um Aufrufe von verwenden rand.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    5. Januar 2011 um 16:39 Uhr

  • … oder die anfänglich verwenden srand und rand nur im Haupt-Thread, dann nach der Initialisierung weiterhin in einem neu erstellten Thread verwenden, während es nie wieder im Haupt-Thread verwendet wird.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. Januar 2011 um 16:30 Uhr

1411980cookie-checkWelche Funktionen in der C-Standardbibliothek fördern häufig schlechte Praktiken? [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy