This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, pablolob, paul.moore, steve.dower, tim.golden, zach.ware
Date 2021-10-29.20:12:22
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1635538342.22.0.758528594619.issue45645@roundup.psfhosted.org>
In-reply-to
Content
In theory, a crash could be prevented in most cases by setting a larger stack guarantee (i.e. region of guard pages) via SetThreadStackGuarantee() [1] and using a vectored exception handler [2]. The exception handler can set a flag in the thread state that indicates stack-overflow recovery is in progress and then return EXCEPTION_CONTINUE_EXECUTION. The guaranteed stack space will be available, but there are no guard pages, so another stack overflow in this context will crash with an access violation. The stack guarantee should be large enough to raise and unwind a RecursionError. As the stack unwinds, if the recovery flag is still set, try calling _resetstkoflw() [3] to restore the guard region. If it succeeds, clear the flag in the thread state.

For giggles, here's a toy example using ctypes:

    import ctypes
    import sys

    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
    ucrt = ctypes.CDLL('ucrtbase', use_errno=True)

    EXCEPTION_CONTINUE_EXECUTION = 0xFFFFFFFF

    stack_overflow = False

    @ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_void_p)
    def handler(p):
        global stack_overflow
        stack_overflow = True
        return EXCEPTION_CONTINUE_EXECUTION

    kernel32.AddVectoredExceptionHandler(1, handler)

    def recursive():
        if stack_overflow:
            raise RecursionError
        recursive()

    # Normally the stack has 2 or 3 guard pages, which is actually
    # enough to recover in this example, but let's increase it to
    # 9 pages (8 plus an extra that the memory manager adds). You
    # can inspect this with Sysinternals VMMap.
    size = ctypes.c_ulong(8 * 4096)
    kernel32.SetThreadStackGuarantee(ctypes.byref(size))

    sys.setrecursionlimit(1000000)

    for n in range(5):
        try:
            recursive()
        except RecursionError:
            if stack_overflow and ucrt._resetstkoflw():
                stack_overflow = False
                print("recovered from stack overflow:", n)

---
[1] https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadstackguarantee
[2] https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-addvectoredexceptionhandler
[3] https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=msvc-160
History
Date User Action Args
2021-10-29 20:12:22eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, pablolob
2021-10-29 20:12:22eryksunsetmessageid: <1635538342.22.0.758528594619.issue45645@roundup.psfhosted.org>
2021-10-29 20:12:22eryksunlinkissue45645 messages
2021-10-29 20:12:22eryksuncreate