Frage:
Sicherer Speicher für reines C.
AviD
2011-04-20 17:01:32 UTC
view on stackexchange narkive permalink

Pufferüberläufe sind nichts Neues. Und dennoch treten sie häufig auf, insbesondere in nativem (dh nicht verwaltetem) Code ...

Ein Teil der Hauptursache ist die Verwendung "unsicherer" Funktionen, einschließlich C ++ - Heftklammern wie memcpy, strcpy, strncpy , und mehr. Diese Funktionen gelten als unsicher, da sie direkt mit nicht eingeschränkten Puffern umgehen und ohne intensive, sorgfältige Grenzüberprüfungen in der Regel alle Zielpuffer direkt überlaufen.

Microsoft über SDL hat die Verwendung dieser "unsicheren" Funktionen verboten und bietet Ersatzfunktionen für C ++ - z. strcpy_s für strcpy, memcpy_s für memcpy usw. (abhängig von der Umgebung). In den neuesten Versionen von Visual Studio können Sie dies sogar automatisch ausführen ...

Aber was ist mit "reinem" C (dh nicht C ++)?
Und insbesondere mit Nicht-MS-Plattformen - einschließlich Linux und sogar Nicht-VS-Compiler unter Windows ...
Hat jemand sicherere Ersatzfunktionen für diese? Empfohlene Problemumgehungen (abgesehen davon, dass einfach mehr Grenzen überprüft werden ...)?
Oder sind wir alle dazu verdammt, die Verwendung von memcpy weiterhin zu wiederholen?

+1 interessant, aber ich kann sehen, wie argumentativ und sonst voller Rhetorik.
@Rook wirklich? Ich lese es einen Tag später noch einmal und habe immer noch Schwierigkeiten, irgendetwas zu finden, das aus der Ferne argumentativ (außer bei den Pufferüberläufen) oder rhetorisch ist - außer dem Hinweis, dass Microsoft möglicherweise etwas Nicht-Böses getan hat ... Können Sie bitte darauf hinweisen? ? (Die Kollegen bei der SO-Frage waren zu beschäftigt damit, Anti-MS-Slogans zu rufen, um Informationen zu geben ...)
♦ Nun ja, ich meine, das war aus diesem Grund auf SO geschlossen. Der Linux-Kernel bevorzugt akut die Verwendung von "strcpy ()" für statischen Text, da dies den Overhead reduziert. Um ehrlich zu sein, bin ich nicht davon überzeugt, dass ein blindes Engagement für "sichere Funktionen" die beste Lösung ist. Ich denke, dass implizite Speicherschutzsysteme gezeigt haben, dass ein Pufferüberlauf allein wertlos ist.
@Rook es wurde auf SO aus dem Grund geschlossen, den ich erwähnte. strcpy für statischen Text unterscheidet sich von Benutzereingaben, aber die Debatte darüber, ob "sichere Funktionen" verwendet werden sollen oder nicht, ist nicht argumentativ, sondern Teil der Antwort. Wo ist also die Argumentation? Bitte sag es mir und ich werde es ändern.
♦ Es ist mir egal. Ehrlich gesagt funktionieren dort viele verschiedene Sicherheitsansätze gut und sind eifrig anderer Meinung.
@MikeSamuel `strncpy` ist ineffizient und unsicher: Es schreibt so viele '\ 0', wie noch` char` im Puffer vorhanden sind (möglicherweise 0 `\ 0`). Die anderen Funktionen können leicht missbraucht werden.
@MikeSamuel Zusätzlich zu dem, was Neugierige bemerkt haben, gibt es auch keine Bestätigung, dass der Parameter `size_t` * kleiner * als die Größe des Puffers ist. Sicher, in einer perfekten Welt kann ein vorsichtiger Überprogrammierer diese Überprüfungen selbst durchführen - aber ich habe immer noch viel zu viele Fehler gefunden, sogar einzelne Fehler, selbst von einigen der größten Programmierer da draußen, um zu glauben, dass dies ein Fehler ist Dieser sensible Bereich ist eine solche Schlussfolgerung.
AiliplxjbqCMT fair genug.
Sieben antworten:
Ninefingers
2011-04-21 01:33:37 UTC
view on stackexchange narkive permalink

Lassen Sie uns hier klar sein.

    unsicher ist eine Frage des Kontexts , nicht der "Verwendung" einer bestimmten Funktion. Wenn ich memcpy unsicher in einem Prozess verwende, der als Benutzer ausgeführt wird und wenig zu verlieren hat, keine Setuid oder solche Flags, ist das "Schlimmste", was ich tun kann, eine Shell für diesen Benutzer zu erhalten und von dort aus fortzufahren. Um eine Eskalation von Berechtigungen zu erreichen, müssen Sie etwas angreifen, das Sie täuschen können, um Ihnen höhere Berechtigungen zu gewähren.
  1. C / C ++ sind keine "gefährlichen Werkzeuge". Sie sind Werkzeuge. Fügen Sie eine entsprechende Metapher über gefährliche DIY-Geräte ein.
  2. ol>

    Tatsache ist, wie Rook bereits gesagt hat, dass C / C ++ und andere solche auf die Maschine kompilierten Sprachen einen Platz in der Welt haben. Sie dienen zum Erstellen schneller Systeme, Betriebssysteme, Systemdienste usw. Sie bieten Ihnen die Möglichkeit, Ihren eigenen Speicher nach Belieben zu verwalten. Sie haben die Kontrolle.

    Wenn Sie keine Form der automatischen Speicherverwaltung einführen, können Sie nicht wirklich herausfinden, ob Sie den zugewiesenen Speicher überschritten haben. Also, ok, lasst uns einen Container vorstellen. Jetzt muss jeder Speicherzugriffsaufruf, der jemals war, überprüft werden. Ist es in Umfang für diese bestimmte Funktion? Wie viele Objekte zeigen darauf? Wo sind sie im Geltungsbereich? Kann ich Zeiger außerhalb des Bereichs der ursprünglichen Referenz haben und wenn ja, wie verfolge ich diese? Sehr schnell haben Sie eine virtuelle Maschine und damit eine verwaltete Sprache.

    Wie in StackOverflow gezeigt wurde, ist es auch möglich, memcpy_s auf eine Art und Weise aufzurufen das ist sowieso unsicher. Es löst das grundlegende Problem nicht wirklich, sondern macht es nur etwas schwieriger, Fehler zu machen.

    Das ist der Unterschied. C / C ++ ist eine ausgefallene Assembly mit all der Leistung, die Sie für alles benötigen. Java / Python usw. schützen Sie davor zu einem Preis: Geschwindigkeit und Leistung.

    Sie haben im Laufe des heutigen Tages wiederholt gesagt (ich habe auch die SO-Version befolgt), dass Sie immer noch keine Antwort darauf haben, wie Sie sich mit C / C ++ auf anderen Systemen wie Linux sicher entwickeln. Zunächst einmal habe ich mit VS2005 / 2008 gewöhnlich CRT_SECURE_NO_DEPRECATE festgelegt, aber trotzdem können Sie Folgendes tun:

    1. Sie fragen nach StackOverflow. Sie nehmen zur Kenntnis, lernen, lesen und lesen erneut.
    2. Ihr Compiler ist (meistens) Ihr Freund. Hör es dir an. Verwenden Sie Warnungen. Verwandle sie in Fehler. Also cl / W4 und gcc -Wall -Werror -pedantic -std = c99 . Ja, es ist OTT in Bezug auf Fehlermeldungen. Aber wenn Sie nicht jeden einzelnen erklären und begründen können, warum Sie ihn ignorieren, verstehen Sie Ihren eigenen Code nicht.
    3. Überprüfen Sie Ihre Speicherzuordnung. Der Standardaufruf von valgrind überprüft Ihre Zuordnungen und Freigaben, damit Sie keinen Speicher verlieren. Wenn Sie Speicherlecks haben, haben Sie nicht genug über Ihren Code nachgedacht. Dies ist ein gutes Zeichen dafür, dass Sie Fehler nacheinander, ungültige Grenzwerte usw. usw. haben.
    4. Verwenden Sie erneut valgrind . Ich habe das gerade aufgegriffen, aber schauen Sie, es gibt einen experimentellen Über- / Unterlaufprüfer in Valgrind. Hier erfahren Sie, ob Sie beispielsweise am Ende des Stapels abstürzen (möglich, da dort die meisten lokalen Variablen zugewiesen sind) oder außerhalb des Heaps brk () .
    5. Verwenden Sie Schiene , auch als sichere Flusen bezeichnet, und notieren Sie sich deren Ausgabe. Wenn Sie keine Ausgabe erklären können, verstehen Sie Ihren Code nicht.
    6. Verdauen Sie den C Secure Coding Standard nach innen. Hier besonders relevant:

      • STR31-C. Stellen Sie sicher, dass der Speicher für Zeichenfolgen ausreichend Platz für Zeichendaten und den Nullterminator bietet.

        Das Kopieren von Daten in einen Puffer, der nicht groß genug ist, um diese Daten aufzunehmen, führt zu einem Pufferüberlauf. Obwohl dies nicht auf nullterminierte Byte-Strings (NTBS) beschränkt ist, treten bei der Bearbeitung von NTBS-Daten häufig Pufferüberläufe auf. Um solche Fehler zu vermeiden, beschränken Sie Kopien entweder durch Abschneiden oder stellen Sie vorzugsweise sicher, dass das Ziel ausreichend groß ist, um die zu kopierenden Zeichendaten und das Nullterminierungszeichen aufzunehmen.

      • STR35-C. Kopieren Sie keine Daten von einer unbegrenzten Quelle in ein Array mit fester Länge

        Funktionen, die unbegrenzte Kopien ausführen, erfordern häufig, dass externe Eingaben eine angemessene Größe haben. Solche Annahmen können sich als falsch erweisen und einen Pufferüberlauf verursachen. Aus diesem Grund muss bei der Verwendung von Funktionen, die möglicherweise unbegrenzte Kopien ausführen, Vorsicht walten lassen.

        Splint bietet Ihnen eine Anleitung ähnlich der von CERT. Beachten Sie, dass memcpy im ersten Beispiel von STR35-C als kompatible Lösung betrachtet wird.

    7. Wenn Sie C ++ verwenden, verwenden Sie std :: string und boost :: shared_ptr (und verwandte; verwenden Sie das entsprechende). Es gibt absolut kein Argument dafür, malloc'ing und memcpy Zeichenfolgen in C ++ zu speichern, außer für die Interaktion mit C. Selbst dann string.c_str () Bitte überlassen Sie die Manipulationen C ++.
    8. Verknüpfen Sie nach Möglichkeit dynamisch. Wenn Sie einen Code von Drittanbietern statisch mit Ihrer App verknüpft haben und dies ein Sicherheitsproblem darstellt, haben Sie es auch und müssen Ihr Bild ebenfalls neu verteilen. Freigegebene Objekte sind im Allgemeinen nicht nur bequemer, Sie erhalten auch standardmäßig Sicherheitsupdates. Ich weiß, dass es Fälle gibt, in denen Sie dies nicht können. Deshalb sage ich "wo möglich".
    9. Bauen Sie dies in Ihren Entwicklungszyklus ein.
    10. Ich hoffe, Sie haben nichts verpasst.
    11. Bleiben Sie über relevante Aktualisierungen der Sicherheitsgemeinschaft auf dem Laufenden.
    12. Sei nett zur Sicherheitsgemeinschaft. Bestätigen Sie die Sicherheitslücken und ergreifen Sie Maßnahmen, um sie zu beheben. Jeder Code hat (oder hatte sie) Fehler, wenn es sich lohnt, sie auszuführen. So einfach ist das.
    13. ol>

      Letztendlich glaube ich nicht, dass das Problem durch "sichere" Funktionen gelöst wird. Ich denke, das Problem wird durch die Verwendung einiger anständiger Tools, einen ordnungsgemäßen Entwicklungsprozess, der schlechten Code aus kritischen Versionen (späte Betas und Release-Kandidaten) ablehnt, und schließlich durch ein Bewusstsein für bewährte Verfahren / aktuelle Probleme gelöst.

      Schließlich ist memcpy_s nicht Teil des C99-Standards. Es ist eine Erweiterung der C-Bibliothek (wie in keinem Teil des Kerns) und daher nicht garantiert auf der Plattform, die ich verwende. memcpy ist. Für ein Softwareprojekt, das plattformübergreifend kompiliert werden muss, ist dies wahrscheinlich der entscheidende Faktor für die zu verwendende Funktion.

Eine solide Antwort mit null argumentativem Inhalt! +1 von mir
So sehr ich die 'Good Job'-Kommentare nicht mag ... Sehr gut ausgedrückt.
Insgesamt allgemein gute Ratschläge ... Aber speziell für die Frage gibt es hier ein paar gute Punkte: "... macht es etwas schwieriger, Fehler zu machen", ja, das ist der Punkt ... und das bin ich (und die anderen Sicherheitsprofis) versuchen zu erreichen. Keine Silberkugel. Ich versuche nur, STR31 / 35 durchzusetzen und zu validieren ...
Der C ++ - Kommentar zu `std: string` ist passend, aber das ist hier leider nicht der Fall ...
Ich möchte auch 2 weitere Punkte für `memcpy_s` ansprechen: Obwohl es nicht Teil von C99 ist, * ist * es Teil von ISO / IEC TR 24731. Noch wichtiger ist, dass memcpy_s (oder ähnliches) nur ein weiteres" anständiges Werkzeug "ist hilft beim "Bewusstsein für bewährte Verfahren" und ermöglicht es mir (dem Sicherheitsprüfer), den "schlechten Code" besser zu validieren. Ich * weiß *, dass es nicht auf der Plattform ist, die ich verwende, deshalb frage ich, da es keinen Grund gibt, es plattformübergreifend zu kompilieren.
@AviD Es ist vereinbart. Lassen Sie mich jedoch einen Zusammenhang hinzufügen, warum die Leute Microsoft als etwas ... aggressiv betrachten. MSVC10 unterstützt nicht einmal alle C99; Insbesondere unterstützen sie "stdint" -Funktionen, aber "stdbool" fehlt, ebenso wie "inline", das einige dieser schrecklichen Makros ersetzen könnte. Ein weiteres Feature, das in MSVC 64-Bit fehlt, ist die Inline-Assembly, die ihre 32-Bit-Compiler meiner Meinung nach schon seit einiger Zeit haben.
@AviD Sie wissen wahrscheinlich auch, dass gcc eine Reihe von Erweiterungen für C unterstützt, wie berechnete gotos, `__attribute__` Dekorateure und andere. Dann gibt es Intel Compiler und LLVM. Es ist harte Arbeit, Software für all diese Zwecke zum Laufen zu bringen. Der gemeinsame Nenner besteht darin, nur zu versuchen, einen Standard wie C89 zu unterstützen, da C99 in einem der grundlegenden Ziele fehlt.
Auch wenn memcpy_s weit verbreitet wäre, würde ich dennoch raten, was ich oben vorschlage. Die Existenz von "sicheren" Funktionen allein wird das Problem der schlechten Codierer nicht beseitigen, insbesondere wenn einem Freund von mir beigebracht wurde, "char" -Typen zu verwenden, um Arrays an einer der führenden Universitäten in Großbritannien zu indizieren. Ich denke, ich möchte sagen, dass Sie großartige Programmierer einstellen sollten, die wissen, was sie tun, aber die meisten Unternehmen haben kein Ass-Team von C-Entwicklern, daher denke ich, dass es für Qualitätskontrollzwecke keine so schreckliche Idee ist als Kodierungsstandard durchsetzen.
@Ninefingers, Ich verstehe aus Ihren Kommentaren, dass es einige Anti-MS-Gefühle gibt. Allerdings ist mir das auf die eine oder andere Weise wirklich egal. Es hat keinen Einfluss auf die Frage, ob es sich um einen "sichereren Ersatz für memcpy" handelt (der sich immer wieder als wahrscheinlich gefährlich erwiesen hat, dh wahrscheinlich auf unsichere Weise verwendet wird). Als Beispiel erwähnte ich das bekannte Bandaid, das MS implementierte (was übrigens nicht ihre "Erfindung" war).
Zwei praktische Punkte: Cross-Compilation ist hier kein Thema, ich brauche keinen gemeinsamen Nenner (wenn ich das tun würde, würde ich wahrscheinlich Java in Betracht ziehen ...;)). Zweitens, wie Sie am Ende Ihres letzten Kommentars erwähnen, sind die meisten Programmierer * keine * großartigen Programmierer, und genau deshalb suche ich danach. Ich würde auch darauf hinweisen, dass dies für schlechte Programmierer nicht hilfreich wäre, sie können alles durcheinander bringen ... es ist für die im Grunde GUTEN (aber nicht übergroßen) Programmierer, die das Richtige tun, wenn sie darauf hingewiesen werden - es ist für * sie * dass memcpy_s (oder ähnliches) immens helfen würde.
@AviD Ich war nicht sarkastisch. "Wie Sie wahrscheinlich wissen" war eine Bestätigung, dass Sie wahrscheinlich die verschiedenen zusätzlichen Funktionen verschiedener Compiler verstehen. Ich habe keine Anti-MS-Stimmung und was ich sage, ist Tatsache - versuchen Sie, ".c" -Dateien mit "Inline" -Präfixfunktionen zu kompilieren, und es funktioniert nicht mit MSVC, was aus meiner Sicht eine Schande ist weil ich sonst Visual Studio mag. Außerdem habe ich in meinem letzten Satz Ihrem Standpunkt zu Good / Supergreat-Programmierern praktisch zugestimmt.
user185
2011-04-21 16:56:56 UTC
view on stackexchange narkive permalink

Und insbesondere, was ist mit Nicht-MS-Plattformen - einschließlich Linux- und sogar Nicht-VS-Compilern unter Windows ...

Es gibt ein plattformübergreifendes Open-Source-Projekt namens CoreFoundation Lite als Teil von Apples Core OS, das C-Typen für die sichere Bearbeitung von Byteblöcken, Zeichenfolgen und anderen grundlegenden Datentypen bereitstellt. Relevant für diese Diskussion implementieren sie Typprüfung, Grenzwertprüfung, Speicherverwaltung und unterscheiden zwischen veränderlichen und unveränderlichen Objekten.

Empfohlene Problemumgehungen (außer einfach mehr Grenzwertprüfung durchzuführen ...)?

Microsoft unsafe.h bietet übrigens GCC-Unterstützung. Auf diese Weise verwenden Sie das gift -Pragma von GCC, um einen Fehler zu verursachen, wenn Sie eine unsichere Funktion verwenden. Ich habe darüber zusätzlich in einem Buch geschrieben, das ich geschrieben habe (Haftungsausschluss: Ich habe es geschrieben).

Oder sind wir alle dazu verdammt, unsere Verwendung von memcpy weiterhin zu wiederholen?

Grundsätzlich lautet die Lösung "Tu das nicht". Sogenannte sichere Implementierungen können Sie daran hindern, über Speicher zu kritzeln, der nicht Ihnen gehört, aber dennoch viele "Missbrauchsfälle" offen lassen. Die Überprüfung von Grenzen ist nur ein kleiner Teil des Problems. Nehmen wir zum Beispiel an, Sie malloc (1024) und beschließen, einen Zeiger auf Byte 56 zu erstellen und diesen als Referenz auf eine bestimmte Struktur zu behandeln, die 16 Byte lang ist. Wenn Sie dann ("sicher") 58 Bytes auf den ursprünglichen Zeiger kopieren, ist der Kopiervorgang erfolgreich, aber Ihre Struktur ist fehlerhaft. Können Sie den Bruch erkennen? Vielleicht nicht: Es könnte immer noch wie eine gültige Struktur aussehen (insbesondere wenn Sie eine magische Zahl am Ende setzen oder keine magische Zahl haben).

Die Probleme " Stack Smashing "und" Heap Corruption "sind eigentlich Sonderfälle des Problems" Speicher als großen, untypisierten Beutel mit Bytes behandeln ". Einige andere Beispiele für dieses Problem sind:

  • STR30-C. Versuchen Sie nicht, Zeichenfolgenliterale zu ändern Während veränderbare und unveränderliche C-Zeichenfolgen aus der Perspektive "Big Bag of Bytes" gleich aussehen, führt der Versuch, ein Zeichenfolgenliteral an Ort und Stelle zu bearbeiten, zu undefiniertem Verhalten. Der Code kann jedoch nicht erkennen, ob ein char * ein Zeichenarray oder ein Zeichenfolgenliteral darstellt. Daher muss der gesamte Aufwand für die Sicherstellung des korrekten Verhaltens durch Aufrufen von Code behoben werden.
  • MEM01-C. Speichern Sie einen neuen Wert in Zeigern unmittelbar nach free () , da es möglich ist, auf einen Speicherblock zu verweisen, der nicht mehr Ihnen gehört, und im Allgemeinen ist es schwer zu sagen, ob er gültig ist oder nicht nicht.
  • MEM05-C. Vermeiden Sie große Stapelzuweisungen Diese ist besonders witzig, wenn Sie mit jemandem zu tun haben, der behauptet, Sie müssten nur Ihre Grenzen korrigieren, und der Rest fällt zusammen. Mit diesem Problem können Sie vollständig intern konsistenten und korrekten Code haben, der den Stapel dennoch zerschmettert.

Die Liste wird fortgesetzt: Siehe den Rest des CERT C Secure Coding Standard.

+1 für die erste Hälfte (obwohl mir das nicht wirklich hilft - es sei denn, es gibt einen Port für Linux?) Und eine weitere +1 für die zweite Hälfte - ich stimme vollkommen zu. Ich habe die "Gewerkschafts" -Form immer gehasst - sie passt einfach nicht gut zu mir. Aber natürlich sagen Sie dies jedem erfahrenen C-Programmierer, und er springt Ihnen mit beiden Händen um die Luftröhre in den Hals: "Sie verstehen die C-Philosophie einfach nicht, das ist die richtige Vorgehensweise und die einzige unsichere Die Sache ist, weil du nicht weißt, was du tust. "
Ich kann Ihnen mit arroganten C-Nackenbärten nicht helfen, aber ich kann Ihnen sagen, dass CF Lite neben Mac OS X und iOS auch unter * nix und Windows funktioniert.
Das ist natürlich einer der Vorteile der Arbeit in C und des Speichers auf niedriger Ebene im Allgemeinen - keine Einschränkung. Viel Seil ...
Kann ich hier einwerfen und sagen, dass das "Vereinigungs" -Formular nützlich ist, um die Endianität der Plattform zu erkennen? Es ist auch nützlich, um Bitfelder aufzubrechen, beispielsweise könnte ein 64-Bit-Feld durch eine Vereinigung von zwei 32-Bit-Feldern dargestellt werden. Zugegeben, ich persönlich würde Bitshift und logisches Ands verwenden, um dies zu extrahieren, da diese Konstrukte endianunabhängig sind, aber manchmal nützlich.
Ich bin auch nicht der Meinung, dass die Lösung "Tu das nicht" lautet. Das Problem besteht nicht darin, Speicher als Bytesack zu behandeln, da dies der Fall ist. Das Problem ist die Eingabevalidierung oder ein Mangel daran. "Ich werde nie die Länge davon überprüfen müssen ..." usw. Seien wir ehrlich, C-Entwickler sind nicht die einzigen ... SQL Injection fällt mir zum Beispiel ein.
@ninefingers, wie Sie sehen können, sind wir uns nicht einig. Die Eingabevalidierung ist eine kleine Teilmenge der Probleme, die auftreten, wenn keine niederohmigen Abstraktionen über dem Speicher verwendet werden.
@AviD: jemand (ich vergesse wer, vielleicht Alan Cox) sagte C ++ gibt dir genug Seil "um dich in den Fuß zu schießen" ;-)
@Graham, Ich weiß, darauf habe ich mich mit meinem Kommentar "Viel Seil" bezogen: D.
@Graham Ich bin fasziniert. Ich gebe zu, ich bin kein Experte für Hardware, ein Bereich, den ich verbessern möchte. Haben Sie Links / relevante Artikel dazu? Ich habe ein hohes Verständnis von Von Neumann vs Harvard Architecture, aber hier endet mein Wissen, wie es ist. Ich werde mich darauf beschränken, Ihnen zuzustimmen oder nicht zuzustimmen, bis ich verstanden habe, was Sie vollständig meinen. Alternativ, wenn es vielleicht besser zu einer Frage gemacht wird, kann ich das auch tun.
Ich habe einige Beispiele in die Antwort aufgenommen, es gibt noch viel mehr.
Thomas Pornin
2011-04-20 18:10:20 UTC
view on stackexchange narkive permalink

Die umfassende Antwort lautet: Verwenden Sie C (oder C ++) nicht mehr! Es ist ein archaisches und geradezu gefährliches Werkzeug. Wechseln Sie zu einer Sprache mit integrierter Sicherheit (Java, C #, Schema, OCaml, Python ... die Auswahl ist groß).

Ein Pufferüberlauf ist ein Fehler unter den generischen Fehlerklassen, die als "the" bekannt sind Der Programmierer weiß nicht genau, was er tut. " Eine Sprache mit Grenzüberprüfung (oder sogar die Verwendung von memcpy_s () ) entfernt solche Fehler nicht. Dies führt nur zu weniger schwerwiegenden Konsequenzen (z. B. wird eine Ausnahme ausgelöst, die normalerweise eine sofortige Thread-Beendigung impliziert). Es ist wie ein Sicherheitsgurt: Ein Sicherheitsgurt verhindert keine Autounfälle, sondern versucht nur, Sie am Leben zu erhalten, während Ihr Auto in Vergessenheit gerät. Sie sollten daher gebundene Schecks (insbesondere in Sprachen, in denen solche Schecks auf transparente Weise enthalten sind) aus demselben Grund verwenden, aus dem Sie immer den Sicherheitsgurt anlegen sollten.

Wie bei C: memcpy_s () und sein ilk sind tatsächlich in ISO / IEC TR 24731 (einem Standard aus dem Jahr 2005) definiert, sodass sie irgendwann in C- und C ++ - Compiler versickern sollen. Aber es scheint, dass derzeit nur Microsoft sie vorantreibt. Für die "String" -Funktionen ( strcpy () und strcat () ) gibt es Standardfunktionen (C99) ( strncpy () und strncat () ) und einige BSD-Betriebssysteme (hauptsächlich OpenBSD) verwenden Varianten mit einer etwas benutzerfreundlicheren API ( strlcpy () und strlcat () ). Keine dieser Funktionen behebt jedoch den Fehler, dh der Programmierer versucht, Daten in einen zu kleinen Puffer zu legen. Sie machen den Programmierer lediglich auf die Tatsache aufmerksam, dass sein Puffer möglicherweise eine unzureichende Größe hat.

+1, ich liebe es, von jemandem wie Ihnen einen gesunden Menschenverstand zu hören (siehe einige der lächerlichen Rhetoriken, die in der verknüpften SO-Frage vor sich gehen ...). Leider ist es unwahrscheinlich, dass C / C ++ nicht mehr verwendet wird, obwohl ich gerne alle dazu bringen würde, dies zu tun ...
"strncpy" und dergleichen gelten immer noch als unsicher, wenn auch etwas besser als "strcpy". Das Problem ist das gleiche wie bei memcpy - Sie definieren nur, wie viele Bytes kopiert werden sollen, aber das hat keine Beziehung zum Zielpuffer ... Gut zu wissen über das ISO-Bit ... Danke!
Was ist los mit `strncpy`? Wenn die Idee darin besteht, die Größe der Puffer zu steuern, und "strncpy" dies angeblich tut, wie würden Sie es ausnutzen?
@Marcin, als eines der Beispiele - http://cwe.mitre.org/data/definitions/193.html
@Marcin strncpy steuert nur die Größe des * Quellpuffers *, nicht des * Zielpuffers. Gleich wie beim Original-Memcpy ...
Wow, was haben sie dann wirklich verbessert? :) Gutes Zeug, danke.
@Marcin MS bietet zusätzliche "sicherere" Versionen dieser Methoden - z. unter anderem strcpy_s, strncpy_s - die dasselbe tun wie memcpy_s, d. h. den Zielpuffer gegen ein zusätzliches Argument prüfen. Auch hier kann ein Programmierer falsche Werte angeben, aber gute Programmierer müssten über die Größe des Zielpuffers nachdenken.
@Thomas, re ISO / IEC - Ich erinnere mich, dass ich irgendwo gelesen habe (ich kann es jetzt nicht finden), dass die gcc / linux-Implementierer offiziell erklärt haben, dass sie memcpy_s oder ähnliches nicht implementieren würden ... Aber natürlich suche ich immer noch nach einem Äquivalent oder eine Problemumgehung ...
@Thomas Pornin Dies ist ein ziemlich entzündlicher Beitrag. Ob es Ihnen gefällt oder nicht (Java, C #, Scheme, OCaml, Python ...) sind alle ** in C oder C ++ ** geschrieben. Sie müssen die Sprache auswählen, die für das Problem am besten geeignet ist, und C / C ++ kann eine gute Wahl sein. Davon abgesehen liebe ich es, Python zu verwenden, wenn es angebracht ist.
@Rook: und C und C ++ werden von einem Compiler in Maschinensprache umgewandelt, und die Maschinensprache läuft auf einer CPU, die hauptsächlich aus Silizium und verschiedenen Metallen besteht, die irgendwo aus einer Mine stammen. Dies bedeutet nicht, dass das Programmieren nichts anderes als verherrlichtes Schaufeln ist.
@Thomas Pornin Mein Punkt war zweifach, was Sie alle nicht angesprochen haben und mich stattdessen nur beleidigt haben. Sie würden keine VM in Python schreiben, sondern in C / C ++. Außerdem sehe ich nur 0 Tage im Flash Player.
@Rook, tut mir leid, Sie aufzuklären, aber @Thomas ist ein anerkannter Kryptograf.
Meine Herren, bitte halten Sie Ihre Kommentare höflich und relevant!
"Verwenden Sie C / C ++ nicht mehr" ist keine nützliche Antwort. Die Sprachen sind unter bestimmten Umständen das richtige Werkzeug für den Job (mem / cpu-effizienzsensitive Anwendungen oder Umstände, in denen die Verteilung eines VM / Frameworks nicht erforderlich ist).
@TobyS: Die detaillierte Antwort lautet, dass es nicht viele Jobs gibt, bei denen C das "richtige Werkzeug" ist - viel weniger als normalerweise angenommen. Schauen Sie sich als Beispiel http://jikesrvm.org/ an: Dies ist eine in Java geschriebene Java-VM. Die C-Programmierung steht kurz vor der Montage: Sie haben die Kontrolle, aber es gibt keine andere Kontrolle. Wenn ein Programmierer den Drang verspürt, "memcpy ()" zu verbieten, und sich auf automatisch angewendete "memcpy_s ()" verlässt, möchte er nicht wirklich in C programmieren - also zu sagen, dass er es nicht wirklich strecken sollte.
@TobyS:, daher denke ich, dass "C / C ++ nicht mehr verwenden" immer noch eine effektive, wenn auch knappe Antwort ist. Fühlen Sie sich frei, es mit einem impliziten "(es sei denn, Sie wissen, was Sie tun)" zu lesen. Ich kann hinzufügen, dass das Schreiben einer eigenen VM in einem eingebetteten System, das nur eine Teilmenge von C kennt, immer noch eine bessere Option ist als der Versuch, eine komplexe Verarbeitung in C durchzuführen, insbesondere in Bezug auf die Sicherheit.
Die Funktion "strncpy" ist kein "begrenzter strcpy"; Der Zweck besteht darin, eine nullterminierte Zeichenfolge in eine nullterfüllte Zeichenfolge umzuwandeln. Ich bin mir nicht sicher, wofür "strncat" ist, da es nur in Fällen nützlich wäre, in denen man weder wusste, wie lang die Quellzeichenfolge war, noch sich darum kümmerte, wie lange es danach dauern würde, aber irgendwie wusste, dass es eine bestimmte Menge von gab Leerzeichen nach dem Ende der Quellzeichenfolge. Ich kann mir keine Situation vorstellen, in der alle Bedingungen zutreffen würden, und wenn dies nicht der Fall wäre, wäre es besser, die Länge der Zeichenfolgen zu berechnen, "memcpy" zu verwenden und eine abschließende Null zu schreiben.
rook
2011-04-20 21:40:53 UTC
view on stackexchange narkive permalink

Der Linux-Kernel ruft viele tausend sogenannte unsichere Funktionen wie strcpy () und strcat () auf, der Puffer läuft jedoch über Diese Codebasis ist sehr ungewöhnlich. Tatsächlich werden "unsichere Funktionen" zum Kopieren von statischem Text bevorzugt, da dies den Overhead verringert. Sie können "sichere Funktionsaufrufe" auf unsichere Weise verwenden: strncpy (dest, source, strlen (source)); . Die Auswahl der Funktionen hat keinen Einfluss darauf, wie sicher das System ist.

Dieses Problem lässt sich mit modernen Speicherschutzsystemen besser lösen. Diese sind implizit und der Code muss nicht neu geschrieben werden. In moderner Software mit Kanarienvögeln sind ASLR- und NX-Zonen Pufferüberläufe allein nicht sehr hilfreich. Dangling-Zeiger wurden in 2010 pwn2own für Windows 7 und IE8 und der letzte Flash-0-Tag überhaupt nicht für einen Pufferüberlauf verwendet.

Ich hoffe, niemand verwendet jemals "strncpy (dest, source, strlen (source))" in seinem Code. Es ist funktional äquivalent zu strcpy.
@Yuliy Sie wären überrascht, ich habe es aus erster Hand gesehen. Es kommt häufiger vor, wenn Sie "strcpy ()" nicht zulassen und ein Programmierer nur finnisch sprechen und nach Hause gehen möchte.
frankodwyer
2011-04-21 01:36:03 UTC
view on stackexchange narkive permalink

Aber was ist mit "reinem" C (dh nicht C ++)?

Ich glaube nicht, dass Funktionen wie memcpy_s wirklich als "sicherer" Ersatz für memcpy gelten. Aber so oder so sind sie gleichermaßen in reinem 'C' anwendbar, es gibt nicht viel, was 'C ++' an ihnen ist. Sie sind auch nicht plattformspezifisch (in dem Sinne, dass es nicht allzu schwierig ist, eine tragbare Version von ihnen zu schreiben, auch wenn Sie sich nicht darauf verlassen können, dass sie Teil der Plattform-C-Bibliothek sind).

As Aus Sicherheitsgründen sind memcpy & co so definiert , dass sie willkürliches Kritzeln im Speicher ermöglichen. Dafür sind sie gedacht, und das Hinzufügen von Größenparametern wird dies nicht ändern.

Gleichzeitig halte ich es (noch) nicht für sinnvoll, "Nicht 'C' / C ++ verwenden" zu sagen, da es immer noch viele Systeme gibt, in denen eine 'C' -ähnliche Sprache die Sprache ist einzige praktische Option.

Die eigentliche Frage ist wohl, ob es eine sicherere Alternative zum Puffermanagement gibt, die eher ein Sicherheitsnetz als memcpy hat und von C und C ++ aus verwendet werden kann. Ja sicher. Eine 'C'-API oder C ++ - Klasse könnte definiert werden, um Grenzen überprüfte Puffer und Arrays zuzuweisen, zu verwalten und darauf zuzugreifen. Genau darauf greift der Interpreter für Java VM et al. Zu.

Dort Es gibt auch Sprachen wie Objective-C, die noch keinen verwalteten Code haben und die einen Großteil der Sicherheitsaspekte des verwalteten Codes aufweisen, obwohl memcpy usw. verfügbar sind. Dies liegt daran, dass das Kakao-Framework dazu neigt, sicherere Alternativen zu fördern und zumindest zuzulassen.

Genau, ich stimme zu. +1
user185
2011-04-25 00:09:50 UTC
view on stackexchange narkive permalink

Eine weitere 'sichere' API für die Behandlung von Zeichenfolgen in C ist Daniel J Bernsteins Zeichenfolgenbibliothek. Dank an André Pang, der mich darauf hingewiesen hat.

Dieser Link funktioniert bei mir nicht. Vielleicht ist es näher an [a] (http://www.skarnet.org/software/skalibs/libstddjb/) oder [b] (http://cr.yp.to/software.html) gerückt?
Oder beziehen Sie sich vielleicht auf Bernsteins Format [netstring] (http://en.wikipedia.org/wiki/netstring)?
anonymous
2011-04-20 18:40:48 UTC
view on stackexchange narkive permalink

Menschen werden niemals aufhören, Fehler zu machen. Selbst in sogenannten "sicheren Implementierungen" und einer Liste von Sprachen, die Thomas Pornin erwähnt hat, wird es einen Platz für eine gewisse Verwundbarkeit geben. In der aktuellen Antwort möchte ich nicht auf die Besonderheiten von C / C ++ eingehen - es wird als Ergänzung zu dem, was bereits gesagt wurde, nicht viel Sinn machen. An dieser Stelle möchte darauf hingewiesen werden, dass eine andere Lösung nicht darin besteht, Fehler selbst zu verhindern, sondern die Folgen der Ausbeutung zu mindern. Das ist nur ein weiterer Teil dessen, was aus Sicherheitsgründen getan werden kann. Schauen Sie sich als bestes Beispiel den Browser von Google Chrome an - er hat Sandboxing implementiert. Eine weitere Möglichkeit sind Compiler-Sicherheitsoptionen und Ausnutzungsminderungen auf Betriebssystemebene. Dies sind die Konsequenzen dessen, was in einem allerersten Satz gesagt wurde, und eine Bestätigung der Tatsache, dass die Einführung von Fehlern ein unvermeidlicher Bestandteil jeder Softwareentwicklung ist.

Stimmt - aber ich habe die spezifische Frage gestellt, wie man dieses spezifische Bit verbessern kann. Danke für die Antwort.


Diese Fragen und Antworten wurden automatisch aus der englischen Sprache übersetzt.Der ursprüngliche Inhalt ist auf stackexchange verfügbar. Wir danken ihm für die cc by-sa 3.0-Lizenz, unter der er vertrieben wird.
Loading...