From e60a97c1b45a4b2a15077feb36b1e16abf2d8ef4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 30 Nov 2023 17:43:14 +0100 Subject: [PATCH] ntdll: Fix stack layout for KiUserCallbackDispatcher on i386. --- dlls/ntdll/tests/exception.c | 56 +++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_i386.c | 45 ++++++++++++++++++---------- dlls/wow64/syscall.c | 29 ++++++++++++------ 3 files changed, 105 insertions(+), 25 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index a1dade5bc28..c8647e7e9fa 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -2175,6 +2175,61 @@ static void test_KiUserApcDispatcher(void) VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect ); } +static void CDECL hook_KiUserCallbackDispatcher( void *eip, ULONG id, ULONG *args, ULONG len, + ULONG unk1, ULONG unk2, ULONG arg0, ULONG arg1 ) +{ + NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[id]; + + trace( "eip %p id %lx args %p (%x) len %lx unk1 %lx unk2 %lx args %lx,%lx\n", + eip, id, args, (char *)args - (char *)&eip, len, unk1, unk2, arg0, arg1 ); + + if (args[0] != arg0) /* new style with extra esp */ + { + void *esp = (void *)arg0; + + ok( args[0] == arg1, "wrong arg1 %lx / %lx\n", args[0], arg1 ); + ok( (char *)esp - ((char *)args + len) < 0x10, "wrong esp offset %p / %p\n", esp, args ); + } + + if (eip && pRtlPcToFileHeader) + { + void *mod, *win32u = GetModuleHandleA("win32u.dll"); + + pRtlPcToFileHeader( eip, &mod ); + if (win32u) ok( mod == win32u, "ret address %p not in win32u %p\n", eip, win32u ); + else trace( "ret address %p in %p\n", eip, mod ); + } + NtCallbackReturn( NULL, 0, func( args, len )); +} + +static void test_KiUserCallbackDispatcher(void) +{ + BYTE saved_code[7], patched_code[7]; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code) ); + ptr = patched_code; + /* mov $hook_trampoline, %eax */ + *ptr++ = 0xb8; + *(void **)ptr = hook_KiUserCallbackDispatcher; + ptr += sizeof(void *); + /* call *eax */ + *ptr++ = 0xff; + *ptr++ = 0xd0; + memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code) ); + + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + + memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code)); + VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect ); +} + #elif defined(__x86_64__) #define UNW_FLAG_NHANDLER 0 @@ -12121,6 +12176,7 @@ START_TEST(exception) test_prot_fault(); test_KiUserExceptionDispatcher(); test_KiUserApcDispatcher(); + test_KiUserCallbackDispatcher(); test_extended_context(); test_copy_context(); test_set_live_context(); diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index ebb529116a3..751b0081534 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -465,6 +465,19 @@ struct apc_stack_layout C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x14 ); C_ASSERT( sizeof(struct apc_stack_layout) == 0x308 ); +/* stack layout when calling KiUserCallbackDispatcher */ +struct callback_stack_layout +{ + ULONG eip; /* 000 */ + ULONG id; /* 004 */ + void *args; /* 008 */ + ULONG len; /* 00c */ + ULONG unk[2]; /* 010 */ + ULONG esp; /* 018 */ + BYTE args_data[0]; /* 01c */ +}; +C_ASSERT( sizeof(struct callback_stack_layout) == 0x1c ); + struct syscall_frame { WORD syscall_flags; /* 000 */ @@ -1595,8 +1608,8 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context /*********************************************************************** * call_user_mode_callback */ -extern NTSTATUS call_user_mode_callback( ULONG id, void *args, ULONG len, void **ret_ptr, - ULONG *ret_len, void *func, TEB *teb ); +extern NTSTATUS call_user_mode_callback( ULONG user_esp, void **ret_ptr, ULONG *ret_len, + void *func, TEB *teb ); __ASM_GLOBAL_FUNC( call_user_mode_callback, "pushl %ebp\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") @@ -1609,7 +1622,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, __ASM_CFI(".cfi_rel_offset %esi,-8\n\t") "pushl %edi\n\t" __ASM_CFI(".cfi_rel_offset %edi,-12\n\t") - "movl 0x20(%ebp),%edx\n\t" /* teb */ + "movl 0x18(%ebp),%edx\n\t" /* teb */ "pushl 0(%edx)\n\t" /* teb->Tib.ExceptionList */ "subl $0x380,%esp\n\t" /* sizeof(struct syscall_frame) */ "andl $~63,%esp\n\t" @@ -1620,14 +1633,9 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movl %eax,(%esp)\n\t" "movl %ecx,0x3c(%esp)\n\t" /* frame->prev_frame */ "movl %esp,0x1f8(%edx)\n\t" /* x86_thread_data()->syscall_frame */ - "movl 0x1c(%ebp),%ecx\n\t" /* func */ - "movl 0x0c(%ebp),%edx\n\t" /* args */ + "movl 0x14(%ebp),%ecx\n\t" /* func */ /* switch to user stack */ - "leal -4(%edx),%esp\n\t" - "pushl 0x10(%ebp)\n\t" /* len */ - "pushl %edx\n\t" /* args */ - "pushl 0x08(%ebp)\n\t" /* id */ - "pushl $0\n\t" + "movl 8(%ebp),%esp\n\t" "xorl %ebp,%ebp\n\t" "jmpl *%ecx" ) @@ -1654,9 +1662,9 @@ __ASM_GLOBAL_FUNC( user_mode_callback_return, "movl 8(%esp),%edi\n\t" /* ret_len */ "movl 12(%esp),%eax\n\t" /* status */ "leal -16(%ebp),%esp\n\t" - "movl 0x14(%ebp),%ecx\n\t" /* ret_ptr */ + "movl 0x0c(%ebp),%ecx\n\t" /* ret_ptr */ "movl %esi,(%ecx)\n\t" - "movl 0x18(%ebp),%ecx\n\t" /* ret_len */ + "movl 0x10(%ebp),%ecx\n\t" /* ret_len */ "movl %edi,(%ecx)\n\t" "popl 0(%edx)\n\t" /* teb->Tib.ExceptionList */ "popl %edi\n\t" @@ -1698,14 +1706,19 @@ __ASM_GLOBAL_FUNC( user_mode_abort_thread, NTSTATUS KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len ) { struct syscall_frame *frame = x86_thread_data()->syscall_frame; - void *args_data = (void *)((frame->esp - len) & ~15); + ULONG esp = (frame->esp - offsetof(struct callback_stack_layout, args_data[len])) & ~3; + struct callback_stack_layout *stack = (struct callback_stack_layout *)esp; if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&frame) return STATUS_STACK_OVERFLOW; - memcpy( args_data, args, len ); - return call_user_mode_callback( id, args_data, len, ret_ptr, ret_len, - pKiUserCallbackDispatcher, NtCurrentTeb() ); + stack->eip = frame->eip; + stack->id = id; + stack->args = stack->args_data; + stack->len = len; + stack->esp = frame->esp; + memcpy( stack->args_data, args, len ); + return call_user_mode_callback( esp, ret_ptr, ret_len, pKiUserCallbackDispatcher, NtCurrentTeb() ); } diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c index 3c247d62e0b..2af7bf75de2 100644 --- a/dlls/wow64/syscall.c +++ b/dlls/wow64/syscall.c @@ -1095,20 +1095,31 @@ NTSTATUS WINAPI Wow64KiUserCallbackDispatcher( ULONG id, void *args, ULONG len, { case IMAGE_FILE_MACHINE_I386: { + /* stack layout when calling 32-bit KiUserCallbackDispatcher */ + struct callback_stack_layout32 + { + ULONG eip; /* 000 */ + ULONG id; /* 004 */ + ULONG args; /* 008 */ + ULONG len; /* 00c */ + ULONG unk[2]; /* 010 */ + ULONG esp; /* 018 */ + BYTE args_data[0]; /* 01c */ + } *stack; I386_CONTEXT orig_ctx, ctx = { CONTEXT_I386_FULL }; - void *args_data; - ULONG *stack; + + C_ASSERT( sizeof(struct callback_stack_layout32) == 0x1c ); pBTCpuGetContext( GetCurrentThread(), GetCurrentProcess(), NULL, &ctx ); orig_ctx = ctx; - stack = args_data = ULongToPtr( (ctx.Esp - len) & ~15 ); - memcpy( args_data, args, len ); - *(--stack) = 0; - *(--stack) = len; - *(--stack) = PtrToUlong( args_data ); - *(--stack) = id; - *(--stack) = 0xdeadbabe; + stack = ULongToPtr( (ctx.Esp - offsetof(struct callback_stack_layout32,args_data[len])) & ~15 ); + stack->eip = ctx.Eip; + stack->id = id; + stack->args = PtrToUlong( stack->args_data ); + stack->len = len; + stack->esp = ctx.Esp; + memcpy( stack->args_data, args, len ); ctx.Esp = PtrToUlong( stack ); ctx.Eip = pLdrSystemDllInitBlock->pKiUserCallbackDispatcher;