avakar
avakar

Reputation: 32685

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.

Upvotes: 8

Views: 510

Answers (1)

RbMm
RbMm

Reputation: 33784

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

Upvotes: 1

Related Questions