From: Jinoh Kang jinoh.kang.kr@gmail.com
__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 --- 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 d8f5d44123a..a2c47ce3120 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8467,7 +8467,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 1fea76f6563..27ee5425156 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 1df97f16f13..34194421a4b 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 e2a6148d609..18939d6d2ed 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1500,6 +1500,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 @@ -1676,6 +1698,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 34334f72ff0..99b14c6649b 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2237,6 +2237,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 */ @@ -2483,6 +2505,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;