From cfb579dd67c597711ae291ebe7fbb8ee9a9cd2e1 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 10 Mar 2020 22:06:19 +0200 Subject: [PATCH] [PSEH3] Fix unlinking of EH frames during unwind. On Windows the function that calls the language specific exception handler function registers it's own exception handler, so the top of the exception chain does not point to our own handler. We need to take that into account when unwinding the stack and removing each handler as we go. --- sdk/lib/pseh/i386/pseh3.c | 60 ++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/sdk/lib/pseh/i386/pseh3.c b/sdk/lib/pseh/i386/pseh3.c index 35db77d58b0..7b74100e03f 100644 --- a/sdk/lib/pseh/i386/pseh3.c +++ b/sdk/lib/pseh/i386/pseh3.c @@ -37,6 +37,8 @@ #include #include +#define ASSERT(exp) if (!(exp)) __int2c(); + #include "pseh3.h" #include "pseh3_asmdef.h" @@ -66,10 +68,49 @@ __attribute__((regparm(1))) _SEH3$_Unregister( volatile SEH3$_REGISTRATION_FRAME *Frame) { - if (Frame->Handler) - _SEH3$_UnregisterFrame(Frame); + volatile SEH3$_REGISTRATION_FRAME *CurrentFrame; + + /* Check if the frame has a handler (then it's the registered frame) */ + if (Frame->Handler != NULL) + { + /* There shouldn't be any more nested try-level frames */ + ASSERT(Frame->EndOfChain == Frame); + + /* During unwinding on Windows ExecuteHandler2 installs it's own EH frame, + so there can be one or even multiple frames before our own one and we + need to search for the link that points to our head frame. */ + CurrentFrame = (SEH3$_REGISTRATION_FRAME*)__readfsdword(0); + if (Frame == CurrentFrame) + { + /* The target frame is the first installed frame, remove it */ + __writefsdword(0, (ULONG)Frame->Next); + return; + } + + /* Loop to find the frame that links the target frame */ + while (CurrentFrame->Next != Frame) + { + CurrentFrame = CurrentFrame->Next; + } + + /* Remove the frame from the linked list */ + CurrentFrame->Next = Frame->Next; + } else - _SEH3$_UnregisterTryLevel(Frame); + { + /* Search for the head frame */ + CurrentFrame = Frame->Next; + while (CurrentFrame->Handler == NULL) + { + CurrentFrame = CurrentFrame->Next; + } + + /* Make sure the head frame points to our frame */ + ASSERT(CurrentFrame->EndOfChain == Frame); + + /* Remove this frame from the internal linked list */ + CurrentFrame->EndOfChain = Frame->Next; + } } static inline @@ -146,10 +187,7 @@ __attribute__((regparm(1))) _SEH3$_AutoCleanup( volatile SEH3$_REGISTRATION_FRAME *Frame) { - if (Frame->Handler) - _SEH3$_UnregisterFrame(Frame); - else - _SEH3$_UnregisterTryLevel(Frame); + _SEH3$_Unregister(Frame); /* Check for __finally frames */ if (Frame->ScopeTable->Target == NULL) @@ -167,12 +205,6 @@ _SEH3$_GetFilterResult( PVOID Filter = Record->ScopeTable->Filter; LONG Result; - /* Check for __finally frames */ - if (Record->ScopeTable->Target == NULL) - { - return EXCEPTION_CONTINUE_SEARCH; - } - /* Check if we have a constant filter */ if (((ULONG)Filter & 0xFFFFFF00) == 0) { @@ -278,7 +310,7 @@ _SEH3$_except_handler( ExceptionPointers.ContextRecord = ContextRecord; /* Check if this is an unwind */ - if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING) + if (IS_UNWINDING(ExceptionRecord->ExceptionFlags)) { /* Unwind all local frames */ TargetFrame = EstablisherFrame->Next;