Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is __finally supposed to run after EXCEPTION_CONTINUE_SEARCH?

In the following code, the function foo calls itself recursively once. The inner call causes an access violation to be raised. The outer call catches the exception.

#include <windows.h>
#include <stdio.h>

void foo(int cont)
{
    __try
    {
        __try
        {
            __try
            {
                if (!cont)
                    *(int *)0 = 0;
                foo(cont - 1);
            }
            __finally
            {
                printf("inner finally %d\n", cont);
            }
        }
        __except (!cont? EXCEPTION_CONTINUE_SEARCH: EXCEPTION_EXECUTE_HANDLER)
        {
            printf("except %d\n", cont);
        }
    }
    __finally
    {
        printf("outer finally %d\n", cont);
    }
}

int main()
{
    __try
    {
        foo(1);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("main\n");
    }
    return 0;
}

The expected output here should be

inner finally 0
outer finally 0
inner finally 1
except 1
outer finally 1

However, outer finally 0 is conspicuously missing from the real output. Is this a bug or is there some detail I'm overlooking?

For completeness, happens with VS2015, compiling for x64. Surprisingly it doesn't happen on x86, leading me to believe that it is really a bug.

like image 590
avakar Avatar asked Sep 27 '15 08:09

avakar


People also ask

How do I continue execution after an exception?

Continuing Execution After an Exception. When the debugger breaks execution because of an exception, you will see the Exception Helper, by default. If you have disabled the Exception Helper in the Options dialog box, you will see the Exception Assistant (C# or Visual Basic) or the Exception dialog box (C++).

What is exception_continue_search in C++?

The constant EXCEPTION_CONTINUE_SEARCH, EXCEPTION_CONTINUE_EXECUTION, or EXCEPTION_EXECUTE_HANDLER is returned when an exception occurs during execution of the guarded section of a try-except statement. The return value determines how the exception is handled.

How do you fix an unhandled exception?

When the Exception Helper appears, you can try to fix the problem that caused the exception. In managed and native code, you can continue execution in the same thread after an unhandled exception. The Exception Helper unwinds the call stack to the point where the exception was thrown.

How do I enable exception handling in the debugger?

When the debugger breaks execution because of an exception, you will see the Exception Helper, by default. If you have disabled the Exception Helper in the Options dialog box, you will see the Exception Assistant (C# or Visual Basic) or the Exception dialog box (C++).


1 Answers

exist and more simply example (we can remove inner try/finally block:

void foo(int cont)
{
    __try
    {
        __try
        {
            if (!cont) *(int *)0 = 0;
            foo(cont - 1);
        }
        __except (cont? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
        {
            printf("except %d\n", cont);
        }
    }
    __finally
    {
        printf("finally %d\n", cont);
    }
}

with output

except 1
finally 1

so finally 0 block not executed. but in not recursive case - no bug:

__try
{
    foo(0);
} 
__except(EXCEPTION_EXECUTE_HANDLER)
{
    printf("except\n");
}

output:

finally 0
except

this is bug in next function

EXCEPTION_DISPOSITION
__C_specific_handler (
    _In_ PEXCEPTION_RECORD ExceptionRecord,
    _In_ PVOID EstablisherFrame,
    _Inout_ PCONTEXT ContextRecord,
    _Inout_ PDISPATCHER_CONTEXT DispatcherContext
    );

old implementation of this function with bug here :

                    //
                    // try/except - exception filter (JumpTarget != 0).
                    // After the exception filter is called, the exception
                    // handler clause is executed by the call to unwind
                    // above. Having reached this point in the scan of the
                    // scope tables, any other termination handlers will
                    // be outside the scope of the try/except.
                    //

                    if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) { // bug
                        break;
                    }

if we have latest VC compiler/libraries installed, search for chandler.c (in my install in located at \VC\crt\src\amd64\chandler.c )

and in file can view now next code:

                if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget
                    // Terminate only when we are at the Target frame;
                    // otherwise, continue search for outer finally:
                    && IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)
                    ) {
                    break;
                }

so additional condition is added IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags) which fix this bug

__C_specific_handler implemented in different crt libraries (in some case with static link, in some case will be imported from vcruntime*.dll or msvcrt.dll (was forwarded to ntdll.dll)). also ntdll.dll export this function - however in latest win10 builds(14393) it still not fixed

like image 59
RbMm Avatar answered Sep 19 '22 22:09

RbMm