Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/ntdll/signal_i386.c | 9 ++-
dlls/ntdll/tests/exception.c | 111 +++++++++++++++++++++++++++++++++-
dlls/ntdll/unix/signal_i386.c | 9 +--
3 files changed, 121 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 676329c5bd2..aa19c632be2 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_GLOBAL_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..29d0e2adeaf 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,13 @@ 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"
+ "jmp *pKiUserExceptionDispatcher\n\t")
/**********************************************************************
* get_fpu_code
--
2.26.2