ntdll: Add a machine frame to the KiUserCallbackDispatcher stack on x86-64.

This commit is contained in:
Alexandre Julliard 2023-11-30 16:56:23 +01:00
parent 8b24139fa7
commit 0690851c9b
3 changed files with 126 additions and 22 deletions

View file

@ -690,10 +690,8 @@ __ASM_GLOBAL_FUNC( KiUserApcDispatcher,
/*******************************************************************
* KiUserCallbackDispatcher (NTDLL.@)
*
* FIXME: not binary compatible
*/
void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
void WINAPI dispatch_callback( void *args, ULONG len, ULONG id )
{
NTSTATUS status;
@ -711,6 +709,18 @@ void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
RtlRaiseStatus( status );
}
__ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
__ASM_SEH(".seh_pushframe\n\t")
__ASM_SEH(".seh_stackalloc 0x30\n\t")
__ASM_SEH(".seh_endprologue\n\t")
__ASM_CFI(".cfi_signal_frame\n\t")
__ASM_CFI(".cfi_def_cfa_offset 0\n\t")
__ASM_CFI(".cfi_offset %rip,0x30\n\t")
__ASM_CFI(".cfi_offset %rsp,0x48\n\t")
"movq 0x20(%rsp),%rcx\n\t" /* args */
"movl 0x28(%rsp),%edx\n\t" /* len */
"movl 0x2c(%rsp),%r8d\n\t" /* id */
"call " __ASM_NAME("dispatch_callback") )
/**************************************************************************

View file

@ -27,6 +27,7 @@
#include "winbase.h"
#include "winnt.h"
#include "winreg.h"
#include "winuser.h"
#include "winternl.h"
#include "ddk/wdm.h"
#include "excpt.h"
@ -61,6 +62,7 @@ static void * (WINAPI *pRtlLocateExtendedFeature)(CONTEXT_EX *context_ex, ULO
static void * (WINAPI *pRtlLocateLegacyContext)(CONTEXT_EX *context_ex, ULONG *length);
static void (WINAPI *pRtlSetExtendedFeaturesMask)(CONTEXT_EX *context_ex, ULONG64 feature_mask);
static ULONG64 (WINAPI *pRtlGetExtendedFeaturesMask)(CONTEXT_EX *context_ex);
static void * (WINAPI *pRtlPcToFileHeader)(PVOID pc, PVOID *address);
static NTSTATUS (WINAPI *pNtRaiseException)(EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance);
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
@ -80,6 +82,7 @@ static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *fea
static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD);
static void *pKiUserApcDispatcher;
static void *pKiUserCallbackDispatcher;
static void *pKiUserExceptionDispatcher;
#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
@ -5019,6 +5022,82 @@ static void test_KiUserApcDispatcher(void)
VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect );
}
static void WINAPI hook_KiUserCallbackDispatcher(void *rsp)
{
struct
{
ULONG64 padding[4];
void *args;
ULONG len;
ULONG id;
struct machine_frame frame;
BYTE args_data[0];
} *stack = rsp;
NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[stack->id];
trace( "rsp %p args %p (%#Ix) len %lu id %lu\n", stack, stack->args,
(char *)stack->args - (char *)stack, stack->len, stack->id );
trace( "machine frame rip=%#Ix cs=%#Ix eflags=%#Ix rsp=%#Ix (%Ix) ss=%#Ix\n",
stack->frame.rip, stack->frame.cs, stack->frame.eflags, stack->frame.rsp, stack->frame.rsp - (ULONG64)rsp, stack->frame.ss );
ok( !((ULONG_PTR)stack & 15), "unaligned stack %p\n", stack );
ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data );
ok( (BYTE *)stack->frame.rsp - &stack->args_data[stack->len] <= 16, "wrong rsp %p / %p\n",
(void *)stack->frame.rsp, &stack->args_data[stack->len] );
if (stack->frame.rip && pRtlPcToFileHeader)
{
void *mod, *win32u = GetModuleHandleA("win32u.dll");
pRtlPcToFileHeader( (void *)stack->frame.rip, &mod );
if (win32u) ok( mod == win32u, "ret address %Ix not in win32u %p\n", stack->frame.rip, win32u );
else trace( "ret address %Ix in %p\n", stack->frame.rip, mod );
}
NtCallbackReturn( NULL, 0, func( stack->args, stack->len ));
}
static void test_KiUserCallbackDispatcher(void)
{
BYTE hook_trampoline[] =
{
0x48, 0x89, 0xe1, /* mov %rsp,%rcx */
0x48, 0xb8, /* movabs hook_KiUserCallbackDispatcher,%rax */
0,0,0,0,0,0,0,0, /* offset 5 */
0xff, 0xd0, /* callq *rax */
};
BYTE saved_KiUserCallbackDispatcher[12];
BYTE patched_KiUserCallbackDispatcher[12];
DWORD old_protect;
BYTE *ptr;
BOOL ret;
*(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserCallbackDispatcher;
memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher),
PAGE_EXECUTE_READWRITE, &old_protect );
ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
memcpy( saved_KiUserCallbackDispatcher, pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher) );
ptr = patched_KiUserCallbackDispatcher;
/* mov $code_mem, %rax */
*ptr++ = 0x48;
*ptr++ = 0xb8;
*(void **)ptr = code_mem;
ptr += sizeof(ULONG64);
/* jmp *rax */
*ptr++ = 0xff;
*ptr++ = 0xe0;
memcpy( pKiUserCallbackDispatcher, patched_KiUserCallbackDispatcher, sizeof(patched_KiUserCallbackDispatcher) );
DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
memcpy( pKiUserCallbackDispatcher, saved_KiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher));
VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher), old_protect, &old_protect );
}
static BOOL got_nested_exception, got_prev_frame_exception;
static void *nested_exception_initial_frame;
@ -11563,9 +11642,11 @@ START_TEST(exception)
X(RtlLocateLegacyContext);
X(RtlSetExtendedFeaturesMask);
X(RtlGetExtendedFeaturesMask);
X(RtlPcToFileHeader);
X(RtlCopyContext);
X(RtlCopyExtendedContext);
X(KiUserApcDispatcher);
X(KiUserCallbackDispatcher);
X(KiUserExceptionDispatcher);
#undef X
@ -11728,6 +11809,7 @@ START_TEST(exception)
test_wow64_context();
test_KiUserExceptionDispatcher();
test_KiUserApcDispatcher();
test_KiUserCallbackDispatcher();
test_nested_exception();
test_collided_unwind();

View file

@ -375,6 +375,19 @@ struct apc_stack_layout
C_ASSERT( offsetof(struct apc_stack_layout, machine_frame) == 0x4d0 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x500 );
/* stack layout when calling KiUserCallbackDispatcher */
struct callback_stack_layout
{
ULONG64 padding[4]; /* 000 parameter space for called function */
void *args; /* 020 arguments */
ULONG len; /* 028 arguments len */
ULONG id; /* 02c function id */
struct machine_frame machine_frame; /* 030 machine frame */
BYTE args_data[0]; /* 058 copied argument data */
};
C_ASSERT( offsetof(struct callback_stack_layout, machine_frame) == 0x30 );
C_ASSERT( sizeof(struct callback_stack_layout) == 0x58 );
/* flags to control the behavior of the syscall dispatcher */
#define SYSCALL_HAVE_XSAVE 1
#define SYSCALL_HAVE_XSAVEC 2
@ -1550,8 +1563,7 @@ 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( ULONG64 user_rsp, void **ret_ptr, ULONG *ret_len, void *func, TEB *teb );
__ASM_GLOBAL_FUNC( call_user_mode_callback,
"subq $0x48,%rsp\n\t"
__ASM_CFI(".cfi_adjust_cfa_offset 0x48\n\t")
@ -1571,33 +1583,28 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback,
__ASM_CFI(".cfi_rel_offset %r15,-0x28\n\t")
"stmxcsr -0x30(%rbp)\n\t"
"fnstcw -0x2c(%rbp)\n\t"
"movq %rcx,-0x38(%rbp)\n\t" /* ret_ptr */
"movq %r8,-0x40(%rbp)\n\t" /* ret_len */
"mov 0x10(%rbp),%r11\n\t" /* teb */
"movq %rsi,-0x38(%rbp)\n\t" /* ret_ptr */
"movq %rdx,-0x40(%rbp)\n\t" /* ret_len */
"subq $0x408,%rsp\n\t" /* sizeof(struct syscall_frame) + exception */
"andq $~63,%rsp\n\t"
"leaq 0x10(%rbp),%rax\n\t"
"movq %rax,0xa8(%rsp)\n\t" /* frame->syscall_cfa */
"movq 0x328(%r11),%r10\n\t" /* amd64_thread_data()->syscall_frame */
"movq (%r11),%rax\n\t" /* NtCurrentTeb()->Tib.ExceptionList */
"movq 0x328(%r8),%r10\n\t" /* amd64_thread_data()->syscall_frame */
"movq (%r8),%rax\n\t" /* NtCurrentTeb()->Tib.ExceptionList */
"movq %rax,0x408(%rsp)\n\t"
"movl 0xb0(%r10),%r14d\n\t" /* prev_frame->syscall_flags */
"movl %r14d,0xb0(%rsp)\n\t" /* frame->syscall_flags */
"movq %r10,0xa0(%rsp)\n\t" /* frame->prev_frame */
"movq %rsp,0x328(%r11)\n\t" /* amd64_thread_data()->syscall_frame */
"movq %rsp,0x328(%r8)\n\t" /* amd64_thread_data()->syscall_frame */
#ifdef __linux__
"testl $12,%r14d\n\t" /* SYSCALL_HAVE_PTHREAD_TEB | SYSCALL_HAVE_WRFSGSBASE */
"jz 1f\n\t"
"movw 0x338(%r11),%fs\n" /* amd64_thread_data()->fs */
"movw 0x338(%r8),%fs\n" /* amd64_thread_data()->fs */
"1:\n\t"
#endif
"movq %rdi,%rcx\n\t" /* id */
"movq %rdx,%r8\n\t" /* len */
"movq %rsi,%rdx\n\t" /* args */
/* switch to user stack */
"leaq -0x20(%rsi),%rsp\n\t"
"push $0\n\t"
"jmpq *%r9" )
"movq %rdi,%rsp\n\t" /* user_rsp */
"jmpq *%rcx" ) /* func */
/***********************************************************************
@ -1669,14 +1676,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 = amd64_thread_data()->syscall_frame;
void *args_data = (void *)((frame->rsp - len) & ~15);
ULONG64 rsp = (frame->rsp - offsetof( struct callback_stack_layout, args_data[len] )) & ~15;
struct callback_stack_layout *stack = (struct callback_stack_layout *)rsp;
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->args = stack->args_data;
stack->len = len;
stack->id = id;
stack->machine_frame.rip = frame->rip;
stack->machine_frame.rsp = frame->rsp;
memcpy( stack->args_data, args, len );
return call_user_mode_callback( rsp, ret_ptr, ret_len, pKiUserCallbackDispatcher, NtCurrentTeb() );
}