Erkennen von Speicherbeschädigungen in C und C ++

Von Arthur Hicken

24. Mai 2012

3  min lesen

Ein kurzer Blick darauf, warum C- und C ++ - Speicherbeschädigungen durch Codeinspektion so schwer zu finden sind - und wie Sie ein Tool zur Erkennung von Speicherfehlern verwenden können, um zu helfen (und Sie vor diesen nächtlichen Debugging-Sitzungen zu bewahren).

Programmierer verwenden aufgrund ihrer Leistung, Leistung und Effizienz weiterhin C- und C ++ - Programmiersprachen. Diese Sprachen neigen jedoch zu subtilen Speicherproblemen wie Speicherlecks, Pufferüberlauf, numerischem Überlauf und vielem mehr. Es ist leider nur allzu häufig, dass solche Fehler während des normalen Testens verborgen bleiben. Software mit subtilen Problemen wie Speicherbeschädigung kann auf einem Computer fehlerfrei ausgeführt werden, auf einem anderen jedoch abstürzen oder für eine bestimmte Zeit einwandfrei ausgeführt werden, um dann unerwartet abzustürzen, wenn das System längere Zeit in Betrieb war.

Diese Art der Speicherbeschädigung sowie andere häufig auftretende Fehler wie Probleme bei der Zeichenfolgenmanipulation, fehlerhafte Initialisierung und Zeigerfehler führen zu Abstürzen in der Produktion. Mit der Zunahme von eingebetteter Software in Flugzeugen, Autos, medizinischen Geräten und dem wachsenden IoT-Markt sind die Folgen fehlerhafter Software mehr als nur unglückliche Kunden geworden, sondern können lebensbedrohlich sein.

Grundlegendes zur Speicherbeschädigung

Speicherbeschädigung Fehler wie diese sind unangenehm, besonders wenn sie gut getarnt sind. Wenn sie sich manifestieren, kann es täuschend schwierig sein, sie zu reproduzieren und aufzuspüren. Betrachten Sie als Beispiel, was passieren kann, das unten gezeigte Programm.

Dieses Programm verkettet die in der Befehlszeile angegebenen Argumente und gibt die resultierende Zeichenfolge aus:

/ * * Datei: hello.c * / #include #einschließen int main (argc, argv) int argc; char * argv []; {int i; char str [16]; str [0] = '\ 0'; für (i = 0; i

Wenn Sie dieses Programm mit Ihrem normalen Compiler kompilieren und ausführen, werden Sie wahrscheinlich nichts Interessantes sehen. Zum Beispiel:

    c: \ source> cc -o hallo hallo.cc:\source> hallo du bist eingetreten: hallo c: \ source> hallo Welt du bist eingetreten: hallo Welt c: \ source> hallo grausame Welt Du bist eingetreten: hallo grausame Welt

Wenn dies der Umfang Ihrer Testverfahren wäre, würden Sie wahrscheinlich zu dem Schluss kommen, dass dieses Programm korrekt funktioniert, obwohl es einen sehr schwerwiegenden Speicherbeschädigungsfehler aufweist, der sich nur nicht durch die Erzeugung einer falschen Ausgabe manifestiert hat. Dies tritt häufig bei Speicherproblemen auf. Sie können häufig unentdeckt bleiben, da sie die Ausgabe möglicherweise nicht direkt beeinflussen und daher von normalen Komponententests oder Funktionstests nicht erkannt werden.

Diese Art von Fehler sieht einfach genug aus, wenn er sich in einem kleinen Beispielprogramm befindet, in dem er nicht übersehen wird. Wenn er jedoch in kompliziertem Code mit Hunderttausenden von Zeilen und vielen dynamischen Zuordnungen vergraben ist, kann die Erkennung bis nach der Veröffentlichung leicht vermieden werden.

Erkennung und Visualisierung von Laufzeit- und Speicherfehlern mit Parasoft Insure ++

Erkennen von Speicherfehlern

Der beste Weg, um komplexe Speicherfehler zu finden, ist die Verwendung von a Tool zur Erkennung von Speicherfehlern (oder „Laufzeit-Debugger“). Es ist einfach zu bedienen - Sie ersetzen einfach Ihren Compilernamen (cc) durch "versichern" - also

 cc -o hallo hallo.c

wird

 versichere -o hallo hallo.c

und dann führen Sie einfach das Programm aus. Wenn Sie ein gut formatiertes Makefile haben, können Sie Parasoft Insure ++ verwenden, indem Sie Ihren Compiler-Befehl so einstellen, dass Folgendes versichert wird:

 mach CC = versichere hallo

Nachdem Sie mit dem Laufzeit-Debugger kompiliert haben, können Sie den folgenden Befehl ausführen:

 Hallo grausame Welt

und es werden die unten gezeigten Fehler generiert, da die verkettete Zeichenfolge länger wird als die 16 Zeichen, die in der Deklaration in Zeile 11 zugewiesen sind:

[hello.c: 14] ** WRITE_OVERFLOW ** >> strcat (str, argv [i]); Schreiben läuft über Speicher: bbbbbbbbbbbbbbbbbbbbbbbbb | 1 | 16 | wwwwwwwwwwwwwwwwwwwwwwwwwwwww Schreiben (w): 2xbfffeed0 bis 0xbfffeee0 (1 Bytes) Blockieren (b): 18xbfffeed0 bis 0xbfffeedf (0 Bytes) str, deklariert bei hello.c, 16 Stack trace (Fehler) ) hallo.c, 11 ** Speicher beschädigt. Programm kann abstürzen !! ** [hello.c: 14] ** READ_OVERFLOW ** >> printf ("Sie haben eingegeben:% s \ n", str); Die Zeichenfolge wird nicht innerhalb des Bereichs null beendet: str Lesen: 17xbfffeed0 Vom Block: 0xbfffeed0 bis 0xbfffeedf (0 Byte) str, deklariert bei hello.c, 16 Stapelverfolgung, bei der der Fehler aufgetreten ist: main () hello.c, 11 Sie haben Folgendes eingegeben: hello grausame Welt    

Sie haben wahrscheinlich etwas Interessantes in der Ausgabe bemerkt, nämlich, dass es tatsächlich zwei Fehler gibt, die auf dieses Problem zurückzuführen sind - einer ist der Schreibüberlauf, wenn Sie versuchen, zu viele Bytes in den Zeichenfolgenpuffer einzufügen, und dann ein Leseüberlauf, wenn Sie aus der Zeichenfolge lesen Puffer. Wie Sie sehen, kann sich der Fehler an verschiedenen Stellen auf unterschiedliche Weise manifestieren. Stellen Sie sich also vor, was in einem realen Programm passieren kann. Es ist fast selbstverständlich, dass alle funktionierenden C- und C ++ - Programme Speicherlecks und andere Speicherfehler aufweisen.

Wenn Sie diese Fehler finden möchten, ohne wochenlang obskure Probleme zu verfolgen, werfen Sie einen Blick darauf Parasoft Insure ++. Es kann alle Probleme finden, die mit dem Überschreiben des Speichers oder dem Lesen über die gesetzlichen Grenzen eines Objekts hinaus zusammenhängen, unabhängig davon, ob es statisch (dh eine globale Variable), lokal auf dem Stapel, dynamisch (mit malloc oder neu) oder sogar zugeordnet ist als Shared Memory Block. Es kann sogar Situationen erkennen, in denen ein Zeiger von einem Speicherblock in einen anderen wechselt und dort den Speicher überschreibt, selbst wenn die Speicherblöcke benachbart sind. Die Erkennung von Laufzeitfehlern mit Insure ++ schützt Ihre Anwendung und hält Sie von diesen nächtlichen Debugging-Sitzungen ab.

Holen Sie sich den ultimativen Speicher-Debugger für C und C ++.
Probieren Sie jetzt Parasoft Insure ++ aus

Von Arthur Hicken

Arthur ist seit über 25 Jahren bei Parasoft im Bereich Software-Sicherheit und Testautomatisierung tätig. Er hilft bei der Erforschung neuer Methoden und Techniken (einschließlich 5 Patente) und hilft Kunden dabei, ihre Software-Praktiken zu verbessern.

Erhalten Sie die neuesten Nachrichten und Ressourcen zum Testen von Software sofort.