Search code examples
c++exceptionc++17visual-studio-2019stack-overflow

Is it possible to recover from stack overflow exception and continue executing the next instruction after handling the exception?


For example, this code will throw Stack Overflow because of infinite recursion which will lead to the program crash. Is there a way to handle this exception and avoid the crash and move on to the next instruction execution?

#include <iostream>
#include <exception>

template <typename T>
T sum(T firstValue, T secondValue)
{
        try{
                return firstValue + secondValue;
        }

        catch(const std::exception &e)
        {
                std::cerr << "Caught exception: " << e.what() << '\n';
        }
}

void causeStackOverflow() {
    causeStackOverflow();
}

int main() {

        std::cout << "Sum of 3 & 4 is: " << sum(3, 4) << '\n';

    try {
        causeStackOverflow();
    }

    catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << '\n'; // If anything can be done here so program can recover and next line can execute?
    }

        std::cout << "Sum of 1 & 2 is: " << sum(1, 2) << '\n';

    return 0;
}

There must be some way which can be used here to do this. Another question would be if it should be done even if it's possible?

Is there any way which can predict with some probability that Stack Overflow is going to happen? So we can take some action to avoid that?


Solution

  • On Windows, with Microsoft Visual C++, you can handle a stack overflow using Structured Exception Handling (SEH) like this:

    void causeStackOverflow() {
        causeStackOverflow();
    }
    
    // Filter for the stack overflow exception.
    // This function traps the stack overflow exception, but passes
    // all other exceptions through.
    int stack_overflow_exception_filter(int exception_code)
    {
       if (exception_code == EXCEPTION_STACK_OVERFLOW)
       {
           // Do not call _resetstkoflw here, because
           // at this point, the stack isn't yet unwound.
           // Instead, signal that the handler (the __except block)
           // is to be executed.
           return EXCEPTION_EXECUTE_HANDLER;
       }
       else
           return EXCEPTION_CONTINUE_SEARCH;
    }
    
    int main() {
        // Demonstrate handling a stack overflow 3 times.
        for (int i = 0; i < 3; ++i) {
            __try {
                causeStackOverflow();
            }
            __except (stack_overflow_exception_filter(GetExceptionCode())) {
                std::cout << "Handled!\n";
                if (!_resetstkoflw()) {
                    // Failed to reset the stack.  Emergency exit.
                    exit(-1);
                }
            }
        }
    }
    

    Important: A stack overflow exception damages the guard page that will catch any subsequent stack overflow exceptions. You have to put these guard pages back by calling _resetstkoflw if you want to be able to recover from another future stack overflow exception on that same thread. I strongly suggest reading the entire remarks section in the documentation.

    The _resetstkoflw function recovers from a stack overflow condition, allowing a program to continue instead of failing with a fatal exception error. If the _resetstkoflw function isn't called, there are no guard pages after the previous exception. The next time that there's a stack overflow, there are no exceptions at all and the process terminates without warning.

    ...