[PATCH v3 1/2] ntdll: Fix KiUserExceptionDispatcher ABI on x86.

Paul Gofman pgofman at codeweavers.com
Fri Jul 3 05:22:23 CDT 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v3:
    - pass dispatcher function address to call_user_exception_dispatcher.

 dlls/ntdll/signal_i386.c        |   9 ++-
 dlls/ntdll/tests/exception.c    | 111 +++++++++++++++++++++++++++++++-
 dlls/ntdll/unix/signal_i386.c   |  10 ++-
 dlls/ntdll/unix/signal_x86_64.c |   6 +-
 dlls/ntdll/unix/thread.c        |   2 +-
 dlls/ntdll/unix/unix_private.h  |   3 +-
 6 files changed, 129 insertions(+), 12 deletions(-)

diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 676329c5bd2..594c6f9a1d1 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -198,7 +198,7 @@ static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
 /*******************************************************************
  *		KiUserExceptionDispatcher (NTDLL.@)
  */
-NTSTATUS WINAPI KiUserExceptionDispatcher( EXCEPTION_RECORD *rec, CONTEXT *context )
+NTSTATUS WINAPI dispatch_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
 {
     NTSTATUS status;
     DWORD c;
@@ -243,6 +243,13 @@ NTSTATUS WINAPI KiUserExceptionDispatcher( EXCEPTION_RECORD *rec, CONTEXT *conte
     return NtRaiseException( rec, context, FALSE );
 }
 
+__ASM_STDCALL_FUNC( KiUserExceptionDispatcher, 8,
+                  /* hotpatch prologue. */
+                  "push %ebp\n\t"
+                  "mov %esp,%ebp\n\t"
+                  "pop %ebp\n\t"
+                  "call " __ASM_NAME("dispatch_exception") "\n\t"
+                  "int3")
 
 /***********************************************************************
  *           save_fpu
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 95d25375f77..a27dee21f00 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -1660,6 +1660,114 @@ static void test_thread_context(void)
 #undef COMPARE
 }
 
+static BYTE saved_KiUserExceptionDispatcher_bytes[7];
+static void *pKiUserExceptionDispatcher;
+static BOOL hook_called;
+static void *hook_KiUserExceptionDispatcher_eip;
+static void *dbg_except_continue_handler_eip;
+static void *hook_exception_address;
+
+static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+        CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
+{
+    ok(hook_called, "Hook was not called.\n");
+    got_exception = 1;
+    dbg_except_continue_handler_eip = (void *)context->Eip;
+    ++context->Eip;
+    return ExceptionContinueExecution;
+}
+
+/* Use CDECL to leave arguments on stack. */
+void CDECL hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context)
+{
+    trace("rec %p, context %p.\n", rec, context);
+    trace("context->Eip %#x, context->Esp %#x, ContextFlags %#x.\n",
+            context->Eip, context->Esp, context->ContextFlags);
+
+    hook_called = TRUE;
+    /* Broken on Win2008, probably rec offset in stack is different. */
+    ok(rec->ExceptionCode == 0x80000003 || broken(!rec->ExceptionCode),
+            "Got unexpected ExceptionCode %#x.\n", rec->ExceptionCode);
+
+    hook_KiUserExceptionDispatcher_eip = (void *)context->Eip;
+    hook_exception_address = rec->ExceptionAddress;
+    memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
+            sizeof(saved_KiUserExceptionDispatcher_bytes));
+}
+
+static void test_kiuserexceptiondispatcher(void)
+{
+    HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+    static const BYTE except_code[] =
+    {
+        0xcc,  /* int3 */
+        0xc3,  /* ret  */
+    };
+    static BYTE hook_trampoline[] =
+    {
+        0xff, 0x15,
+        /* offset: 2 bytes */
+        0x00, 0x00, 0x00, 0x00,     /* callq *addr */ /* call hook implementation. */
+
+        0xff, 0x25,
+        /* offset: 8 bytes */
+        0x00, 0x00, 0x00, 0x00,     /* jmpq *addr */ /* jump to original function. */
+    };
+    void *phook_KiUserExceptionDispatcher = hook_KiUserExceptionDispatcher;
+    void *phook_trampoline = hook_trampoline;
+    DWORD old_protect1, old_protect2;
+    BYTE *ptr;
+    BOOL ret;
+
+    pKiUserExceptionDispatcher = (void *)GetProcAddress(hntdll, "KiUserExceptionDispatcher");
+    if (!pKiUserExceptionDispatcher)
+    {
+        win_skip("KiUserExceptionDispatcher is not available.\n");
+        return;
+    }
+
+    *(unsigned int *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserExceptionDispatcher;
+    *(unsigned int *)(hook_trampoline + 8) = (ULONG_PTR)&pKiUserExceptionDispatcher;
+
+    ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), PAGE_EXECUTE_READWRITE, &old_protect1);
+    ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+    ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
+            PAGE_EXECUTE_READWRITE, &old_protect2);
+    ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+    memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher,
+            sizeof(saved_KiUserExceptionDispatcher_bytes));
+    ptr = (BYTE *)pKiUserExceptionDispatcher;
+    /* mov hook_trampoline, %eax */
+    *ptr++ = 0xa1;
+    *(void **)ptr = &phook_trampoline;
+    ptr += sizeof(void *);
+    /* jmp *eax */
+    *ptr++ = 0xff;
+    *ptr++ = 0xe0;
+
+    got_exception = 0;
+    run_exception_test(dbg_except_continue_handler, NULL, except_code, ARRAY_SIZE(except_code),
+            PAGE_EXECUTE_READ);
+    ok(got_exception, "Handler was not called.\n");
+    ok(hook_called, "Hook was not called.\n");
+
+    ok(hook_exception_address == code_mem || broken(!hook_exception_address) /* Win2008 */,
+            "Got unexpected exception address %p, expected %p.\n",
+            hook_exception_address, code_mem);
+    todo_wine ok(hook_KiUserExceptionDispatcher_eip == code_mem, "Got unexpected exception address %p, expected %p.\n",
+            hook_KiUserExceptionDispatcher_eip, code_mem);
+    ok(dbg_except_continue_handler_eip == code_mem, "Got unexpected exception address %p, expected %p.\n",
+            dbg_except_continue_handler_eip, code_mem);
+
+    ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
+            old_protect2, &old_protect2);
+    ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+    ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), old_protect1, &old_protect1);
+    ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+}
+
 #elif defined(__x86_64__)
 
 #define is_wow64 0
@@ -3751,7 +3859,6 @@ START_TEST(exception)
     test_suspend_thread();
     test_suspend_process();
     test_unload_trace();
-    test_kiuserexceptiondispatcher();
 
     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
       test_dynamic_unwind();
@@ -3766,5 +3873,7 @@ START_TEST(exception)
 
 #endif
 
+    test_kiuserexceptiondispatcher();
+
     VirtualFree(code_mem, 0, MEM_RELEASE);
 }
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c
index 730d0b1cb32..d9cbec2c811 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -428,7 +428,6 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 /* stack layout when calling an exception raise function */
 struct stack_layout
 {
-    void             *ret_addr;      /* return address from raise_generic_exception */
     EXCEPTION_RECORD *rec_ptr;       /* first arg for raise_generic_exception */
     CONTEXT          *context_ptr;   /* second arg for raise_generic_exception */
     CONTEXT           context;
@@ -1583,15 +1582,14 @@ static void setup_raise_exception( ucontext_t *sigcontext, struct stack_layout *
     FS_sig(sigcontext)  = get_fs();
     GS_sig(sigcontext)  = get_gs();
     SS_sig(sigcontext)  = get_ds();
-    stack->ret_addr     = (void *)0xdeadbabe;  /* KiUserExceptionDispatcher must not return */
     stack->rec_ptr      = &stack->rec;         /* arguments for KiUserExceptionDispatcher */
     stack->context_ptr  = &stack->context;
 }
 
-void WINAPI call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context )
-{
-    pKiUserExceptionDispatcher( rec, context );
-}
+__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
+                   "add $4,%esp\n\t"
+                   "mov 0x8(%esp),%eax\n\t"
+                   "jmp *%eax")
 
 /**********************************************************************
  *		get_fpu_code
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index cb0fdfb00f1..406a4dacf3c 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -1977,15 +1977,17 @@ __ASM_GLOBAL_FUNC( user_exception_dispatcher_trampoline,
                    "movq 0xb0(%rsp),%rdi\n\t"
                    "jmpq *%rdx")
 
-void WINAPI do_call_user_exception_dispatcher(EXCEPTION_RECORD *rec, CONTEXT *context, struct stack_layout *stack)
+void WINAPI do_call_user_exception_dispatcher(EXCEPTION_RECORD *rec, CONTEXT *context,
+        struct stack_layout *stack, void *dispatcher_func)
 {
     memmove(&stack->context, context, sizeof(*context));
     memcpy(&stack->rec, rec, sizeof(*rec));
 
-    user_exception_dispatcher_trampoline( stack, pKiUserExceptionDispatcher );
+    user_exception_dispatcher_trampoline( stack, dispatcher_func );
 }
 
 __ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
+                   "movq %r8,%r9\n\t" /* dispatcher_func parameter */
                    "movq 0x98(%rdx),%r8\n\t" /* context->Rsp */
                    "andq $~0xf,%r8\n\t"
                    "subq $0x630,%r8\n\t" /* sizeof(struct stack_layout) */
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index 686d3991b7d..41d4fe296e6 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -415,7 +415,7 @@ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL
     if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
         NtSetContextThread( GetCurrentThread(), context );
 
-    if (first_chance) call_user_exception_dispatcher( rec, context );
+    if (first_chance) call_user_exception_dispatcher( rec, context, pKiUserExceptionDispatcher );
 
     if (rec->ExceptionFlags & EH_STACK_INVALID)
         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index e6c8d5764ed..fbf550c60bd 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -241,7 +241,8 @@ extern void init_cpu_info(void) DECLSPEC_HIDDEN;
 
 extern void dbg_init(void) DECLSPEC_HIDDEN;
 
-extern void WINAPI call_user_exception_dispatcher(EXCEPTION_RECORD *rec, CONTEXT *context) DECLSPEC_HIDDEN;
+extern void WINAPI call_user_exception_dispatcher(EXCEPTION_RECORD *rec, CONTEXT *context,
+        void *dispatcher_func) DECLSPEC_HIDDEN;
 
 #define TICKSPERSEC 10000000
 #define SECS_1601_TO_1970  ((369 * 365 + 89) * (ULONGLONG)86400)
-- 
2.26.2




More information about the wine-devel mailing list