Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- include/winnt.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+)
diff --git a/include/winnt.h b/include/winnt.h index 2ab74ab123c..fd17c3464cd 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -915,6 +915,122 @@ NTSYSAPI PSLIST_ENTRY WINAPI RtlInterlockedPushEntrySList(PSLIST_HEADER, PSLIST_ NTSYSAPI WORD WINAPI RtlQueryDepthSList(PSLIST_HEADER);
+/* Fast fail (__fastfail) codes */ + +#define FAST_FAIL_LEGACY_GS_VIOLATION 0 +#define FAST_FAIL_VTGUARD_CHECK_FAILURE 1 +#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2 +#define FAST_FAIL_CORRUPT_LIST_ENTRY 3 +#define FAST_FAIL_INCORRECT_STACK 4 +#define FAST_FAIL_INVALID_ARG 5 +#define FAST_FAIL_GS_COOKIE_INIT 6 +#define FAST_FAIL_FATAL_APP_EXIT 7 +#define FAST_FAIL_RANGE_CHECK_FAILURE 8 +#define FAST_FAIL_UNSAFE_REGISTRY_ACCESS 9 +#define FAST_FAIL_GUARD_ICALL_CHECK_FAILURE 10 +#define FAST_FAIL_GUARD_WRITE_CHECK_FAILURE 11 +#define FAST_FAIL_INVALID_FIBER_SWITCH 12 +#define FAST_FAIL_INVALID_SET_OF_CONTEXT 13 +#define FAST_FAIL_INVALID_REFERENCE_COUNT 14 +#define FAST_FAIL_INVALID_JUMP_BUFFER 18 +#define FAST_FAIL_MRDATA_MODIFIED 19 +#define FAST_FAIL_CERTIFICATION_FAILURE 20 +#define FAST_FAIL_INVALID_EXCEPTION_CHAIN 21 +#define FAST_FAIL_CRYPTO_LIBRARY 22 +#define FAST_FAIL_INVALID_CALL_IN_DLL_CALLOUT 23 +#define FAST_FAIL_INVALID_IMAGE_BASE 24 +#define FAST_FAIL_DLOAD_PROTECTION_FAILURE 25 +#define FAST_FAIL_UNSAFE_EXTENSION_CALL 26 +#define FAST_FAIL_DEPRECATED_SERVICE_INVOKED 27 +#define FAST_FAIL_INVALID_BUFFER_ACCESS 28 +#define FAST_FAIL_INVALID_BALANCED_TREE 29 +#define FAST_FAIL_INVALID_NEXT_THREAD 30 +#define FAST_FAIL_GUARD_ICALL_CHECK_SUPPRESSED 31 +#define FAST_FAIL_APCS_DISABLED 32 +#define FAST_FAIL_INVALID_IDLE_STATE 33 +#define FAST_FAIL_MRDATA_PROTECTION_FAILURE 34 +#define FAST_FAIL_UNEXPECTED_HEAP_EXCEPTION 35 +#define FAST_FAIL_INVALID_LOCK_STATE 36 +#define FAST_FAIL_GUARD_JUMPTABLE 37 +#define FAST_FAIL_INVALID_LONGJUMP_TARGET 38 +#define FAST_FAIL_INVALID_DISPATCH_CONTEXT 39 +#define FAST_FAIL_INVALID_THREAD 40 +#define FAST_FAIL_INVALID_SYSCALL_NUMBER 41 +#define FAST_FAIL_INVALID_FILE_OPERATION 42 +#define FAST_FAIL_LPAC_ACCESS_DENIED 43 +#define FAST_FAIL_GUARD_SS_FAILURE 44 +#define FAST_FAIL_LOADER_CONTINUITY_FAILURE 45 +#define FAST_FAIL_GUARD_EXPORT_SUPPRESSION_FAILURE 46 +#define FAST_FAIL_INVALID_CONTROL_STACK 47 +#define FAST_FAIL_SET_CONTEXT_DENIED 48 +#define FAST_FAIL_INVALID_IAT 49 +#define FAST_FAIL_HEAP_METADATA_CORRUPTION 50 +#define FAST_FAIL_PAYLOAD_RESTRICTION_VIOLATION 51 +#define FAST_FAIL_LOW_LABEL_ACCESS_DENIED 52 +#define FAST_FAIL_ENCLAVE_CALL_FAILURE 53 +#define FAST_FAIL_UNHANDLED_LSS_EXCEPTON 54 +#define FAST_FAIL_ADMINLESS_ACCESS_DENIED 55 +#define FAST_FAIL_UNEXPECTED_CALL 56 +#define FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS 57 +#define FAST_FAIL_UNEXPECTED_HOST_BEHAVIOR 58 +#define FAST_FAIL_FLAGS_CORRUPTION 59 +#define FAST_FAIL_VEH_CORRUPTION 60 +#define FAST_FAIL_ETW_CORRUPTION 61 +#define FAST_FAIL_RIO_ABORT 62 +#define FAST_FAIL_INVALID_PFN 63 +#define FAST_FAIL_GUARD_ICALL_CHECK_FAILURE_XFG 64 +#define FAST_FAIL_CAST_GUARD 65 +#define FAST_FAIL_HOST_VISIBILITY_CHANGE 66 +#define FAST_FAIL_KERNEL_CET_SHADOW_STACK_ASSIST 67 +#define FAST_FAIL_PATCH_CALLBACK_FAILED 68 +#define FAST_FAIL_NTDLL_PATCH_FAILED 69 +#define FAST_FAIL_INVALID_FLS_DATA 70 +#define FAST_FAIL_INVALID_FAST_FAIL_CODE 0xFFFFFFFF + +#if defined(__GNUC__) +#if defined(__x86_64__) +static FORCEINLINE DECLSPEC_NORETURN void __fastfail(unsigned int code) +{ + register ULONGLONG val __asm__("rcx") = code; + __asm__ __volatile__( "int $0x29" :: "r" (val) : "memory" ); +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) + __builtin_unreachable(); +#endif +} +#elif defined(__i386__) +static FORCEINLINE DECLSPEC_NORETURN void __fastfail(unsigned int code) +{ + register ULONG val __asm__("ecx") = code; + __asm__ __volatile__( "int $0x29" :: "r" (val) : "memory" ); +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) + __builtin_unreachable(); +#endif +} +#elif defined(__aarch64__) +static FORCEINLINE DECLSPEC_NORETURN void __fastfail(unsigned int code) +{ + register ULONGLONG val __asm__("x0") = code; + __asm__ __volatile__( "brk #0xf003" :: "r" (val) : "memory" ); +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) + __builtin_unreachable(); +#endif +} +#elif defined(__arm__) +static FORCEINLINE DECLSPEC_NORETURN void __fastfail(unsigned int code) +{ + register ULONG val __asm__("r0") = code; + __asm__ __volatile__( "udf #0xfb" :: "r" (val) : "memory" ); +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) + __builtin_unreachable(); +#endif +} +#endif /* __x86_64__ */ +#elif defined(_MSC_VER) && (_MSC_VER >= 1610) +DECLSPEC_NORETURN void __fastfail(unsigned int); +#pragma intrinsic(__fastfail) +#endif /* __GNUC__ */ + + /* Heap flags */
#define HEAP_NO_SERIALIZE 0x00000001
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/ntdll/tests/exception.c | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index dd4f4a35344..c0d3ae19a51 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8380,6 +8380,91 @@ static void test_ripevent(DWORD numexc) pRtlRemoveVectoredExceptionHandler(vectored_handler); }
+static void subtest_fastfail(unsigned int code) +{ + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + DEBUG_EVENT de; + DWORD continuestatus; + BOOL ret; + BOOL had_ff = FALSE, had_se = FALSE; + + sprintf(cmdline, "%s %s %s %u", my_argv[0], my_argv[1], "fastfail", code); + si.cb = sizeof(si); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "could not create child process error: %u\n", GetLastError()); + if (!ret) + return; + + do + { + continuestatus = DBG_CONTINUE; + ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + + if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) + { + if (de.u.Exception.ExceptionRecord.ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) + { + ok(!de.u.Exception.dwFirstChance, "must be a second chance exception\n"); + ok(de.u.Exception.ExceptionRecord.NumberParameters == 1, "expected exactly one parameter, got %u\n", + de.u.Exception.ExceptionRecord.NumberParameters); + ok(de.u.Exception.ExceptionRecord.ExceptionInformation[0] == code, "expected %u for code, got %Iu\n", + code, de.u.Exception.ExceptionRecord.ExceptionInformation[0]); + had_ff = TRUE; + } + + if (de.u.Exception.dwFirstChance) + { + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + { + had_se = TRUE; + pNtTerminateProcess(pi.hProcess, 0); + } + } + + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); + + } 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 ); + ret = CloseHandle(pi.hThread); + ok(ret, "error %u\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "error %u\n", GetLastError()); + + return; +} + +static void test_fastfail(void) +{ + unsigned int codes[] = { + FAST_FAIL_LEGACY_GS_VIOLATION, + FAST_FAIL_VTGUARD_CHECK_FAILURE, + FAST_FAIL_STACK_COOKIE_CHECK_FAILURE, + FAST_FAIL_CORRUPT_LIST_ENTRY, + FAST_FAIL_INCORRECT_STACK, + FAST_FAIL_INVALID_ARG, + FAST_FAIL_GS_COOKIE_INIT, + FAST_FAIL_FATAL_APP_EXIT, + FAST_FAIL_INVALID_FAST_FAIL_CODE, + 0xdeadbeefUL, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(codes); i++) + { + winetest_push_context("__fastfail(%#x)", codes[i]); + subtest_fastfail(codes[i]); + winetest_pop_context(); + } +} + static DWORD breakpoint_exceptions;
static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo) @@ -10611,6 +10696,13 @@ START_TEST(exception) if (my_argc >= 4) { void *addr; + + if (strcmp(my_argv[2], "fastfail") == 0) + { + __fastfail(strtoul(my_argv[3], NULL, 0)); + return; + } + sscanf( my_argv[3], "%p", &addr );
if (addr != &test_stage) @@ -10749,6 +10841,7 @@ START_TEST(exception) test_thread_context(); test_outputdebugstring(1, FALSE); test_ripevent(1); + test_fastfail(); test_breakpoint(1); test_closehandle(0, (HANDLE)0xdeadbeef); /* Call of Duty WWII writes to BeingDebugged then closes an invalid handle,
This is required to implement some instructions that raise second chance exceptions directly, such as those emitted from the __fastfail() intrinsic function.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v1 -> v2: proper patch splitting
dlls/ntdll/unix/signal_arm.c | 18 ++++++++++++------ dlls/ntdll/unix/signal_arm64.c | 22 ++++++++++++++-------- dlls/ntdll/unix/signal_i386.c | 21 ++++++++++++++------- dlls/ntdll/unix/signal_x86_64.c | 18 ++++++++++++------ dlls/ntdll/unix/thread.c | 28 +++++++++++++++++++--------- dlls/ntdll/unix/unix_private.h | 1 + 6 files changed, 72 insertions(+), 36 deletions(-)
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index ebc08984adf..dbc2770f72e 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -576,7 +576,7 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline, * * Modify the signal context to call the exception raise function. */ -static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) +static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, BOOL first_chance ) { struct { @@ -591,13 +591,19 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) rec->ExceptionAddress = (void *)PC_sig(sigcontext); save_context( &context, sigcontext );
- status = send_debug_event( rec, &context, TRUE ); + status = send_debug_event( rec, &context, first_chance ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( &context, sigcontext ); return; }
+ if (!first_chance) + { + handle_second_chance_exception( rec ); + return; + } + stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec ); stack->rec = *rec; stack->context = context; @@ -834,7 +840,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) break; } if (handle_syscall_fault( context, &rec )) return; - setup_exception( context, &rec ); + setup_exception( context, &rec, TRUE ); }
@@ -858,7 +864,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.NumberParameters = 1; break; } - setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -915,7 +921,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION; break; } - setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -944,7 +950,7 @@ static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { EXCEPTION_WINE_ASSERTION, EH_NONCONTINUABLE };
- setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 680a14223c6..ef70d4df44b 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -618,7 +618,7 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline, * * Modify the signal context to call the exception raise function. */ -static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) +static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, BOOL first_chance ) { struct { @@ -634,13 +634,19 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) rec->ExceptionAddress = (void *)PC_sig(sigcontext); save_context( &context, sigcontext );
- status = send_debug_event( rec, &context, TRUE ); + status = send_debug_event( rec, &context, first_chance ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( &context, sigcontext ); return; }
+ if (!first_chance) + { + handle_second_chance_exception( rec ); + return; + } + stack = virtual_setup_exception( stack_ptr, (sizeof(*stack) + 15) & ~15, rec ); stack->rec = *rec; stack->context = context; @@ -861,7 +867,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) (void *)SP_sig(context) ); if (!rec.ExceptionCode) return; if (handle_syscall_fault( context, &rec )) return; - setup_exception( context, &rec ); + setup_exception( context, &rec, TRUE ); }
@@ -874,7 +880,7 @@ static void ill_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { EXCEPTION_ILLEGAL_INSTRUCTION };
- setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -887,7 +893,7 @@ static void bus_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { EXCEPTION_DATATYPE_MISALIGNMENT };
- setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -911,7 +917,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.NumberParameters = 1; break; } - setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -968,7 +974,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION; break; } - setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
@@ -997,7 +1003,7 @@ static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { EXCEPTION_WINE_ASSERTION, EH_NONCONTINUABLE };
- setup_exception( sigcontext, &rec ); + setup_exception( sigcontext, &rec, TRUE ); }
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index bf3abc1a587..e18aed2c222 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1400,7 +1400,8 @@ static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *r * Change context to setup a call to a raise exception function. */ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, - EXCEPTION_RECORD *rec, struct xcontext *xcontext ) + EXCEPTION_RECORD *rec, struct xcontext *xcontext, + BOOL first_chance ) { CONTEXT *context = &xcontext->c; size_t stack_size; @@ -1420,7 +1421,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) );
- NTSTATUS status = send_debug_event( rec, context, TRUE ); + NTSTATUS status = send_debug_event( rec, context, first_chance );
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { @@ -1428,6 +1429,12 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) return; }
+ if (!first_chance) + { + handle_second_chance_exception( rec ); + return; + } + /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--;
@@ -1482,7 +1489,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) struct xcontext xcontext; void *stack = setup_exception_record( sigcontext, rec, &xcontext );
- setup_raise_exception( sigcontext, stack, rec, &xcontext ); + setup_raise_exception( sigcontext, stack, rec, &xcontext, TRUE ); }
/* stack layout when calling an user apc function. @@ -1675,7 +1682,7 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo rec->ExceptionInformation[0] = context->Eax; rec->ExceptionInformation[1] = context->Ecx; rec->ExceptionInformation[2] = context->Edx; - setup_raise_exception( sigcontext, stack, rec, xcontext ); + setup_raise_exception( sigcontext, stack, rec, xcontext, TRUE ); return TRUE; default: return FALSE; @@ -1848,7 +1855,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) break; } if (handle_syscall_fault( ucontext, stack, &rec, &xcontext.c )) return; - setup_raise_exception( ucontext, stack, &rec, &xcontext ); + setup_raise_exception( ucontext, stack, &rec, &xcontext, TRUE ); }
@@ -1894,7 +1901,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.ExceptionInformation[2] = 0; /* FIXME */ break; } - setup_raise_exception( sigcontext, stack, &rec, &xcontext ); + setup_raise_exception( sigcontext, stack, &rec, &xcontext, TRUE ); }
@@ -1939,7 +1946,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION; break; } - setup_raise_exception( sigcontext, stack, &rec, &xcontext ); + setup_raise_exception( sigcontext, stack, &rec, &xcontext, TRUE ); }
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 9e9a96db832..ff4a40c7933 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2110,7 +2110,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) /*********************************************************************** * setup_raise_exception */ -static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext ) +static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext, BOOL first_chance ) { void *stack_ptr = (void *)(RSP_sig(sigcontext) & ~15); CONTEXT *context = &xcontext->c; @@ -2136,13 +2136,19 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec context->EFlags &= ~0x100; /* clear single-step flag */ }
- status = send_debug_event( rec, context, TRUE ); + status = send_debug_event( rec, context, first_chance ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( xcontext, sigcontext ); return; }
+ if (!first_chance) + { + handle_second_chance_exception( rec ); + return; + } + /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--;
@@ -2193,7 +2199,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
rec->ExceptionAddress = (void *)RIP_sig(sigcontext); save_context( &context, sigcontext ); - setup_raise_exception( sigcontext, rec, &context ); + setup_raise_exception( sigcontext, rec, &context, TRUE ); }
@@ -2461,7 +2467,7 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r default: return FALSE; } - setup_raise_exception( sigcontext, rec, xcontext ); + setup_raise_exception( sigcontext, rec, xcontext, TRUE ); return TRUE; }
@@ -2614,7 +2620,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) break; } if (handle_syscall_fault( sigcontext, &rec, &context.c )) return; - setup_raise_exception( sigcontext, &rec, &context ); + setup_raise_exception( sigcontext, &rec, &context, TRUE ); }
@@ -2658,7 +2664,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) rec.ExceptionInformation[0] = 0; break; } - setup_raise_exception( sigcontext, &rec, &context ); + setup_raise_exception( sigcontext, &rec, &context, TRUE ); }
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 455272e07e0..d7204b2fd3b 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -87,6 +87,24 @@ static inline int get_unix_exit_code( NTSTATUS status ) }
+/*********************************************************************** + * handle_second_chance_exception + * + * Handle a second chance exception. + */ +void handle_second_chance_exception( EXCEPTION_RECORD *rec ) +{ + if (rec->ExceptionFlags & EH_STACK_INVALID) + ERR_(seh)("Exception frame is not in stack limits => unable to dispatch exception.\n"); + else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION) + ERR_(seh)("Process attempted to continue execution after noncontinuable exception.\n"); + else + ERR_(seh)("Unhandled exception code %x flags %x addr %p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + + NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); +} + /*********************************************************************** * fpux_to_fpu * @@ -1509,15 +1527,7 @@ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL
if (first_chance) return call_user_exception_dispatcher( rec, context );
- if (rec->ExceptionFlags & EH_STACK_INVALID) - ERR_(seh)("Exception frame is not in stack limits => unable to dispatch exception.\n"); - else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION) - ERR_(seh)("Process attempted to continue execution after noncontinuable exception.\n"); - else - ERR_(seh)("Unhandled exception code %x flags %x addr %p\n", - rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); - - NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); + handle_second_chance_exception( rec ); return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 0dcb09ad641..d5414474d2e 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -172,6 +172,7 @@ extern void server_init_process_done(void) DECLSPEC_HIDDEN; extern void server_init_thread( void *entry_point, BOOL *suspend ) DECLSPEC_HIDDEN; extern int server_pipe( int fd[2] ) DECLSPEC_HIDDEN;
+void handle_second_chance_exception( EXCEPTION_RECORD *rec ); extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux ) DECLSPEC_HIDDEN; extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu ) DECLSPEC_HIDDEN; extern void *get_cpu_area( USHORT machine ) DECLSPEC_HIDDEN;
__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 | 14 ++++++++++++-- dlls/ntdll/unix/signal_arm64.c | 11 +++++++++++ dlls/ntdll/unix/signal_i386.c | 7 +++++++ dlls/ntdll/unix/signal_x86_64.c | 7 +++++++ 5 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c0d3ae19a51..14b1396dbc2 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8429,7 +8429,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 dbc2770f72e..1816e210644 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -809,13 +809,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); + setup_exception( context, &rec, FALSE ); + 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 ef70d4df44b..fe80732c03d 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -905,6 +905,17 @@ 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; + + if ((get_fault_esr( context ) & 0xfe00ffffU) == 0xf200f003U) + { + rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec.ExceptionFlags = EH_NONCONTINUABLE; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = REGn_sig( 0, context ); + setup_exception( sigcontext, &rec, FALSE ); + return; + }
switch (siginfo->si_code) { diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index e18aed2c222..6d448b99223 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1660,6 +1660,13 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
switch(interrupt) { + case 0x29: + rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec->ExceptionFlags = EH_NONCONTINUABLE; + rec->NumberParameters = 1; + rec->ExceptionInformation[0] = context->Ecx; + setup_raise_exception( sigcontext, stack, rec, xcontext, FALSE ); + 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 ff4a40c7933..39bf1bcc835 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2441,6 +2441,13 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
switch (ERROR_sig(sigcontext) >> 3) { + case 0x29: + rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + rec->ExceptionFlags = EH_NONCONTINUABLE; + rec->NumberParameters = 1; + rec->ExceptionInformation[0] = context->Rcx; + setup_raise_exception( sigcontext, rec, xcontext, FALSE ); + return TRUE; case 0x2c: rec->ExceptionCode = STATUS_ASSERTION_FAILURE; break;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=103597
Your paranoid android.
=== w10pro64_ja (64 bit report) ===
ntdll: exception.c:4370: Test failed: cs32: ecx f9070000 / FEA0352CF9070000 exception.c:4405: Test failed: cs32: got eip 775e2c0c / 775e2c0c
Today, the UDF instruction handler code assumes Thumb mode code, and cannot recognise the UDF.W form or equivalent instructions in ARM mode encoding.
Fix this by generalising the UDF instruction parser code.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/ntdll/unix/signal_arm.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 1816e210644..f4987f902e0 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -359,6 +359,35 @@ static inline WORD get_error_code( const ucontext_t *sigcontext ) }
+/*********************************************************************** + * get_udf_immediate + * + * Get the immediate operand if the PC is at a UDF instruction. + */ +static inline int get_udf_immediate( const ucontext_t *sigcontext ) +{ + if (CPSR_sig(sigcontext) & 0x20) + { + WORD thumb_insn = *(WORD *)PC_sig(sigcontext); + if ((thumb_insn >> 8) == 0xde) return thumb_insn & 0xff; + if ((thumb_insn & 0xfff0) == 0xf7f0) /* udf.w */ + { + WORD ext = *(WORD *)(PC_sig(sigcontext) + 2); + if ((ext & 0xf000) == 0xa000) return ((thumb_insn & 0xf) << 12) | (ext & 0x0fff); + } + } + else + { + DWORD arm_insn = *(DWORD *)PC_sig(sigcontext); + if ((arm_insn & 0xfff000f0) == 0xe7f000f0) + { + return ((arm_insn >> 4) & 0xfff0) | (arm_insn & 0xf); + } + } + return -1; +} + + /*********************************************************************** * save_context * @@ -809,16 +838,16 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) switch (get_trap_code(signal, context)) { case TRAP_ARM_PRIVINFLT: /* Invalid opcode exception */ - switch (*(WORD *)PC_sig(context)) + switch (get_udf_immediate( context )) { - case 0xdefb: /* __fastfail */ + case 0xfb: /* __fastfail */ rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; rec.ExceptionFlags = EH_NONCONTINUABLE; rec.NumberParameters = 1; rec.ExceptionInformation[0] = REGn_sig(0, context); setup_exception( context, &rec, FALSE ); return; - case 0xdefe: /* breakpoint */ + case 0xfe: /* breakpoint */ rec.ExceptionCode = EXCEPTION_BREAKPOINT; rec.NumberParameters = 1; break;