Why is .NET behaving so badly when a StackOverflowException is thrown?

I know that StackOverflowExceptions in .NET cannot be caught, deleted your process, and have no stack trace. It is officially registered in MSDN . However, I am wondering what are the technical (or other) reasons for this behavior. All MSDN says:

In previous versions of the .NET Framework, your application could have a StackOverflowException object (for example, to recover from unlimited recursion). However, this practice is currently not encouraged because significant additional code is needed to reliably exclude and continue program execution.

What is this "significant additional code"? Are there other documented reasons for this behavior? Even if we can't catch SOE, why can't we get a stack trace? A few employees, and I just spent several hours debugging a production StackOverflowException that would take minutes with a stack trace, so I wonder if there is a good reason for my suffering.

+58
c # stack-overflow
Mar 17 '14 at 21:14
source share
3 answers

The thread stack is created by Windows. It uses so-called protective pages for detection. A feature that is typically available for user mode code, as described in this MSDN library article . The basic idea is that the last two pages of the stack (2 x 4096 = 8192 bytes) are reserved, and any access to the processor causes a page error, which turned into a SEH exception, STATUS_GUARD_PAGE_VIOLATION.

This is intercepted by the kernel in the case of pages that belong to the thread stack. It changes the security attributes of the first of these two pages, thereby giving the thread some sort of emergency stack space to solve the problem, and then repeatedly raising the STATUS_STACK_OVERFLOW exception.

This exception, in turn, is caught by the CLR. At this point, about 3 kilobytes of stack space is left. This is not enough to run the Just-in-time compiler (JITter) to compile code that could handle an exception in your program, JITTER requires much more space than this. Thus, the CLR can do nothing but flagrant interruption of the stream. And according to the policy of .NET 2.0, which also completes the process.

Please note that this is not a problem in Java, it has a bytecode interpreter, so there is a guarantee that the user's executable code can work. Or in an unmanaged program written in languages ​​such as C, C ++ or Delphi, the code is generated at build time. However, it is still a very difficult problem to deal with, an emergency place in the stack is blown out, so there is no scenario in which safe code execution in a thread is safe. The likelihood that the program may continue to work correctly with a thread interrupted in a completely random place and in a rather damaged state is unlikely.

If at all there was any effort when considering the issue of an event in another thread or about removing the limit in winapi (the number of protective pages is not configurable), then this is either a very well-kept secret or simply was not considered useful. I suspect that the latter, I do not know, is a fact.

+84
Mar 17 '14 at 23:38
source share

The stack contains almost everything related to the state of the program. The address of each recipient site when calling methods, local variables, method parameters, etc. If a method overflows the stack, its execution stops immediately, if necessary (since there is no more stack space left to continue), then, in order to gracefully restore, someone must clear everything that this method did before the stack before it died. This means knowing what the stack looked like before the method was called. This does some overhead.

And if you cannot clear the stack, you also cannot get the stack trace, because the information needed to generate the trace arises from the "expand" of the stack to discover which methods were called.

+16
Mar 17 '14 at 21:18
source share

In order to correctly handle stack overflow or due to lack of memory, it is necessary to throw an exception before the stack is actually full or the heap memory is completely exhausted, while the available resources of the stack and heap will be adequate to execute any cleaning code that should be launched before exceptions are detected. In the case of exceptions, handling them cleanly basically requires checking the stack pointer for writing for each method (which should not be really expensive). They are usually handled by setting an access violation trap outside the stack, but the problem with this is that the trap does not fire until it is too late to handle things cleanly. It would be possible to catch a trap on the last block of the stack’s memory, rather than one past, and the system would change the trap to a block after the stack after it was launched and throw a StackOverflowException , but the problem there would be a good way to make sure that the “almost from the stack” trap is activated again after the stack has turned so far.

It was said that an alternative approach would be to allow threads to set a delegate for what should happen if a thread hits its stack, and then say that in the case of a StackOverflowException the thread's stack will be cleared and it will run the delegate provided. The trap can be restored before the delegate starts (the stack will be empty at this point), and the code can support a thread status object that the delegate could use to find out if any important finally blocks had passed.

+7
Mar 17 '14 at 22:36
source share



All Articles