Die Stack-Sicherheitszone ist ein Speicherbereich direkt unterhalb des Stacks, wo er im Fall eines Overflows Spuren hinterlässt. Diese Methode wird immer für Desktop-Systeme genutzt, in denen das Betriebssystem einfach so eingerichtet werden kann, dass es Speicherschutzfehler in einem Stack-Overflow-Fall erkennt. In einem kleinen Embedded System ohne Speichermanagement (MMU) kann eine extra eingefügte Sicherheitszone sehr nützlich sein.
Damit eine Sicherheitszone effektiv ist, muss sie eine angemessene Größe haben, um Schreibvorgänge vollständig erfassen zu können. Die Konsistenzprüfung der Sicherheitszone kann über die Software stattfinden, die regelmäßig prüft, ob das Füllmuster der Sicherheitszone intakt ist. Ein besseres Verfahren kann angewandt werden, wenn der Mikrocontroller über eine Speicherschutzeinheit (Memory Protection Unit, MPU) verfügt. In diesem Fall lässt sich dieser Funktionsblock so einstellen, dass er reagiert, sobald die Sicherheitszone beschrieben wird. Findet ein Zugriff statt, wird eine Exception ausgelöst, und ein Exception-Handler kann den Vorgang für eine spätere Analyse aufzeichnen. Ein Verfahren, um einen Stack-Overflow zu erkennen, ist, den gesamten für den Stack vorgesehenen Speicherplatz mit einem speziellen Wert wie zum Beispiel 0xCD zu füllen, bevor die Anwendung startet.
Sobald die Ausführung stoppt, lässt sich der Stack von unten nach oben kontrollieren, bis ein Wert nicht 0xCD entspricht und einen Hinweis darauf gibt, wie weit der Stack genutzt wurde. Wird kein Wert nach dem vorgegebenen Muster gefunden, liegt ein Stack-Overflow vor. Obwohl dies eine sinnvolle Methode ist, um die Stack-Nutzung zu ermitteln, gibt es keine Garantie dafür, dass auch tatsächlich ein Overflow festgestellt wird.
Zum Beispiel kann ein Stack außerhalb seiner Grenzen wachsen und sogar Speicher außerhalb des Stack-Bereiches modifizieren, ohne dabei Bytes in der Nähe der Stack-Reichweite zu verändern. Auch könnte die Anwendung den Speicher innerhalb des Stack-Bereiches versehentlich verändern. Dieses Verfahren zur Überwachung der Stack-Nutzung wenden häufig Debugger an, um die Stack-Nutzung grafisch darzustellen (siehe Bild 2). Der Debugger ist aber nicht in der Lage, den Stack-Overflow unmittelbar festzustellen, sondern kann nur die Spuren erfassen, die dieser hinterlässt.
Berechnung über den Linker
Bei der Berechnung des maximal benötigten Stacks eignen sich auch Tools wie Compiler und Linker. Im Folgenden kommen Compiler und Linker von IAR zum Einsatz. Der Compiler ermittelt die benötigten Informationen, während der Linker bei richtigen Umgebungsbedingungen genau die maximale Stack-Nutzung für jeden Call-Graph-Root berechnet, also für jede Funktion, die nicht von einer anderen Funktion aufgerufen wird, beispielsweise der Anwendungsstart. Diese ist aber nur genau, wenn auch die Stack-Nutzungsinformation für jede Funktion in der Anwendung exakt ist.
Generell generiert der Compiler diese Information für jede C-Funktion, aber in manchen Situationen muss man dem System die Stack-bezogenen Informationen selber liefern. Gibt es zum Beispiel indirekte Aufrufe (über Funktionszeiger) in der betreffenden Anwendung, muss eine Liste mit möglichen Funktionen, die von jeder Aufruffunktion gestartet werden können, bereitgestellt werden. Dazu können »pragma«-Direktiven in der Quelldatei dienen oder eine eigene Stack-Nutzungs-Steuerdatei beim Linken.
void
foo(int i)
{
#pragma calls =
fun1, fun2, fun3
func_arr[i]();
}
In eine Stack-Nutzungs-Steuerdatei lassen sich Informationen für Funktionen in Modulen eintragen, die über keine solchen verfügen. Der Linker kann auch Warnungen generieren, wenn Informationen fehlen, beispielsweise unter folgenden Umständen:
Wenn die Stack-Analyse aktiviert wird, erhält die Linker-Map-Datei einen zusätzlichen Abschnitt zur Stack-Nutzung, der für jeden Call-Graph-Root die jeweilige Aufrufkette auflistet, woraus sich die maximale Stack-Tiefe ergibt. Die maximale Stack-Nutzung für das gesamte System berechnet sich durch die Addition der Ergebnisse aus jedem Call-Graph-Root. In der vorliegenden Analyse beträgt die maximale Stack-Nutzung in Summe 500+24+24+12+92+8+1144+8+24+32+152 = 2020 Byte (Bild 3). Dabei ist zu berücksichtigen, dass diese Art der Stack-Nutzungs-Analyse ein Worst-Case-Ergebnis errechnet.
Es ist möglich, dass sich in der Anwendung - ob aufgrund der Programmierung oder aus Zufall - diese maximale Aufrufkette niemals ergibt.