Reputation: 32685
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
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