Warum überspringt mein Programm den Stack nicht, wenn ich ein 11MB Char-Array zuteile, während das Stack-Limit 10MB beträgt?

8

Ich habe hier zwei einfache C ++ - Programme und zwei Fragen. Ich arbeite in CentOS 5.2 und meine Entwicklungsumgebung ist wie folgt:

  • g ++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50)
  • "ulimit -s" Ausgabe: 10240 (kbytes), dh 10MB

Programm # 1:

main.cpp:

%Vor%

(Kompiliert mit "g ++ -g main.cpp")

Das Programm weist dem Stack 1024 * 1024 * 11 Bytes (dh 11 MB) zu, stürzt jedoch nicht ab. Nachdem ich die Zuordnungsgröße auf 1024 * 1024 * 12 (12 MB) geändert habe, stürzt das Programm ab. Ich denke, das sollte durch einen Stack-Überlauf verursacht werden. Aber warum stürzt das Programm nicht ab, wenn die Zuweisungsgröße 11 MB beträgt, was auch größer ist als die 10 MB-Obergrenze?

Programm # 2:

main.cpp:

%Vor%

(Kompiliert mit "g ++ -g main.cpp")

Dieses Programm würde zu einem Programmabsturz führen, weil es 12 MB Bytes auf dem Stack zuweist. Gemäß der Core-Dump-Datei (siehe unten) tritt der Absturz jedoch auf dem Buf auf, aber nicht auf Buf2. Sollte der Absturz nicht mit buf2 geschehen, weil wir aus Programm # 1 wissen, dass die Zuweisung von char buf [1024 * 1024 * 11] OK ist, nachdem wir weitere 1024 * 1024 Bytes zugewiesen haben, würde der Stack überlaufen?

Ich denke, dass es einige ziemlich grundlegende Konzepte geben muss, auf die ich kein solides Verständnis aufgebaut habe. Aber was sind sie?

Anhang: Die Core-Dump-Informationen, die von Programm # 2 erzeugt wurden:

%Vor%     
yaobin 15.08.2011, 09:12
quelle

4 Antworten

6

Sie gehen fälschlicherweise davon aus, dass die Stapelzuweisungen dort stattfinden, wo sie in Ihrem Code erscheinen. Immer wenn Sie lokale Variablen haben, deren Größe zum Zeitpunkt der Kompilierung bekannt ist, wird bei der Eingabe der Funktion Platz für diese zusammen zugewiesen. Nur dynamische lokale Variablen werden später zugewiesen (VLAs und alloca).

Außerdem tritt der Fehler auf, sobald Sie in den Speicher schreiben, nicht wenn er zuerst zugewiesen wird. Höchstwahrscheinlich befindet sich buf vor buf2 auf dem Stack und der Überlauf passiert also in buf, nicht in buf2.

    
Per Johansson 15.08.2011 09:37
quelle
4

Um diese Art von Mysterien zu analysieren, ist es immer nützlich, sich den generierten Code anzusehen. Meine Vermutung ist, dass Ihre bestimmte Compiler-Version etwas anderes macht, weil mein Segment mit -O0, aber nicht mit -O1 abschließt.

Aus Ihrem Programm # 1, mit g ++ a.c -g -O0 und dann objdump -S a.out

%Vor%

Dies ist der Standard-Stapelrahmen. Nichts zu sehen hier.

%Vor%

Richten Sie den Stapel auf ein Vielfaches von 16 aus, nur für den Fall.

%Vor%

Ordnen Sie 0xB00030 Byte Stack-Speicherplatz zu. Das sind 1024 * 1024 * 11 + 48 Bytes. Noch kein Zugriff auf den Speicher, also keine Ausnahme. Die zusätzlichen 48 Bytes werden intern vom Compiler verwendet.

%Vor%

Der erste Zugriff auf den Stack erfolgt außerhalb des ulimit, daher wird segfold ausgeführt.

%Vor%

Thiis ist der Stack-Protector .

%Vor%

Initialisiere das Array und rufe memset

auf %Vor%

Wie Sie sehen können, tritt der segfault auf, wenn auf die internen Variablen zugegriffen wird, weil sie zufällig unter dem großen Array liegen (sie müssen sein, weil es den Stack-Protector gibt, um Stack-Crash zu erkennen).

Wenn Sie mit Optimierungen kompilieren, merkt der Compiler, dass Sie mit dem Array nichts Nützliches tun und optimiert es. Also kein Sigseg.

Wahrscheinlich ist Ihre Version von GCC im Nicht-Optimierungsmodus ein wenig übermäßig intelligent und entfernt das Array. Wir können es weiter analysieren, wenn Sie die Ausgabe von objdump -S a.out veröffentlichen.

    
rodrigo 16.08.2011 09:18
quelle
3

Wenn lokale Variablen auf dem Stack definiert werden, gibt es keine echte Speicherzuweisung wie im Heap. Die Stapelspeicherzuordnung besteht einfacher darin, die Adresse des Stapelzeigers (der von aufgerufenen Funktionen verwendet wird) zu ändern, um den gewünschten Speicher zu reservieren.

Ich vermute, dass diese Operation des Änderns des Stapelzeigers nur einmal am Anfang der Funktion erfolgt, um Platz für alle verwendeten lokalen Variablen zu reservieren (durch die Möglichkeit, sie einmal pro Lokal zu ändern Variable). Dies erklärt, warum der Fehler in Ihrem Programm # 2 bei der ersten Zuweisung auftritt.

    
Didier Trosset 15.08.2011 09:19
quelle
0

Beide Programme sollten idealerweise segfault geben.

Normalerweise wird bei jeder Eingabe einer Funktion allen darin definierten Variablen Speicher auf dem Stack zugewiesen. Dies hängt jedoch auch von der Optimierungsstufe ab, mit der der Code kompiliert wird.

Die Optimierungsstufe Null, die während der Kompilierung als -O0 angezeigt wird, zeigt überhaupt keine Optimierung an. Es ist auch die Standard-Optimierungsebene, mit der ein Code kompiliert wird. Die oben genannten Programme ergeben, wenn sie mit -O0 kompiliert werden, segfault.

Wenn Sie jedoch die Programme mit höheren Optimierungsebenen kompilieren, stellt der Compiler fest, dass die definierte Variable nicht in der Funktion verwendet wird. Es entfernt daher die Variablendefinition aus dem Assemblersprachcode. Infolgedessen geben Ihre Programme keinen segfault.

    
rango 12.07.2016 15:26
quelle

Tags und Links