__fastfail() is used by the Visual C++ runtime and Windows system libraries to signal that the in-process state is corrupted and unrecoverable.
If __fastfail() is invoked, the NT kernel raises a second-chance non-continuable exception STATUS_STACK_BUFFER_OVERRUN. This quickly terminates the process, bypassing all in-process exception handlers (since they all rely on the potentially corrupted process state).
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v3 -> v4: fix BRK #0xF003 detection on ARM64 add comments v4 -> v5: use NtRaiseException() v5 -> v6: - don't special case __fastfail in i386 and x86_64. - fix ERR() call spacing issues.
dlls/ntdll/tests/exception.c | 1 - dlls/ntdll/unix/signal_arm.c | 40 +++++++++++++++++++++++++++++++-- dlls/ntdll/unix/signal_arm64.c | 40 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_i386.c | 30 +++++++++++++++++++++++++ dlls/ntdll/unix/signal_x86_64.c | 30 +++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 488a5e94729..71d25fa3e93 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8446,7 +8446,6 @@ static void subtest_fastfail(unsigned int code)
} while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
- todo_wine ok(had_ff || broken(had_se) /* Win7 */, "fast fail did not occur\n");
wait_child_process( pi.hProcess ); diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index a1bcb0ddd32..a3c98cdb85a 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -615,6 +615,32 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) }
+/*********************************************************************** + * raise_second_chance_exception + * + * Raise a second chance exception. + */ +static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) +{ + CONTEXT context; + + rec->ExceptionAddress = (void *)PC_sig(sigcontext); + if (is_inside_syscall( sigcontext )) + { + /* Windows would bug check here */ + ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); + } + else + { + save_context( &context, sigcontext ); + NtRaiseException( rec, &context, FALSE ); + restore_context( &context, sigcontext ); + } +} + + /*********************************************************************** * call_user_apc_dispatcher */ @@ -812,13 +838,23 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) switch (get_trap_code(signal, context)) { case TRAP_ARM_PRIVINFLT: /* Invalid opcode exception */ - if (*(WORD *)PC_sig(context) == 0xdefe) /* breakpoint */ + switch (*(WORD *)PC_sig(context)) { + case 0xdefb: /* __fastfail */ + rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec.ExceptionFlags = EH_NONCONTINUABLE; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = REGn_sig( 0, context ); + raise_second_chance_exception( context, &rec ); + return; + case 0xdefe: /* breakpoint */ rec.ExceptionCode = EXCEPTION_BREAKPOINT; rec.NumberParameters = 1; break; + default: + rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; + break; } - rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; break; case TRAP_ARM_PAGEFLT: /* Page fault */ rec.NumberParameters = 2; diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 94f280e218a..c3ab1aeb599 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -656,6 +656,32 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) }
+/*********************************************************************** + * raise_second_chance_exception + * + * Raise a second chance exception. + */ +static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) +{ + CONTEXT context; + + rec->ExceptionAddress = (void *)PC_sig(sigcontext); + if (is_inside_syscall( sigcontext )) + { + /* Windows would bug check here */ + ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); + } + else + { + save_context( &context, sigcontext ); + NtRaiseException( rec, &context, FALSE ); + restore_context( &context, sigcontext ); + } +} + + /*********************************************************************** * call_user_apc_dispatcher */ @@ -908,6 +934,7 @@ static void bus_handler( int signal, siginfo_t *siginfo, void *sigcontext ) static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { 0 }; + ucontext_t *context = sigcontext;
switch (siginfo->si_code) { @@ -916,6 +943,19 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) break; case TRAP_BRKPT: default: + /* debug exceptions do not update ESR on Linux, so we fetch the instruction directly. */ + if (!(PSTATE_sig( context ) & 0x10) && /* AArch64 (not WoW) */ + !(PC_sig( context ) & 3) && + *(ULONG *)PC_sig( context ) == 0xd43e0060UL) /* brk #0xf003 */ + { + /* __fastfail */ + rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec.ExceptionFlags = EH_NONCONTINUABLE; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = REGn_sig( 0, context ); + raise_second_chance_exception( context, &rec ); + return; + } rec.ExceptionCode = EXCEPTION_BREAKPOINT; rec.NumberParameters = 1; break; diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 6bb5649e2b5..3b3ba83c463 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1485,6 +1485,28 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) setup_raise_exception( sigcontext, stack, rec, &xcontext ); }
+/*********************************************************************** + * raise_second_chance_exception + * + * Raise a second chance exception. + */ +static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext ) +{ + if (is_inside_syscall( sigcontext )) + { + /* Windows would bug check here */ + WINE_ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); + } + else + { + NtRaiseException( rec, &xcontext->c, FALSE ); + restore_context( xcontext, sigcontext ); + } +} + + /* stack layout when calling an user apc function. * FIXME: match Windows ABI. */ struct apc_stack_layout @@ -1661,6 +1683,14 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
switch(interrupt) { + case 0x29: + /* __fastfail: process state is corrupted */ + rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec->ExceptionFlags = EH_NONCONTINUABLE; + rec->NumberParameters = 1; + rec->ExceptionInformation[0] = ECX_sig( sigcontext ); + raise_second_chance_exception( sigcontext, rec, xcontext ); + return TRUE; case 0x2d: if (!is_wow64) { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 68e0c7ce66e..e06ac71bb4c 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2198,6 +2198,28 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) }
+/*********************************************************************** + * raise_second_chance_exception + * + * Raise a second chance exception. + */ +static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext ) +{ + if (is_inside_syscall( sigcontext )) + { + /* Windows would bug check here */ + ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); + } + else + { + NtRaiseException( rec, &xcontext->c, FALSE ); + restore_context( xcontext, sigcontext ); + } +} + + /*********************************************************************** * call_user_apc_dispatcher */ @@ -2444,6 +2466,14 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
switch (ERROR_sig(sigcontext) >> 3) { + case 0x29: + /* __fastfail: process state is corrupted */ + rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec->ExceptionFlags = EH_NONCONTINUABLE; + rec->NumberParameters = 1; + rec->ExceptionInformation[0] = RCX_sig( sigcontext ); + raise_second_chance_exception( sigcontext, rec, xcontext ); + return TRUE; case 0x2c: rec->ExceptionCode = STATUS_ASSERTION_FAILURE; break;