2010-07-20 8 views
20

Also hier glaube ich, dass ich ein kleines Pufferüberlaufproblem habe, das ich fand, wenn ich den Code einer anderen Person prüfe. Es erschien mir sofort als falsch und potentiell gefährlich, aber zugegebenermaßen konnte ich die tatsächlichen Folgen dieses "Fehlers", wenn überhaupt, nicht erklären.Konsequenzen dieses Pufferüberlaufs?

Ich hatte eine Test-App geschrieben, um den Fehler zu demonstrieren, fand aber (zu meiner Bestürzung), dass es unabhängig vom Überlauf korrekt zu laufen scheint. Ich möchte glauben, dass dies nur zufällig ist, aber wollte ein Feedback, um festzustellen, ob mein Denken falsch war, oder ob es wirklich ein Problem gibt, das in meiner Test-App einfach nicht seinen Kopf zeigt.

Das Problem Code (Ich denke, es ist jedenfalls):

char* buffer = new char[strlen("This string is 27 char long" + 1)]; 
sprintf(buffer, "This string is 27 char long"); 

Nun stand der Grund, dies für mich und ich möchte es Flag als ein möglicher Pufferüberlauf aufgrund der ersten strlen ist. Aufgrund der Zeigerarithmetik führt die "falsche" Platzierung der + 1 dazu, dass strlen26 anstelle von 27 zurückgibt (die Länge von "seine Zeichenfolge ist 27 Zeichen lang"). sprintf, glaube ich, dann druckt 27 Char in den Puffer und hat einen Pufferüberlauf verursacht.

Ist das eine korrekte Einschätzung?

Ich schrieb eine Test-App, um dies für die Person zu demonstrieren, deren Code ich betrachtete, und stellte fest, dass die Zeichenfolge auch im Debugger korrekt gedruckt wird. Ich habe auch versucht, andere Variablen auf den Stack und Heap vor und nach diesem Code zu setzen, um zu sehen, ob ich benachbarte Speicherbereiche beeinflussen könnte, aber immer noch korrekte Ausgabe erhielt. Mir ist klar, dass mein neu zugewiesener Heap-Speicher möglicherweise nicht angrenzend ist, was den Mangel an nützlichem Überlauf erklären würde, aber ich wollte nur wirklich mit anderen Meinungen bestätigen, wenn dies tatsächlich ein Problem ist.

Da dies eine ziemlich einfache "Frage" ist, wäre es nett, wenn Sie Ihre Antwort mit einer Art Referenz auch unterstützen könnten. Während ich Ihre Eingabe wertschätze und begrüße, werde ich nicht "Ja, es ist" als endgültige Antwort akzeptieren. Vielen Dank im Voraus.




Update: Viele gute Antworten mit vielen besseren Einblick. Leider kann ich sie nicht alle akzeptieren. Danke, dass Sie Ihr Wissen geteilt haben und dass Sie meine "zweite Meinung" haben. Ich schätze die Hilfe.

+4

Sie werden möglicherweise nicht mit dem obigen Code aufgrund von Padding/Alignment gebissen. Könnten Sie Ihre Experimente mit einer Zeichenfolge wiederholen, die z. B. 64 Zeichen lang ist, so dass die Zuweisung 65 Zeichen lang sein müsste? Und ordnen Sie zwei solche Zeichenfolgen vor dem 'Sprintf' zu und füllen Sie sie in verschiedenen Ordnungen. –

+4

Das ist ziemlich böser Code, um eine rohe Zeichenkette zu nehmen und +1 hinzuzufügen! Ich würde den Code-Review nur wegen dieser Tatsache durchgehen. –

+1

Und deshalb verwenden wir Entwickler so viele gut getestete Bibliotheken wie wir können ... weil wir so dumme Fehler machen! :-) @Johnson Ich bin mir ziemlich sicher, dass der Entwickler 1 zur Länge hinzufügen wollte, nicht die Saite selbst, daher der Bug. – corsiKa

Antwort

14

Ihre Einschätzung ist korrekt. [edit] mit dem Zusatz der von James Curran erwähnten Korrektur. [/ Redigieren Sie]

Wahrscheinlich zeigte Ihre Test-APP das Problem nicht, weil die Zuteilung zum folgenden Vielfachen von 4, 8 oder 16 aufgerundet wird (Dies sind gemeinsame Zuordnungsgranularitäten).

Dies bedeutet, dass Sie mit einer Zeichenfolge mit 31 Zeichen demonstrieren können.

Alternativ können Sie einen nativen Speicherprofiler "Instrumentierung" verwenden, der Schutzbytes in der Nähe einer solchen Zuweisung platzieren kann.

6

Du Einschätzung richtig ist, mit der Ausnahme, dass der springf 28 Zeichen in dem Puffer gestellt wird am Ende des End-of-string NUL Zählen (Das ist, warum Sie die fehl am Platz benötigt „+1“ in erster Linie)

Beachten Sie, dass in einigen Fällen, wenn etwas außerhalb eines Debuggers fehlschlägt, aber 100% der Zeit im Debugger ausgeführt wird, ein lokaler Puffer überschritten wurde. Debugger schieben viel mehr auf den Stack, so dass es weniger wahrscheinlich ist, dass etwas Wichtiges überschrieben wird.

+0

Vielen Dank für die Korrektur auf dem '\ 0', ich bin mir nicht sicher, warum es mir in den Sinn kam :) – KevenK

1

Ja, Sie haben Recht. Der zugewiesene Puffer ist 2 Byte zu klein, um die Zeichenfolge zu speichern.

Da dies auf dem Heap zugewiesen wird, könnte dies zu einer Heap-Beschädigung führen. Die Wahrscheinlichkeit hängt jedoch davon ab, welche anderen Zuordnungen und Speicherfreigaben vor diesem Zeitpunkt aufgetreten sind und auch, ob der Heap-Manager verwendet wird. Siehe Heap Overflow für mehr.

3

Das Problem ist, dass Sie irgendwo im Speicher schreiben, aber nicht auf dem Stapel. Daher ist es schwierig zu sehen, was falsch ist. Wenn Sie die Schäden sehen wollen, versuchen Sie die Zeichenfolge auf dem Stack Zuweisung

char buffer[strlen("This string is 27 char long" + 1)]; 

und der Schreib vorbei. Andere Variablen werden geschrieben, Sie können auch Code hinzufügen, der ausgeführt wird, wenn Sie wirklich wissen, wie die Binärdatei funktioniert.

Um einen solchen Pufferüberlauf auszunutzen, müssen Sie die gewünschten Daten schreiben und dann einen Weg finden, um zu diesen Daten "zu springen", um ausgeführt zu werden.

+0

Dies ist immer noch nicht * garantiert * ein Problem zu verursachen, da Stapelzuweisungen auch in der Regel auf 4 oder 8 Byte Grenzen ausgerichtet sind . – Roddy

1

Sie haben Recht, dass die Zeigerarithmetik in diesem Beispiel eine falsche (kürzere) Länge ergibt, die an new übergeben wird. Der wahrscheinlichste Grund, warum Sie diesen Absturz nicht durchführen können, liegt darin, dass eine gewisse Unsicherheit darüber besteht, wie viel Pufferspeicher tatsächlich durch die Speicherzuweisung bereitgestellt wird.

Die Bibliothek darf einen größeren Puffer als angefordert bereitstellen. Darüber hinaus ist es auch möglich, dass, was auch immer Ihrem Puffer folgt, ein Zuordnungsheader vorangestellt wird, der Maschinenwort-Ausrichtungsregeln unterliegt. Das bedeutet, dass bis zu drei Füllbytes (abhängig von der Plattform) vor dem nächsten Verrechnungsheader vorhanden sein können.

Auch wenn Sie den nächsten Zuordnungsheader überschrieben haben (der zum Verwalten der zugewiesenen Speicherblöcke verwendet wird), würde er sich nicht als Problem darstellen, bis der Besitzer des nächsten Blocks versucht hat, ihn an den Heap zurückzugeben.

1

Viele historische malloc Implementierungen legen Buchhaltungsdaten unmittelbar vor und/oder nach dem zugewiesenen Block ab. Es ist möglich, dass Sie solche Daten überschreiben, in diesem Fall würden Sie keinen Fehler/Absturz sehen, bis Sie versuchen, den Speicher freizugeben (oder vielleicht frei, was auch immer der nächste Block passiert). Ebenso ist es möglich, dass die Buchhaltungsinformationen für eine nachfolgende Zuordnung Ihre Zeichenfolge später überschreiben.

Ich vermute, moderne malloc Implementierungen machen einige Anstrengungen, um gegen Heap Korruption durch Auffüllen von Zuordnungen mit Integritätsprüfung Daten zu schützen, wenn Sie Glück haben, wird nichts Schlimmes passieren oder Sie könnten eine Warnmeldung während einer späteren Zuteilung/kostenlos erhalten Betrieb.

0

Wie von anderen angegeben, sind Sie völlig richtig in der Annahme, dass dies nicht gut ist, und der Grund, warum Sie dies nicht sehen, ist Auffüllen. Versuchen Sie valgrind auf diesem, das sollte definitiv diesen Fehler finden.

0

Ihr Problem ist, dass Sie

char* buffer = new char[strlen("This string is 27 char long" + 1)]; 

statt

char* buffer = new char[strlen("This string is 27 char long") + 1]; 

Bedeutung sind schriftlich, dass auf den ersten Sie Strlen geben() eine Adresse, die nicht das ist Anfang Ihrer Zeichenfolge.

diesen Code Versuchen:

const char szText[] = "This string is 27 char long"; 
char* buffer = new char[strlen(szText) + 1]; 
sprintf(buffer, szText); 
+0

Das weiß er schon.Lesen Sie die Frage, bevor Sie eine Antwort schreiben. – manneorama

+1

Ich kenne das Problem, das ist mir aufgefallen und hat mich hierher gebracht. Es war in jemand anderen Code, ich brauchte nur eine zweite Meinung und wie zu erwarten war das zusätzliche Wissen und Einblick hier spektakulär. Danke für Ihre Hilfe! – KevenK

+0

Sie beantworten die Frage nicht. Er weiß, dass die '+ 1' fehl am Platz ist. Er sagte es klar in der Frage. Er wollte wissen, ob seine Einschätzung des Fehlers richtig war, da er beim Testen kein falsches Verhalten zeigen konnte. –

1

Ich versuchte es mit Heapzuweisungen, sind Variablen im Speicher in diesem Fall nicht kontinuierlich. Daher ist es in diesem Fall schwierig, einen Pufferüberlauf zu erzeugen.

kaufen versuchen Sie es mit Stapelüberlauf

#include "stdio.h" 
#include "string.h" 

int main() 
{ 
    unsigned int y  = (0xFFFFFFFF); 
    char buffer[strlen("This string is 27 char long" + 1)]; 
     unsigned int x  = (0xFFFFFFFF); 
     sprintf(buffer, "This string is 27 char long"); 

     printf("X (%#x) is %#x, Y (%#x) is %#x, buffer '%s' (%#x) \n", &x, x,&y, y, buffer, buffer); 
     return 0; 
    } 

Sie werden sehen, dass Y beschädigt ist.

0

Der Grund, dass die Zeichenfolge im Debugger einwandfrei gedruckt wird, ist, dass als Teil von sprintf das abschließende NULL-Zeichen in den Speicher geschrieben wird (in diesem Fall über den zugewiesenen Puffer hinaus) und beim Lesen der Zeichenfolge NULL-Zeichen ist vorhanden, um die Zeichenfolge wie erwartet zu beenden.

Das Problem ist, dass das Byte, das das NULL-Zeichen enthält, nicht als Teil des ursprünglichen new zugewiesen wurde und daher später für eine andere Zuordnung verwendet werden konnte. In diesem Fall erhalten Sie wahrscheinlich die ursprüngliche Zeichenfolge mit angehängtem Müll, wenn Sie die Zeichenfolge danach lesen.

0

Korrekte Aussage. Da Sie die Adresse des zweiten Zeichens der Zeichenkette an strlen() übergeben, erhalten Sie als Ergebnis die Länge ein Zeichen weniger. Abgesehen davon ist das Hauptproblem mit sprintf(), das ist einer der Gründe, dass es nicht sicher ist.

Auch das kompiliert und führt aus (kann auch abstürzen).

char* x = new char; 
    sprintf(x, "This is way longer than one character"); 
    printf("%s", x); 

Um dieses gefährliche Problem zu vermeiden, sollten Sie sicher Versionen dieser Funktion wie snprintf() oder asprintf() unter GCC oder sprintf_s() unter MSVC verwenden.

Als Referenzen finden Sie in dieser Hinsicht und auch Sicherheitshinweis von MSDN Sprintf() Artikel.

Verwandte Themen