For a 32-bit windows application is it valid to use stack memory below ESP for temporary swap space without explicitly decrementing ESP?
Consider a function that ret
When a thread gets created, Windows reserves a contiguous region of virtual memory of a configurable size (the default is 1 MB) for the thread's stack. Initially, the stack looks like this (the stack grows downwards):
--------------
| committed |
--------------
| guard page |
--------------
| . |
| reserved |
| . |
| . |
| |
--------------
ESP
will be pointing somewhere inside the committed page. The guard page is used to support automatic stack growth. The reserved pages region ensures that the requested stack size is available in virtual memory.
Consider the two instructions from the question:
MOV [ESP-4], EAX
FLD [ESP-4]
There are three possibilities:
EXCEPTION_CONTINUE_EXECUTION
. As long as the second instruction is immediately after the first one (it is not in the exception handler or placed after it), then the second instruction will not execute. So you're still safe. Execution continues from stack frame where the exception handler exists.EXCEPTION_CONTINUE_EXECUTION
. Execution continues from the same instruction that raised the exception (potentially with a context modified by the handler). In this particular example, the first will be re-executed to write a value below ESP
. No problem. If the second instruction raised an exception or there are more than two instructions, then the exception might occur a place after a value is written below ESP
. When the exception handler gets called, it may overwrite the value and then return EXCEPTION_CONTINUE_EXECUTION
. But when execution resumes, the value written is assumed to still be there, but it's not anymore. This is a situation where it's not safe to write below ESP
. This applies even if all of the instructions are placed consecutively. Thanks to @RaymondChen for pointing this out.In general, if the two instructions are not placed back-to-back, if you are writing to locations beyond ESP
, there is no guarantee that the written values won't get corrupted or overwritten. One case that I can think of where this might happen is structured exception handling (SEH). If a hardware-defined exception (such as divide by zero) occurs, the kernel exception handler will be invoked (KiUserExceptionDispatcher
) in kernel-mode, which will invoke the user-mode side of the handler (RtlDispatchException
). When switching from user-mode to kernel-mode and then back to user-mode, whatever value was in ESP
will be saved and restored. However, the user-mode handler itself uses the user-mode stack and will iterate over a registered list of exception handlers, each of which uses the user-mode stack. These functions will modify ESP
as required. This may lead to losing the values you've written beyond ESP
. A similar situation occurs when using software-define exceptions (throw
in VC++).
I think you can deal with this by registering your own exception handler before any other exception handlers (so that it is called first). When your handler gets called, you can save your data beyond ESP
elsewhere. Later, during unwinding, you get the cleanup opportunity to restore your data to the same location (or any other location) on the stack.
You need also to similarly watch out for asynchronous procedure calls (APCs) and callbacks.