Frage

Wie kommt es zu einem Stapelüberlauf und was sind die besten Möglichkeiten, um sicherzustellen, dass er nicht passiert, bzw. wie man ihn verhindert, insbesondere auf Webservern, aber auch andere Beispiele wären interessant?

War es hilfreich?

Lösung

Stapel

Ein Stapel ist in diesem Zusammenhang der Last-In-First-Out-Puffer, in dem Sie Daten platzieren, während Ihr Programm ausgeführt wird.Last in, first out (LIFO) bedeutet, dass das Letzte, was Sie hineinstecken, immer auch das Erste ist, was Sie wieder herausbekommen – wenn Sie zwei Gegenstände auf den Stapel schieben, „A“ und dann „B“, dann das Erste, was Sie herauswerfen vom Stapel wird „B“ sein, und als nächstes kommt „A“.

Wenn Sie in Ihrem Code eine Funktion aufrufen, wird die nächste Anweisung nach dem Funktionsaufruf auf dem Stapel gespeichert und der Speicherplatz, der möglicherweise durch den Funktionsaufruf überschrieben wird, wird gespeichert.Die von Ihnen aufgerufene Funktion verbraucht möglicherweise mehr Stack für ihre eigenen lokalen Variablen.Wenn dies erledigt ist, gibt es den verwendeten Stapelspeicherplatz für lokale Variablen frei und kehrt dann zur vorherigen Funktion zurück.

Paketüberfluss

Ein Stapelüberlauf liegt vor, wenn Sie mehr Speicher für den Stapel verbraucht haben, als Ihr Programm verwenden sollte.In eingebetteten Systemen haben Sie möglicherweise nur 256 Bytes für den Stapel, und wenn jede Funktion 32 Bytes einnimmt, können Sie nur Funktionsaufrufe mit einer Tiefe von 8 haben – Funktion 1 ruft Funktion 2 auf, wer Funktion 3 aufruft, wer Funktion 4 aufruft ....Wer ruft Funktion 8 auf, wer ruft Funktion 9 auf, aber Funktion 9 überschreibt Speicher außerhalb des Stapels.Dadurch könnten Speicher, Code usw. überschrieben werden.

Viele Programmierer machen diesen Fehler, indem sie Funktion A aufrufen, die dann Funktion B aufruft, die dann Funktion C aufruft, die dann Funktion A aufruft.In den meisten Fällen funktioniert es vielleicht, aber nur eine einzige falsche Eingabe führt dazu, dass es für immer in diesem Kreis weiterläuft, bis der Computer erkennt, dass der Stapel überlastet ist.

Rekursive Funktionen sind ebenfalls eine Ursache dafür, aber wenn Sie rekursiv schreiben (dh Ihre Funktion ruft sich selbst auf), müssen Sie sich dessen bewusst sein und statische/globale Variablen verwenden, um eine unendliche Rekursion zu verhindern.

Im Allgemeinen verwalten das Betriebssystem und die Programmiersprache, die Sie verwenden, den Stack, und es liegt nicht in Ihrer Hand.Sie sollten sich Ihr Aufrufdiagramm (eine Baumstruktur, die von Ihrem Hauptdiagramm aus zeigt, was jede Funktion aufruft) ansehen, um zu sehen, wie tief Ihre Funktionsaufrufe gehen, und um Zyklen und Rekursionen zu erkennen, die nicht beabsichtigt sind.Absichtliche Zyklen und Rekursionen müssen künstlich überprüft werden, um Fehler zu vermeiden, wenn sie sich zu oft gegenseitig aufrufen.

Abgesehen von guten Programmierpraktiken und statischen und dynamischen Tests können Sie auf diesen High-Level-Systemen nicht viel tun.

Eingebettete Systeme

In der eingebetteten Welt, insbesondere bei hochzuverlässigem Code (Automobil, Flugzeug, Raumfahrt), führen Sie umfangreiche Codeüberprüfungen und -prüfungen durch, führen aber auch Folgendes durch:

  • Rekursion und Zyklen nicht zulassen – durch Richtlinien und Tests erzwungen
  • Halten Sie Code und Stack weit auseinander (Code im Flash, Stack im RAM, und die beiden dürfen sich nie treffen)
  • Platzieren Sie Schutzbänder um den Stapel herum – einen leeren Speicherbereich, den Sie mit einer magischen Zahl füllen (normalerweise eine Software-Interrupt-Anweisung, aber es gibt hier viele Optionen), und schauen Sie sich hunderte oder tausende Male pro Sekunde die Schutzbänder an, um sicherzugehen Sie wurden nicht überschrieben.
  • Speicherschutz verwenden (d. h. keine Ausführung auf dem Stapel, kein Lesen oder Schreiben direkt außerhalb des Stapels)
  • Interrupts rufen keine sekundären Funktionen auf – sie setzen Flags, kopieren Daten und überlassen die Verarbeitung der Anwendung der Anwendung (andernfalls könnten Sie 8 tief in Ihrem Funktionsaufrufbaum landen, einen Interrupt haben und dann noch ein paar weitere Funktionen innerhalb der aufrufen). Unterbrechung, die den Blowout verursacht).Sie haben mehrere Aufrufbäume – einen für die Hauptprozesse und einen für jeden Interrupt.Wenn Ihre Interrupts sich gegenseitig unterbrechen können ...Naja, es gibt Drachen...

Hochsprachen und Systeme

Aber in Hochsprachen, die auf Betriebssystemen ausgeführt werden:

  • Reduzieren Sie Ihren lokalen Variablenspeicher (lokale Variablen werden auf dem Stapel gespeichert – obwohl Compiler diesbezüglich ziemlich schlau sind und manchmal große lokale Variablen auf den Heap legen, wenn Ihr Aufrufbaum flach ist)
  • Vermeiden Sie die Rekursion oder schränken Sie sie strikt ein
  • Teilen Sie Ihre Programme nicht zu weit in immer kleinere Funktionen auf – selbst ohne die Zählung lokaler Variablen verbraucht jeder Funktionsaufruf bis zu 64 Bytes auf dem Stapel (32-Bit-Prozessor, wodurch die Hälfte der CPU-Register, Flags usw. eingespart wird).
  • Halten Sie Ihren Aufrufbaum flach (ähnlich der obigen Aussage)

Webserver

Es hängt von der „Sandbox“ ab, die Sie haben, ob Sie den Stapel steuern oder sogar sehen können.Die Chancen stehen gut, dass Sie Webserver wie jede andere Hochsprache und jedes andere Betriebssystem behandeln können – es liegt größtenteils nicht in Ihrer Hand, aber überprüfen Sie die Sprache und den Server-Stack, die Sie verwenden.Es Ist Es ist beispielsweise möglich, den Stapel auf Ihrem SQL-Server zu sprengen.

-Adam

Andere Tipps

Ein Stapelüberlauf im echten Code kommt sehr selten vor.Die meisten Situationen, in denen es auftritt, sind Rekursionen, bei denen die Beendigung vergessen wurde.Es kann jedoch selten in stark verschachtelten Strukturen vorkommen, z. B.besonders große XML-Dokumente.Die einzige wirkliche Hilfe besteht hier darin, den Code umzugestalten, um ein explizites Stack-Objekt anstelle des Call-Stacks zu verwenden.

Die meisten Leute werden Ihnen sagen, dass es bei einer Rekursion ohne Exit-Pfad zu einem Stapelüberlauf kommt. Das stimmt zwar größtenteils, aber wenn Sie mit Datenstrukturen arbeiten, die groß genug sind, hilft Ihnen selbst ein richtiger Rekursions-Exit-Pfad nicht weiter.

Einige Optionen in diesem Fall:

Die unendliche Rekursion ist eine häufige Methode, um einen Stapelüberlauffehler zu erhalten.Um dies zu verhindern, stellen Sie immer sicher, dass es einen Ausstiegsweg gibt Wille getroffen werden.:-)

Eine andere Möglichkeit, einen Stapelüberlauf zu verursachen (zumindest in C/C++), besteht darin, eine riesige Variable auf dem Stapel zu deklarieren.

char hugeArray[100000000];

Das reicht.

Zu einem Stapelüberlauf kommt es, wenn Jeff und Joel der Welt einen besseren Ort bieten wollen, an dem sie Antworten auf technische Fragen erhalten kann.Es ist zu spät, diesen Stapelüberlauf zu verhindern.Diese „andere Seite“ hätte es verhindern können, indem sie nicht unübersichtlich gewesen wäre.;)

Normalerweise ist ein Stapelüberlauf das Ergebnis eines unendlichen rekursiven Aufrufs (angesichts der üblichen Speichermenge in Standardcomputern heutzutage).

Wenn Sie eine Methode, Funktion oder Prozedur aufrufen, besteht die „Standard“-Methode bzw. der Aufruf aus Folgendem:

  1. Schieben der Rückrichtung für den Aufruf in den Stapel (das ist der nächste Satz nach dem Aufruf)
  2. Normalerweise wird der Platz für den Rückgabewert im Stapel reserviert
  3. Schieben Sie jeden Parameter in den Stapel (die Reihenfolge variiert und hängt von jedem Compiler ab, außerdem werden einige von ihnen manchmal zur Leistungsverbesserung in den CPU-Registern gespeichert)
  4. Den eigentlichen Anruf tätigen.

Dies erfordert in der Regel einige Bytes, abhängig von der Anzahl und Art der Parameter sowie der Maschinenarchitektur.

Sie werden dann sehen, dass der Stapel zu wachsen beginnt, wenn Sie mit rekursiven Aufrufen beginnen.Nun wird der Stapel normalerweise so im Speicher reserviert, dass er in entgegengesetzter Richtung zum Heap wächst, sodass der Stapel bei einer großen Anzahl von Aufrufen ohne „Rückkehr“ langsam voll wird.

In früheren Zeiten konnte es einfach zu einem Stapelüberlauf kommen, weil der gesamte verfügbare Speicher einfach so erschöpft war.Beim virtuellen Speichermodell (bis zu 4 GB auf einem

Abgesehen von der Form des Stapelüberlaufs, die Sie durch eine direkte Rekursion erhalten (z. B Fibonacci(1000000)), eine subtilere Form davon, die ich schon oft erlebt habe, ist eine indirekte Rekursion, bei der eine Funktion eine andere Funktion aufruft, die eine andere aufruft, und dann eine dieser Funktionen die erste erneut aufruft.

Dies kann häufig bei Funktionen auftreten, die als Reaktion auf Ereignisse aufgerufen werden, aber selbst möglicherweise neue Ereignisse generieren, zum Beispiel:

void WindowSizeChanged(Size& newsize) {
  // override window size to constrain width
    newSize.width=200;
    ResizeWindow(newSize);
}

In diesem Fall der Anruf an ResizeWindow kann dazu führen, dass WindowSizeChanged() Rückruf erneut ausgelöst werden, was ruft ResizeWindow noch einmal, bis der Stapel aufgebraucht ist.In Situationen wie diesen müssen Sie die Reaktion auf das Ereignis oft aufschieben, bis der Stack-Frame zurückgekehrt ist, z. B. durch das Posten einer Nachricht.

Was?Niemand liebt diejenigen, die von einer Endlosschleife umgeben sind?

do
{
  JeffAtwood.WritesCode();
} while(StackOverflow.MakingMadBank.Equals(false));

Angesichts der Tatsache, dass dies mit „Hacking“ gekennzeichnet wurde, vermute ich, dass es sich bei dem „Stapelüberlauf“, auf den er sich bezieht, um einen Aufrufstapelüberlauf handelt und nicht um einen Stapelüberlauf einer höheren Ebene, wie er in den meisten anderen Antworten hier erwähnt wird.Es gilt nicht wirklich für verwaltete oder interpretierte Umgebungen wie .NET, Java, Python, Perl, PHP usw., in denen Webanwendungen normalerweise geschrieben werden. Ihr einziges Risiko besteht also im Webserver selbst, in den wahrscheinlich geschrieben ist C oder C++.

Schauen Sie sich diesen Thread an:

https://stackoverflow.com/questions/7308/what-is-a-good-starting-point-for-learning-buffer-overflow

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top