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]; ...
Speicherschutz ist unter Windows seit Windows NT mit dabei. Bei Linux weiß ich es nicht, ob es schon von Anfang an mit dabei war.
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
> 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.
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.
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)
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?
Du liegst falsch, oft hängt der Stackverbrauch auch von den Eingangsdaten ab (alloca(), Rekursionstiefe).
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.
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?
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.
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.
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
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?
Bei einer embedded Entwicklung sollte man der Stackverbrauch schon beim Design kennen.
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?
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
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
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
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
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
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.