1

I have read many articles about SEH exceptions in StackOverflow and CodeProject.net.

After I implemented SEH exceptions handling in my C++ program, I was affected by stack overflow exception, which hadn't been caught by my software.

After next part of research I understand, that it's impossible to detect such exception programmatically, because we don't have free stack address space to use, so program memory is corrupted.

I would like to ask you about your experience in handling stack overflow exception. It looks like a challenge and I'm really interested if it's not possible in unmanaged code programming languages?

Below I present a part of my sample program (C++), which reproduces stack overflow exception. It works perfectly for any SEH exception, but not stack overflow:

LONG WINAPI SehHandler(PEXCEPTION_POINTERS pExceptionPtrs)
{ 
    cerr << "Handled SEH exception!\n";
    cerr << "ContextRecord: " << pExceptionPtrs->ContextRecord << endl;
    cerr << "ExceptionRecord: " << pExceptionPtrs->ExceptionRecord << endl;

    // Write minidump file
    CreateMiniDump(pExceptionPtrs);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1); 

    return EXCEPTION_EXECUTE_HANDLER;
}

int fib(unsigned int n) {
    if(n == 0) return 0;
    if(n == 1) return 1;
    return fib(n-1)+fib(n-2);
}

int main(){
    SetUnhandledExceptionFilter(SehHandler); 
    cout << fib(1000000);
    return 0;
}
Viper
  • 597
  • 1
  • 8
  • 24

2 Answers2

9

Yes, you can get a minidump out of a SO crash, but never the way you are doing it now. Your SehHandler() function runs on the thread that triggered the exception. And it is in a perilous state, you have about ~7080 bytes of emergency stack space left to do what you need to do. If you consume that then the program will fail with an uncatchable access violation exception.

You cannot call MiniDumpWriteDump() and hope to survive it, that function requires more stack than you have available. So it is a hard kaboom without a minidump.

You need another thread to make that call. That could be, for example, a thread that you create at initialization and block with a WaitForMultipleObjects() call. Your SehHandler() can call SetEvent() to wake it up. After writing the PEXCEPTION_POINTERS value to a global variable. And block indefinitely to allow the thread to create the minidump and abort the process.

Fwiw, by far the best place for that thread is in another process. That also allows you to deal with the really nasty ones that completely corrupt the process state. A "guard" process that you start at initialization. With a named event to signal it and, say, a memory-mapped file to pass the PEXCEPTION_POINTERS. Don't start it in SehHandler(), the process heap is no longer reliable so CreateProcess() cannot work anymore, you have to do it early.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Could you please clarify about that "~7080 bytes of emergency stack space left"? Where is it and how can one use it? – n0p Nov 25 '16 at 16:18
  • 2
    Hans, I understand why you didn't respond to n0p, because it's obvious where the emergency space is (on the stack) and how to use it (automatic variables in the exception filter and functions it calls). But inquiring minds would like to know where that 7080 byte number came from... – Ben Voigt May 09 '17 at 14:03
2

The answer from Hans Passant indicates that there's 7080 bytes of emergency stack. I don't know where that info comes from, and he hasn't answered @nop above, and my findings indicate that that info is incorrect. However this site won't allow me to comment above for some reason, so I'll just leave this here...

There's a function that can be used to query and set how much emergency stack is left to the stack handler: SetThreadStackGuarantee(). Note that since Windows 10 definitely (but I think it's since Windows 7 as well), in most cases this value will be 0. So there's no way to do anything complicated in the handler, by default. You may be able to signal another thread or an external process, as Hans has suggested, but that's all.

However, if you don't want to implement such a complicated solution, and can spare some slack space on the stack, it is easiest to use SetThreadStackGuarantee() to set this to a value high enough that you can continue handling the stack overflow exception just like any other. Note that you need to call this function on each thread that needs this feature, and call it before the stack overflow occurs, so preferably at thread initialization.

AlenL
  • 418
  • 3
  • 7
  • To be fair, n0p didn't ask Hans where the information came from. – Ben Voigt May 05 '17 at 14:21
  • He did ask for a clarification, and he didn't get any. And the information seems to be incorrect. I wanted to comment up there and ask about it because I want to know where Hans got it from. However, this site seems to be more interested in funny points and medals than actual correctness and sources of information. So I'm not able to comment there and in now way am able to tag Hans or point out that his accepted answer needs clarification, correction and citation. – AlenL May 07 '17 at 05:29
  • n0p asked how you use stack space. Well, he asked how you use "emergency stack space", as if it needed special access. Since you use it like any other stack space, it's not really a valid question. – Ben Voigt May 07 '17 at 05:32
  • If you have a comment you'd like left to Hans, I'd be happy to do so. You clearly aren't the clueless gimme-teh-codes type, or spammer, which are the reasons for that 50 reputation minimum to leave comments (waived for comments on your own post) – Ben Voigt May 07 '17 at 05:34
  • FWIW, the solution Hans recommends of performing the work in another thread or process is the correct one. The active thread may be in a terrible state (for example, thread-local storage values used by the language runtime may be in a transient state). You have to remember that the exception handler may be running in context of a library function that wasn't designed to be reentrant. Similarly, the process could be in trouble (for example if the global heap lock is taken), which is why an out-of-process handler is the safest. – Ben Voigt May 07 '17 at 05:39
  • Absolutely, doing work in another process is ideal but it is a lot of work to implement. Here, as it is often the case in my experience, OP has a situation where he already has in-situ handling implemented, but it doesn't cover the stack overflow exception. In that case, it is easiest to just use SetThreadStackGuarantee(). It's not the perfect solution, but in real work you often don't have time for perfect solutions. – AlenL May 09 '17 at 07:29
  • Yes, please comment to Hans and ask where he got the 7080 bytes number. Also, I'd suggest an edit to add reference to SetThreadStackGuarantee() which can be used to query (and change) the actual number. That's a much better approach than using arbitrary "magic numbers" like 7080. Thanks! – AlenL May 09 '17 at 07:33