Forum: PC-Programmierung In C Stack-overflow vorhersehen bzw. abfangen


von Lothar (Gast)


Lesenswert?

Kann man auf dem PC in C einen Stack-overflow vorhersehen oder abfangen?

Auf einem uC z.B. 8051 kann man vorher auf den Stack-pointer SP sehen, 
wie weit der noch from Crash weg ist. Oder auf ARM kann man das 
Stack-Ende mit der MPU schützen, und im Memory Fault Handler den Stack 
wieder herstellen, ohne dass das Program crasht.

Aber auf dem PC? Hier mal als Beispiel - ist natürlich vorher klar, das 
muss crashen - aber als Test:

int main(void) { char image[1024*1024*1024]; ...

von Walter T. (nicolas)


Lesenswert?

Speicherschutz ist unter Windows seit Windows NT mit dabei. Bei Linux 
weiß ich es nicht, ob es schon von Anfang an mit dabei war.

von Matthias L. (Gast)


Lesenswert?

Lothar schrieb:
> weit der noch from Crash weg ist

Woher weisst Du den Wert, wo er crasht?

von cppbert3 (Gast)


Lesenswert?

Walter T. schrieb:
> Speicherschutz ist unter Windows seit Windows NT mit dabei. Bei
> Linux weiß ich es nicht, ob es schon von Anfang an mit dabei war.

Er fragt nicht ob es Schutz gibt sondern wie er erkennen kann ob es 
"bald" soweit ist

von Jim M. (turboj)


Lesenswert?

>  char image[1024*1024*1024];

Ein GB muss auf einem 64-Bit PC nicht unbedingt crashen, der hätte 
genug Adressraum. AFAIK setzt man die Stack Größe aber lange nicht so 
hoch.

Auf dem PC erzeugen Zugriffe auf ungemappten Addressraum eine 
Zugriffsverletzung.

Aber nicht jeder mögliche Stacküberlauf endet zwangsläufig in 
ungemappten oder anderweitig gesperrtem Addressraum - er könnte z.B. 
zufällig (oder gewollt) im Datensegment landen und da unbemerkt Werte 
verändern.

von (prx) A. K. (prx)


Lesenswert?

Compiler kann man mitunter beibringen, bei Stack-Allokation keine 
grossen Sprünge zu machen, sondern jede Page zu besuchen. Wenn die 
Laufzeitumgebung dann eine guard page unten an den Stack setzt, dann 
scheppert es mit klarer Ursache. Es gilt dann, diese Exception 
abzufangen.

von udok (Gast)


Lesenswert?

Ja geht mit Exceptions unter Windows.

Am Ende vom Stack gibt es eine "Guard Page", wenn du darauf
zugreifst, dann gibt es eine Exception, die du auch abfangen kannst.
1
__declspec(noinline) int is_stack_available(size_t amount)
2
{
3
    __try {
4
        /* Allocate amount bytes */
5
        _alloca(amount);
6
        return 1;
7
    } __except ( GetExceptionCode() == EXCEPTION_STACK_OVERFLOW ?
8
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
9
    {
10
        /* Stack overflow */
11
        _resetstkoflw();
12
        return 0;
13
    }
14
}

Die Exception Methode hat den Nachteil, dass die Stackseiten in den
Speicher geholt werden.

Es gibt ab Windwos 8 auch die GetCurrentThreadStackLimits(),
und SetThreadStackGuarantee(), mit denen du die Stackgrösse abfragen 
kannst, ohne die Seiten in den RAM zu mappen. Vor Windows 8 gibt es
die Info im Thread Environment Block (TEB)

von Gerald K. (geku)


Lesenswert?

Sollte der maximale Speicherverbrauch nicht zur Übersetzungszeit bekannt 
sein?
Verschachtelte Funktionsaufrufe + lokale Variable am Stack + 
verschachtelte Interruptaufrufe und deren lokale Variable am Stack. 
Damit sollte der Compiler in der Lage sein den richtigen Speicherplatz 
für das Stack zu reservieren. Oder liege ich da  falsch?

von udok (Gast)


Lesenswert?

Du liegst falsch, oft hängt der Stackverbrauch auch von den
Eingangsdaten ab (alloca(), Rekursionstiefe).

von Rolf M. (rmagnus)


Lesenswert?

Lothar schrieb:
> Kann man auf dem PC in C einen Stack-overflow vorhersehen oder abfangen?

Die Frage ist: Was willst du vorhersagen? Wenn eine Funktion zu viel 
Stack braucht, bedeutet das, dass du sie gar nicht erst aufrufen darfst. 
Dazu musst du aber vorher wissen, wie viel Stack sie braucht. Und du 
musst dann natürlich auch noch irgendetwas sinnvolles ohne den Aufruf 
tun können.
Und wenn das alles gegeben ist und du auch die Stackgröße kennst, kannst 
du das hier natürlich auch auf dem PC tun:

Lothar schrieb:
> Auf einem uC z.B. 8051 kann man vorher auf den Stack-pointer SP sehen,
> wie weit der noch from Crash weg ist.

von Gerald K. (geku)


Lesenswert?

udok schrieb:
> Du liegst falsch, oft hängt der Stackverbrauch auch von den
> Eingangsdaten ab (alloca(), Rekursionstiefe).

Soll das heißen, dass der Stackspeicherverbrauch nicht berechenbar oder 
determinístisch ist?

von keinLichtAufGing (Gast)


Lesenswert?

Gerald K. schrieb:

> Soll das heißen, dass der Stackspeicherverbrauch nicht berechenbar oder
> determinístisch ist?

Zumindest manchmal ja. Klassisches Beispiel ist parsen von 
arithmetischen Ausdrücken mit Klammern durch rekursiven Abstieg. Der 
maximale Stackverbrauch hängt dann nur von den Eingabedaten ab. Im 
Embedded-Bereich dürfen rekursive Funktionen deshalb oft nicht verwendet 
werden.

Man kann die Rekursion im Parser zwar wegprogrammieren, damit verschiebt 
sich aber das Problem nur, denn irgendwo muß die 
Verschachtelungsstruktur gespeichert werden. Sprich: Wenn Du in einen 
Taschenrechner mit Klammerfunktion eingibst

 -  1*(1)
 -  1*(0+1*(1))
 -  1*(0+1*(0+1*(1)))
 -  ...

wird dem Taschenrechner sicher irgendwann der Speicher ausgehen müssen.

von A. S. (Gast)


Lesenswert?

Gerald K. schrieb:
> Sollte der maximale Speicherverbrauch nicht zur Übersetzungszeit bekannt
> sein?
> Verschachtelte Funktionsaufrufe + lokale Variable am Stack +
> verschachtelte Interruptaufrufe und deren lokale Variable am Stack.
> Damit sollte der Compiler in der Lage sein den richtigen Speicherplatz
> für das Stack zu reservieren. Oder liege ich da  falsch?

Es gibt Compiler, die das implizit machen. Z.B. der PIC-Compiler, da er 
gar keinen Stack hat sondern die Variablen auf feste, aber mehrfach 
belegte Adressen legt.

Die Grenzen sind Rekursion, direkt und indirekt. Direkt: Funktion a ruft 
sich selbst n mal auf, Indirekt: Funktion A ruft Funktion B, Funktion B 
ruft Funktion A auf.

Beides kann er (teilweise) erkennen und verbieten, schränkt also ein.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Speicherschutz ist unter Windows seit Windows NT mit dabei. Bei Linux
> weiß ich es nicht, ob es schon von Anfang an mit dabei war.

Bei UNIX, dem "Vorfahren" von Linux, war der Speicherschutz schon 
vorhanden, als DOS/Windows davon noch gar nicht wussten. 
Selbstverständlich ist der Speicherschutz Grundvoraussetzung bei 
unixoiden Betriebssystemen, wozu auch   Linux gehört.

Siehe auch: 
https://de.wikipedia.org/wiki/Speicherschutz#Betriebssysteme_ohne_Speicherschutz 
ff.

: Bearbeitet durch Moderator
von Markus K. (markus-)


Lesenswert?

Gerald K. schrieb:
> udok schrieb:
>> Du liegst falsch, oft hängt der Stackverbrauch auch von den
>> Eingangsdaten ab (alloca(), Rekursionstiefe).
>
> Soll das heißen, dass der Stackspeicherverbrauch nicht berechenbar oder
> determinístisch ist?

Ja. Wenn das wichtig ist, dann muss man eben Rekursion und alloca 
verbieten.
Klar, wenn Du weißt, dass Deine Rekursion maximal 10 Ebenen tief ist, 
dann könnte man das trotzdem berechnen, aber wenn die Abbruchbedingung 
fehlerhaft ist oder Deine Annahmen nicht stimmen geht das schief.

Wie keinLichtAufGing schreibt kann man die Rekursion natürlich in 
Iterative Varianten mit Arrayzugriffen umwandeln und muss dann die 
Arrayzugriffe überprüfen. Das hat den Vorteil das man richtige 
Fehlermeldungen geben kann. Wie soll man mit einer "Stack voll"-Meldung 
umgehen? Kann man überhaupt noch eine Fehlermeldung ausgeben? Kann man 
ohne Debugger überhaupt feststellen, an welcher Stelle der Stackoverflow 
passiert ist?

von Pandur S. (jetztnicht)


Lesenswert?

Bei einer embedded Entwicklung sollte man der Stackverbrauch schon beim 
Design kennen.

von PittyJ (Gast)


Lesenswert?

Gibts auf dem PC (Windows/Linux) nicht auch virtuellen Speicher?
Der kann dann doch Teile des Hauptspeichers auf Platte auslagern. Das 
könnten doch auch unbenutzte Stackbereiche sein?

von Georg (Gast)


Lesenswert?

Pandur S. schrieb:
> Bei einer embedded Entwicklung sollte man der Stackverbrauch schon beim
> Design kennen.

Ja, nur hilft das nicht viel weiter - was soll ein Embedded System denn 
tun, wenn es einen Stack-Overflow erkennt? Ausser sicherheitshalber 
stehenzubleiben (wenn das sinnvoll möglich ist). Eine Heizungsregelung 
wird nicht per EMail den Systemadministrator verständigen können.

Bei Controller-Compilern die ich verwendet habe gab es die Option, auf 
Stack Overflow zu prüfen, aber das bläht natürlich den Code erheblich 
auf ohne das obige Problem zu lösen.

Das geht übrigens auch ganz primitiv ohne Memory Management oder 
Exceptions, man muss halt vor jeder Stack-Operation den Wert des 
Stackpointers überprüfen, die Grenzen sind dem Compiler ja bekannt.

Georg

von (prx) A. K. (prx)


Lesenswert?

PittyJ schrieb:
> Gibts auf dem PC (Windows/Linux) nicht auch virtuellen Speicher?
> Der kann dann doch Teile des Hauptspeichers auf Platte auslagern. Das
> könnten doch auch unbenutzte Stackbereiche sein?

Ungenutzer Speicher wird nicht ausgelagert, weil es ihn zunächst nur als 
Adressraum gibt. Erst bei Zugriff wird Speicher zugeordnet.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Lothar schrieb:
> Aber auf dem PC?

Auf dem PC kriegst Du die bekannte Meldung "Access violation at address 
..."
Dann in den Taskmanager gehen und das Programm abschießen.

In der Regel stellen sich Programme beleidigt, wenn sie nicht den 
gewünschten Speicher bekommen. Nur ganz selten prüft ein Entwickler den 
Returnwert von malloc ab und versucht es nochmal mit kleineren Werten.

Aber oft ist es gar nicht zu wenig Speicher, sondern ein Pointer, der in 
den Wald zeigt. D.h. ein Programmfehler des Entwicklers oder in einer 
Lib.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Georg schrieb:
> Pandur S. schrieb:
>> Bei einer embedded Entwicklung sollte man der Stackverbrauch schon beim
>> Design kennen.
>
> Ja, nur hilft das nicht viel weiter - was soll ein Embedded System denn
> tun, wenn es einen Stack-Overflow erkennt?

Gegenfrage:
Wie soll es denn bei einem typischen Embedded System zu einem Überlauf 
des Stacks kommen? Man hat eine klar begrenzte Menge an Sensoren (und 
Aktoren). Man kennt deren Abtastraten und man weiß daher, wie viele 
Daten pro Zeiteinheit "reinkommen". Warum sollte man dann abhängig von 
einem Input jetzt auf einmal ungewöhnlich viel mehr Stack benötigen als 
sonst? Und wenn es nicht von einem Input an das System abhängt, dann 
weiß man es ja eben im voraus wieviel Stack man benötigen wird. Bzw. man 
kann dies mit entsprechenden Tools analysieren (Static Code Analysis, 
Profiling, Valgrind, ...)

Für die Werte, die man von Sensoren oder anderen Geräten empfängt, gilt 
natürlich: Überprüfen auf Plausibilität. Man empfängt ja auch nicht ein 
UINT8 und sagt bei einer Division dann, "Ach, da wird schon keine Null 
drinstehen, weil der Sender bestimmt keine Null verschickt. Also kann 
ich einfach immer sofort durch den empfangenen Wert teilen, das passt 
schon." :-)

Das letzte Mal im Leben, dass ich einen Stack Overflow gesehen habe, war 
als ich auf einem PC eine rekursive Funktion programmiert hab. Auf 
Embedded Systemen macht man aber eh alles iterativ, von daher:

Kann es sein, dass man hier zu einer Lösung ein Problem sucht? ;-)

: Bearbeitet durch User
von Georg (Gast)


Lesenswert?

Mark B. schrieb:
> Gegenfrage:
> Wie soll es denn bei einem typischen Embedded System zu einem Überlauf
> des Stacks kommen?

Bei dir selbstverständlich nicht, weil du ja niemals Fehler machst, es 
soll aber auch Programmierer geben, die manchmal einen Fehler übersehen. 
Teilnehmer dieses Forums natürlich ausgeschlossen.

Georg

von Mark B. (markbrandis)


Lesenswert?

Georg schrieb:
> Bei dir selbstverständlich nicht, weil du ja niemals Fehler machst, es
> soll aber auch Programmierer geben, die manchmal einen Fehler übersehen.
> Teilnehmer dieses Forums natürlich ausgeschlossen.

Klar passieren Fehler. Ein Stack Overflow ist aber alles in allem doch 
eher untypisch. Da gibt es andere, viel häufiger vorkommende 
Fehlerklassen:

-Zugriff über nicht oder nicht korrekt initialisierten Zeiger
-Eingangswerte einer Funktion nicht auf Plausibilität geprüft
-Fehler beim Konvertieren von Datentypen
-Array-index-out-of-bound Zugriffsfehler
-Funktion wurde falsch oder unvollständig spezifiziert
-...

: Bearbeitet durch User
von Nico T. (wurstnase)


Lesenswert?

Mark B. schrieb:
> Ein Stack Overflow ist aber alles in allem doch eher untypisch.

Kommt drauf an. In einem RTOS kannst du für jeden Thread deinen Stack 
definieren. Je nach Umfang muss man da schonmal an die Grenzen gehen. 
Zumindest in der Entwicklung beim Debuggen mit -Og oder -O0 kam der eine 
oder andere Thread schonmal ans Limit. Untypisch oder selten sind RTOS 
nicht.

von c-hater (Gast)


Lesenswert?

Frank M. schrieb:

> Bei UNIX, dem "Vorfahren" von Linux, war der Speicherschutz schon
> vorhanden, als DOS/Windows davon noch gar nicht wussten.
> Selbstverständlich ist der Speicherschutz Grundvoraussetzung bei
> unixoiden Betriebssystemen, wozu auch   Linux gehört.

Das ist sicher so.

Aber im Gegenzug kann man sagen: DOS und Windows liefen schon auf 
günstigen PCs, die sich sehr viele leisten konnten, als Unix daran noch 
völlig gehindert war, weil es eben dank der unbedingten Notwendigkeit 
einer MMU auf teure Workstations oder extrem teure Großrechner 
beschränkt war.

Nun, die 386er haben dann eine MMU bekommen und Windows hat gelernt, 
diese zu nutzen. Und Unix (in Form von Linux) hat es dann etwas später 
auch geschafft.

Und so kann ich heute problemlos BEIDES auf meinen PCs benutzen. Ist 
doch geil, manchmal gehen Geschichten auch richtig gut aus...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.