Anrufliste - Call stack

In der Informatik , ein Call - Stack ist eine Stapeldatenstruktur , dass speichert Informationen über die aktiven Subroutinen eines Computerprogramms . Diese Art von Stack wird auch als Ausführungs-Stack , Programm-Stack , Kontroll-Stack , Laufzeit-Stack oder Maschinen-Stack bezeichnet und wird oft nur als „ der Stack “ abgekürzt . Obwohl die Pflege des Aufrufstapels für das ordnungsgemäße Funktionieren der meisten Software wichtig ist , sind die Details in höheren Programmiersprachen normalerweise versteckt und automatisch . Viele Computer - Befehlssätze liefern spezielle Anweisungen für Stapel zu manipulieren.

Ein Call-Stack wird für mehrere verwandte Zwecke verwendet, aber der Hauptgrund dafür ist, den Punkt zu verfolgen, an den jede aktive Subroutine die Kontrolle zurückgeben sollte, wenn sie ihre Ausführung beendet. Ein aktives Unterprogramm ist ein Unterprogramm, das aufgerufen wurde, die Ausführung jedoch noch nicht abgeschlossen hat, wonach die Steuerung an den Aufrufpunkt zurückgegeben werden sollte. Solche Aktivierungen von Unterprogrammen können beliebig verschachtelt sein (als Sonderfall rekursiv), daher die Stack-Struktur. Wenn beispielsweise ein Unterprogramm DrawSquareein Unterprogramm DrawLinevon vier verschiedenen Stellen aus aufruft , DrawLinemüssen Sie wissen, wohin es nach Abschluss der Ausführung zurückkehren muss. Um dies zu erreichen, wird die Adresse , die der Anweisung folgt, die zu springt DrawLine, die Rückkehradresse , bei jedem Aufruf an die Spitze der Aufrufliste geschoben.

Beschreibung

Da der Call - Stack als organisierte Stapel drückt der Anrufer die Rücksprungadresse auf den Stack, und das aufgerufene Unterprogramm, wenn es fertig ist , zieht oder springt die Absenderadresse aus dem Call - Stack und überträgt die Steuerung an diese Adresse. Wenn ein aufgerufenes Unterprogramm ein weiteres Unterprogramm aufruft, wird es eine weitere Rückkehradresse auf den Aufrufstapel legen usw., wobei sich die Informationen entsprechend den Vorgaben des Programms auf- und abstapeln. Wenn das Pushen den gesamten Speicherplatz verbraucht, der dem Aufrufstapel zugewiesen ist, tritt ein Fehler auf, der als Stapelüberlauf bezeichnet wird und im Allgemeinen zum Absturz des Programms führt . Das Hinzufügen eines Eintrags einer Unterroutine zur Aufrufliste wird manchmal als "Wickeln" bezeichnet; Umgekehrt ist das Entfernen von Einträgen ein "Abwickeln".

Normalerweise ist einem laufenden Programm (oder genauer gesagt, jeder Task oder jedem Thread eines Prozesses ) genau ein Aufrufstapel zugeordnet , obwohl zusätzliche Stapel für die Signalverarbeitung oder kooperatives Multitasking (wie bei setcontext ) erstellt werden können. Da es nur eine in diesem wichtigen Kontext ist, kann es als bezeichnet den Stapel (implizit „der Aufgabe“); jedoch in der Forth Programmiersprache des Datenstapel oder Parameterstapel wird mehr explizit als die Aufrufliste abgerufen und wird allgemein bezeichnet als der Stapel (siehe unten).

In höheren Programmiersprachen sind die Besonderheiten des Aufrufstapels normalerweise vor dem Programmierer verborgen. Sie erhalten nur Zugriff auf eine Reihe von Funktionen und nicht auf den Speicher auf dem Stack selbst. Dies ist ein Beispiel für Abstraktion . Die meisten Assemblersprachen hingegen erfordern, dass Programmierer an der Manipulation des Stapels beteiligt sind. Die tatsächlichen Details des Stapels in einer Programmiersprache hängen vom Compiler , dem Betriebssystem und dem verfügbaren Befehlssatz ab .

Funktionen der Aufrufliste

Wie oben erwähnt, besteht der Hauptzweck einer Aufrufliste darin, die Rückkehradressen zu speichern . Wenn eine Unterroutine aufgerufen wird, muss die Stelle (Adresse) des Befehls, an der die aufrufende Routine später wieder aufgenommen werden kann, irgendwo gespeichert werden. Die Verwendung eines Stack zum Speichern der Rücksendeadresse hat wichtige Vorteile gegenüber alternativen Aufrufkonventionen . Einer ist, dass jede Task ihren eigenen Stack haben kann, und somit kann die Subroutine thread-sicher sein , d. h. gleichzeitig für verschiedene Tasks aktiv sein, die unterschiedliche Dinge tun. Ein weiterer Vorteil ist , dass durch die Bereitstellung reentrancy , Rekursion wird automatisch unterstützt. Wenn sich eine Funktion rekursiv selbst aufruft, muss für jede Aktivierung der Funktion eine Rücksprungadresse hinterlegt werden, damit später von der Funktionsaktivierung zurückgekehrt werden kann. Stapelstrukturen stellen diese Fähigkeit automatisch bereit.

Je nach Sprache, Betriebssystem und Maschinenumgebung kann eine Aufrufliste zusätzlichen Zwecken dienen, darunter zum Beispiel:

Lokale Datenspeicherung
Ein Unterprogramm benötigt häufig Speicherplatz zum Speichern der Werte lokaler Variablen , der Variablen, die nur innerhalb des aktiven Unterprogramms bekannt sind und keine Werte behalten, nachdem es zurückgekehrt ist. Es ist oft praktisch, Platz für diese Verwendung zuzuweisen, indem einfach die Oberseite des Stapels um genug verschoben wird, um den Platz bereitzustellen. Dies ist im Vergleich zur dynamischen Speicherzuweisung , bei der der Heap-Speicherplatz verwendet wird, sehr schnell . Beachten Sie, dass jede separate Aktivierung einer Subroutine ihren eigenen separaten Platz im Stack für Locals erhält.
Parameterübergabe
Unterroutinen erfordern oft, dass ihnen Werte für Parameter durch den Code, der sie aufruft, geliefert werden, und es ist nicht ungewöhnlich, dass Platz für diese Parameter in der Aufrufliste bereitgestellt wird. Im Allgemeinen werden Prozessorregister verwendet, um die Werte zu übergeben, wenn nur wenige kleine Parameter vorhanden sind, aber wenn mehr Parameter vorhanden sind, als auf diese Weise verarbeitet werden können, wird Speicherplatz benötigt. Die Aufrufliste eignet sich gut als Platz für diese Parameter, insbesondere da jedem Aufruf einer Unterroutine mit unterschiedlichen Parameterwerten ein separater Platz in der Aufrufliste für diese Werte zugewiesen wird.
Bewertungsstapel
Operanden für arithmetische oder logische Operationen werden am häufigsten in Register abgelegt und dort bearbeitet. In einigen Situationen können die Operanden jedoch bis zu einer beliebigen Tiefe gestapelt werden, was bedeutet, dass mehr als nur Register verwendet werden müssen (dies ist der Fall von Registerüberlauf ). Der Stapel solcher Operanden wird, ähnlich wie bei einem RPN-Rechner , als Auswertungsstapel bezeichnet und kann Platz im Aufrufstapel belegen.
Zeiger auf aktuelle Instanz
Einige objektorientierte Sprachen (zB C++ ) speichern den this- Zeiger zusammen mit Funktionsargumenten in der Aufrufliste, wenn Methoden aufgerufen werden. Die Dieser Zeiger zeigt auf die Objektinstanz mit dem Verfahren verbundenen aufgerufen werden.
Unterprogrammkontext einschließen
Einige Programmiersprachen (zB Pascal und Ada ) unterstützen die Deklaration von verschachtelten Subroutinen , die auf den Kontext ihrer umgebenden Routinen zugreifen dürfen, dh die Parameter und lokalen Variablen innerhalb des Geltungsbereichs der äußeren Routinen. Eine solche statische Verschachtelung kann sich wiederholen - eine Funktion, die innerhalb einer Funktion deklariert ist, die in einer Funktion deklariert ist... Die Implementierung muss ein Mittel bereitstellen, mit dem eine aufgerufene Funktion auf einer gegebenen statischen Verschachtelungsebene auf den einschließenden Rahmen auf jeder einschließenden Verschachtelungsebene verweisen kann. Im Allgemeinen wird diese Referenz durch einen Zeiger auf den Rahmen der zuletzt aktivierten Instanz der einschließenden Funktion implementiert, der als "Downstack-Link" oder "statischer Link" bezeichnet wird, um ihn von dem "dynamischen Link" zu unterscheiden, der auf den unmittelbaren Aufrufer verweist ( die nicht die statische Elternfunktion sein muss).

Anstelle einer statischen Verbindung können die Verweise auf die einschließenden statischen Rahmen in einer als Anzeige bekannten Zeigermatrix gesammelt werden , die indiziert ist, um einen gewünschten Rahmen zu lokalisieren. Die Tiefe der lexikalischen Verschachtelung einer Routine ist eine bekannte Konstante, daher ist die Größe der Anzeige einer Routine festgelegt. Außerdem ist die Anzahl der zu durchlaufenden enthaltenden Bereiche bekannt, der Index in die Anzeige ist ebenfalls festgelegt. Normalerweise befindet sich die Anzeige einer Routine in einem eigenen Stapelrahmen, aber der Burroughs B6500 implementierte eine solche Anzeige in Hardware, die bis zu 32 Ebenen statischer Verschachtelung unterstützte.
Die Anzeigeeinträge, die enthaltende Bereiche bezeichnen, werden aus dem entsprechenden Präfix der Anzeige des Anrufers abgerufen. Eine innere Routine, die rekursiv ist, erzeugt separate Aufrufrahmen für jeden Aufruf. In diesem Fall zeigen alle statischen Links der inneren Routine auf denselben Kontext der äußeren Routine.
Anderer Rückgabestatus
Neben der Rückkehradresse können in einigen Umgebungen andere Maschinen- oder Softwarezustände vorliegen, die wiederhergestellt werden müssen, wenn eine Subroutine zurückkehrt. Dies kann Dinge wie Berechtigungsebene, Informationen zur Ausnahmebehandlung, arithmetische Modi usw. umfassen. Bei Bedarf kann diese genauso wie die Rücksprungadresse in der Aufrufliste gespeichert werden.

Der typische Aufrufstack wird für die Rückkehradresse, Locals und Parameter (bekannt als Aufrufrahmen ) verwendet. In einigen Umgebungen können der Aufrufliste mehr oder weniger Funktionen zugewiesen sein. In der Programmiersprache Forth zum Beispiel werden normalerweise nur die Rückkehradresse, gezählte Schleifenparameter und Indizes und möglicherweise lokale Variablen auf dem Aufrufstapel (der in dieser Umgebung als Rückkehrstapel bezeichnet wird ) gespeichert , obwohl alle Daten vorübergehend platziert werden können dort unter Verwendung eines speziellen Rückgabe-Stack-Handling-Codes, solange die Anforderungen von Aufrufen und Rückgaben respektiert werden; Parameter werden normalerweise auf einem separaten Datenstapel oder Parameterstapel gespeichert , der in der Forth-Terminologie normalerweise als Stapel bezeichnet wird, obwohl es einen Aufrufstapel gibt, da normalerweise expliziter darauf zugegriffen wird. Einige Forths haben auch einen dritten Stack für Gleitkommaparameter .

Struktur

Call-Stack-Layout für nach oben wachsende Stacks

Ein Aufrufstapel besteht aus Stapelrahmen (auch Aktivierungsdatensätze oder Aktivierungsrahmen genannt ). Dies sind maschinenabhängige und ABI- abhängige Datenstrukturen, die Subroutinen-Zustandsinformationen enthalten. Jeder Stack-Frame entspricht einem Aufruf eines Unterprogramms, das noch nicht mit Return beendet ist. Wenn beispielsweise ein Unterprogramm namens DrawLinegerade läuft, nachdem es von einem Unterprogramm aufgerufen wurde DrawSquare, könnte der obere Teil des Aufrufstapels wie im nebenstehenden Bild angeordnet sein.

Ein Diagramm wie dieses kann in beide Richtungen gezeichnet werden, solange die Platzierung der Oberseite und damit die Richtung des Stapelwachstums verstanden wird. Darüber hinaus unterscheiden sich Architekturen unabhängig davon, ob Call-Stacks zu höheren Adressen oder zu niedrigeren Adressen hin wachsen. Die Logik des Diagramms ist unabhängig von der Adressierungswahl.

Der Stapelrahmen oben im Stapel ist für die aktuell ausgeführte Routine. Der Stapelrahmen enthält normalerweise mindestens die folgenden Elemente (in Push-Reihenfolge):

  • die an die Routine übergebenen Argumente (Parameterwerte) (falls vorhanden);
  • die Rückkehradresse zurück zum Aufrufer der Routine (zB im DrawLineStack-Frame eine Adresse im DrawSquareCode von ); und
  • Platz für die lokalen Variablen der Routine (falls vorhanden).

Stapel- und Rahmenzeiger

Wenn sich die Stack-Frame-Größen unterscheiden können, beispielsweise zwischen verschiedenen Funktionen oder zwischen Aufrufen einer bestimmten Funktion, stellt das Herausnehmen eines Frames aus dem Stack kein festes Dekrementieren des Stack-Zeigers dar . Bei der Rückkehr der Funktion wird der Stack-Pointer stattdessen auf den Frame-Pointer zurückgesetzt , den Wert des Stack-Pointers kurz vor dem Aufruf der Funktion. Jeder Stapelrahmen enthält einen Stapelzeiger auf den oberen Rand des Rahmens unmittelbar darunter. Der Stapelzeiger ist ein veränderliches Register, das von allen Aufrufen gemeinsam genutzt wird. Ein Rahmenzeiger eines gegebenen Aufrufs einer Funktion ist eine Kopie des Stapelzeigers, wie er war, bevor die Funktion aufgerufen wurde.

Die Positionen aller anderen Felder im Rahmen können entweder relativ zum oberen Rand des Rahmens als negative Offsets des Stapelzeigers oder relativ zum oberen Rand des darunter liegenden Rahmens als positive Offsets des Rahmenzeigers definiert werden. Die Position des Rahmenzeigers selbst muss von Natur aus als negativer Offset des Stapelzeigers definiert werden.

Adresse im Rahmen des Anrufers speichern

In den meisten Systemen hat ein Stapelrahmen ein Feld, das den vorherigen Wert des Rahmenzeigerregisters enthält, den Wert, den es während der Ausführung des Aufrufers hatte. Zum Beispiel hätte der Stapelrahmen von DrawLineeinen Speicherplatz, der den verwendeten Rahmenzeigerwert enthält DrawSquare(in der obigen Abbildung nicht gezeigt). Der Wert wird beim Eintritt in das Unterprogramm gespeichert und bei der Rückkehr wiederhergestellt. Wenn ein solches Feld an einer bekannten Stelle im Stapelrahmen vorhanden ist, kann der Code nacheinander auf jeden Rahmen unterhalb des Rahmens der aktuell ausgeführten Routine zugreifen und ermöglicht der Routine auch, den Rahmenzeiger auf den Rahmen des Aufrufers einfach wiederherzustellen , kurz bevor er zurückkehrt.

Lexikalisch verschachtelte Routinen

Programmiersprachen, die verschachtelte Subroutinen unterstützen, haben auch ein Feld im Aufrufrahmen, der auf den Stapelrahmen der letzten Aktivierung der Prozedur zeigt, die den Aufrufer am engsten kapselt, dh den unmittelbaren Geltungsbereich des Aufrufers. Dies wird als Zugriffslink oder statischer Link bezeichnet (da er die statische Verschachtelung während dynamischer und rekursiver Aufrufe verfolgt) und ermöglicht der Routine (sowie allen anderen Routinen, die sie aufrufen kann) bei jeder Verschachtelung Zugriff auf die lokalen Daten ihrer kapselnden Routinen Niveau. Einige Architekturen, Compiler oder Optimierungsfälle speichern einen Link für jede einschließende Ebene (nicht nur die unmittelbar einschließende), sodass tief verschachtelte Routinen, die auf flache Daten zugreifen, nicht mehrere Links durchlaufen müssen; diese Strategie wird oft als "Display" bezeichnet.

Zugriffslinks können wegoptimiert werden, wenn eine innere Funktion auf keine (nicht konstanten) lokalen Daten in der Kapselung zugreift, wie dies beispielsweise bei reinen Funktionen der Fall ist, die nur über Argumente und Rückgabewerte kommunizieren. Einige historische Computer, wie die großen Systeme von Burroughs , hatten spezielle "Anzeigeregister", um verschachtelte Funktionen zu unterstützen, während Compiler für die meisten modernen Maschinen (wie das allgegenwärtige x86) einfach ein paar Wörter auf dem Stack für die Zeiger reservieren, wenn nötig.

Überlappung

Für einige Zwecke kann man davon ausgehen, dass sich der Stapelrahmen einer Unterroutine und der ihres Aufrufers überlappen, wobei die Überlappung aus dem Bereich besteht, in dem die Parameter vom Aufrufer an den Aufrufer übergeben werden. In einigen Umgebungen schiebt der Aufrufer jedes Argument auf den Stapel, erweitert so seinen Stapelrahmen und ruft dann den Aufrufenden auf. In anderen Umgebungen hat der Aufrufer oben in seinem Stapelrahmen einen vorab zugewiesenen Bereich, um die Argumente aufzunehmen, die er an andere Subroutinen liefert, die er aufruft. Dieser Bereich wird manchmal als Bereich für ausgehende Argumente oder Callout-Bereich bezeichnet . Bei diesem Ansatz berechnet der Compiler die Größe des Bereichs als die größte, die von einer aufgerufenen Unterroutine benötigt wird.

Benutzen

Verarbeitung der Anrufseite

Normalerweise ist die erforderliche Manipulation der Aufrufliste an der Stelle eines Aufrufs einer Unterroutine minimal (was gut ist, da es für jede aufzurufende Unterroutine viele Aufrufstellen geben kann). Die Werte für die eigentlichen Argumente werden an der Aufrufstelle ausgewertet, da sie für den jeweiligen Aufruf spezifisch sind, und entweder auf den Stack geschoben oder in Registern abgelegt, je nach verwendeter Aufrufkonvention . Der eigentliche Aufrufbefehl, wie beispielsweise "Verzweigen und Verknüpfen", wird dann typischerweise ausgeführt, um die Steuerung an den Code des Zielunterprogramms zu übertragen.

Unterprogramm-Eintragsverarbeitung

In der aufgerufenen Subroutine wird der erste ausgeführte Code normalerweise als Subroutinen-Prolog bezeichnet , da er die erforderliche Verwaltung übernimmt, bevor der Code für die Anweisungen der Routine begonnen wird.

Bei Befehlssatzarchitekturen, bei denen der zum Aufrufen einer Unterroutine verwendete Befehl die Rückkehradresse in ein Register legt, anstatt sie auf den Stack zu verschieben, speichert der Prolog die Rückkehradresse im Allgemeinen, indem er den Wert auf den Aufrufstack überträgt, obwohl Unterprogramm ruft keine anderen Routinen auf, es kann den Wert im Register belassen. In ähnlicher Weise können die aktuellen Stapelzeiger- und/oder Rahmenzeigerwerte verschoben werden.

Wenn Rahmenzeiger verwendet werden, setzt der Prolog typischerweise den neuen Wert des Rahmenzeigerregisters vom Stapelzeiger. Platz auf dem Stack für lokale Variablen kann dann durch inkrementelles Ändern des Stack-Zeigers zugewiesen werden.

Die Programmiersprache Forth erlaubt das explizite Wickeln des Call-Stack (dort "Return-Stack" genannt).

Retourenabwicklung

Wenn eine Subroutine zur Rückkehr bereit ist, führt sie einen Epilog aus, der die Schritte des Prologs rückgängig macht. Dadurch werden in der Regel gespeicherte Registerwerte (wie der Frame-Pointer-Wert) aus dem Stack-Frame wiederhergestellt, der gesamte Stack-Frame durch Ändern des Stack-Pointer-Werts vom Stack entfernt und schließlich zum Befehl an der Rückkehradresse verzweigt. Unter vielen Aufrufkonventionen enthalten die Elemente, die der Epilog vom Stack entfernt, die ursprünglichen Argumentwerte, wobei in diesem Fall normalerweise keine weiteren Stack-Manipulationen vom Aufrufer vorgenommen werden müssen. Bei einigen Aufrufkonventionen liegt es jedoch in der Verantwortung des Aufrufers, die Argumente nach der Rückgabe vom Stack zu entfernen.

Abwickeln

Die Rückkehr von der aufgerufenen Funktion entfernt den obersten Frame vom Stapel und hinterlässt möglicherweise einen Rückgabewert. Der allgemeinere Vorgang, einen oder mehrere Frames aus dem Stapel zu nehmen, um die Ausführung an anderer Stelle im Programm wieder aufzunehmen, wird als Stapelabwickeln bezeichnet und muss durchgeführt werden, wenn nicht-lokale Kontrollstrukturen verwendet werden, wie sie zum Beispiel für die Ausnahmebehandlung verwendet werden . In diesem Fall enthält der Stapelrahmen einer Funktion einen oder mehrere Einträge, die Ausnahmebehandlungsroutinen angeben. Wenn eine Ausnahme ausgelöst wird, wird der Stapel abgewickelt, bis ein Handler gefunden wird, der bereit ist, den Typ der ausgelösten Ausnahme zu behandeln (abzufangen).

Einige Sprachen haben andere Kontrollstrukturen, die eine allgemeine Abwicklung erfordern. Pascal ermöglicht es einer globalen goto- Anweisung, die Kontrolle aus einer verschachtelten Funktion in eine zuvor aufgerufene äußere Funktion zu übertragen. Diese Operation erfordert, dass der Stapel abgewickelt wird, wobei so viele Stapelrahmen wie nötig entfernt werden, um den richtigen Kontext wiederherzustellen, um die Kontrolle an die Zielanweisung innerhalb der einschließenden äußeren Funktion zu übertragen. In ähnlicher Weise hat C die Funktionen setjmpundlongjmp , die als nicht-lokale Gotos wirken. Common Lisp ermöglicht die Kontrolle darüber, was passiert, wenn der Stapel abgewickelt wird, indem der unwind-protectspezielle Operator verwendet wird.

Beim Anlegen einer Fortsetzung wird der Stapel (logisch) abgewickelt und dann mit dem Stapel der Fortsetzung zurückgespult. Dies ist nicht die einzige Möglichkeit, Fortsetzungen zu implementieren; B. mehrere explizite Stapel verwendet, kann die Anwendung einer Fortsetzung einfach ihren Stapel aktivieren und einen zu übergebenden Wert wickeln. Die Programmiersprache Scheme ermöglicht die Ausführung beliebiger Thunks an bestimmten Punkten beim "Abwickeln" oder "Zurückspulen" des Steuerstapels, wenn eine Fortsetzung aufgerufen wird.

Inspektion

Die Aufrufliste kann manchmal überprüft werden, während das Programm ausgeführt wird. Je nachdem, wie das Programm geschrieben und kompiliert wird, können die Informationen auf dem Stack verwendet werden, um Zwischenwerte und Funktionsaufruf-Traces zu ermitteln. Dies wurde verwendet, um feinkörnige automatisierte Tests zu generieren und in Fällen wie Ruby und Smalltalk erstklassige Fortsetzungen zu implementieren. Als Beispiel implementiert der GNU Debugger (GDB) eine interaktive Inspektion des Aufrufstapels eines laufenden, aber angehaltenen C-Programms.

Regelmäßige Stichproben des Aufrufstapels können bei der Leistungsprofilierung von Programmen hilfreich sein, denn wenn der Zeiger einer Unterroutine in den Stichprobendaten des Aufrufstapels viele Male auftaucht, handelt es sich wahrscheinlich um einen Code-Engpass und sollte auf Leistungsprobleme untersucht werden.

Sicherheit

In einer Sprache mit freien Zeigern oder ungeprüften Array-Schreibvorgängen (wie in C) ist die Vermischung von Kontrollflussdaten, die die Ausführung von Code betreffen (die Rückgabeadressen oder die gespeicherten Rahmenzeiger) und einfachen Programmdaten (Parameter oder Rückgabewerte) möglicherweise) in einem Call - Stack ist ein Sicherheitsrisiko, ausnutzbaren durch Pufferüberlauf - Stack als die häufigste Art von Pufferüberlauf .

Einer dieser Angriffe beinhaltet das Füllen eines Puffers mit willkürlichem ausführbarem Code und dann das Überlaufen desselben oder eines anderen Puffers, um eine Rücksprungadresse mit einem Wert zu überschreiben, der direkt auf den ausführbaren Code zeigt. Als Ergebnis führt der Computer diesen Code aus, wenn die Funktion zurückkehrt. Diese Art von Angriff kann einfach mit W^X abgewehrt werden . Ähnliche Angriffe können sogar mit aktiviertem W^X-Schutz erfolgreich sein, einschließlich des Return-to-libc-Angriffs oder der Angriffe, die von einer Return-orientierten Programmierung ausgehen . Es wurden verschiedene Abschwächungen vorgeschlagen, wie zum Beispiel das Speichern von Arrays an einem vom Rückgabestapel vollständig getrennten Ort, wie dies in der Programmiersprache Forth der Fall ist.

Siehe auch

Verweise

Weiterlesen

Externe Links