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.
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++).
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.
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.
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++).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With