ntdll: Fix stack layout and unwind information for KiUserCallbackDispatcher on ARM64.

This commit is contained in:
Alexandre Julliard 2023-11-30 17:18:13 +01:00
parent 060a8b4af2
commit d6bd264460
3 changed files with 106 additions and 19 deletions

View file

@ -605,7 +605,7 @@ __ASM_GLOBAL_FUNC( KiUserApcDispatcher,
/*******************************************************************
* KiUserCallbackDispatcher (NTDLL.@)
*/
void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
void WINAPI dispatch_callback( void *args, ULONG len, ULONG id )
{
NTSTATUS status;
@ -623,6 +623,17 @@ void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
RtlRaiseStatus( status );
}
__ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
__ASM_SEH(".seh_pushframe\n\t")
"nop\n\t"
__ASM_SEH(".seh_stackalloc 0x20\n\t")
"nop\n\t"
__ASM_SEH(".seh_save_reg lr, 0x18\n\t")
__ASM_SEH(".seh_endprologue\n\t")
"ldr x0, [sp]\n\t" /* args */
"ldp w1, w2, [sp, #0x08]\n\t" /* len, id */
"bl " __ASM_NAME("dispatch_callback") "\n\t"
"brk #1" )
/***********************************************************************

View file

@ -8750,6 +8750,60 @@ static void test_KiUserApcDispatcher(void)
VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code), old_protect, &old_protect );
}
static void WINAPI hook_KiUserCallbackDispatcher(void *sp)
{
struct
{
void *args;
ULONG len;
ULONG id;
ULONG64 unknown;
ULONG64 lr;
ULONG64 sp;
ULONG64 pc;
BYTE args_data[0];
} *stack = sp;
ULONG_PTR redzone = (BYTE *)stack->sp - &stack->args_data[stack->len];
NTSTATUS (WINAPI *func)(void *, ULONG) = ((void **)NtCurrentTeb()->Peb->KernelCallbackTable)[stack->id];
trace( "stack=%p len=%lx id=%lx unk=%Ix lr=%Ix sp=%Ix pc=%Ix\n",
stack, stack->len, stack->id, stack->unknown, stack->lr, stack->sp, stack->pc );
ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data );
ok( redzone >= 16 && redzone <= 32, "wrong sp %p / %p (%Iu)\n",
(void *)stack->sp, stack->args_data, redzone );
if (pRtlPcToFileHeader)
{
void *mod, *win32u = GetModuleHandleA("win32u.dll");
pRtlPcToFileHeader( (void *)stack->pc, &mod );
ok( mod == win32u, "pc %Ix not in win32u %p\n", stack->pc, win32u );
}
NtCallbackReturn( NULL, 0, func( stack->args, stack->len ));
}
void test_KiUserCallbackDispatcher(void)
{
DWORD old_protect;
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));
*(void **)&patched_code[3] = hook_KiUserCallbackDispatcher;
memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code));
FlushInstructionCache(GetCurrentProcess(), pKiUserCallbackDispatcher, sizeof(patched_code));
DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code));
FlushInstructionCache(GetCurrentProcess(), pKiUserCallbackDispatcher, sizeof(saved_code));
VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect );
}
#endif /* __aarch64__ */
#if defined(__i386__) || defined(__x86_64__)
@ -12014,6 +12068,7 @@ START_TEST(exception)
test_virtual_unwind();
test_KiUserExceptionDispatcher();
test_KiUserApcDispatcher();
test_KiUserCallbackDispatcher();
#elif defined(__arm__)

View file

@ -151,6 +151,21 @@ struct apc_stack_layout
C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x30 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x3d0 );
/* stack layout when calling KiUserCallbackDispatcher */
struct callback_stack_layout
{
void *args; /* 000 arguments */
ULONG len; /* 008 arguments len */
ULONG id; /* 00c function id */
ULONG64 unknown; /* 010 */
ULONG64 lr; /* 018 */
ULONG64 sp; /* 020 sp+pc (machine frame) */
ULONG64 pc; /* 028 */
BYTE args_data[0]; /* 030 copied argument data*/
};
C_ASSERT( offsetof(struct callback_stack_layout, sp) == 0x20 );
C_ASSERT( sizeof(struct callback_stack_layout) == 0x30 );
struct syscall_frame
{
ULONG64 x[29]; /* 000 */
@ -1053,8 +1068,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( ULONG64 user_sp, void **ret_ptr, ULONG *ret_len,
void *func, TEB *teb );
__ASM_GLOBAL_FUNC( call_user_mode_callback,
"stp x29, x30, [sp,#-0xc0]!\n\t"
__ASM_CFI(".cfi_def_cfa_offset 0xc0\n\t")
@ -1081,22 +1096,22 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback,
"stp d10, d11, [x29, #0x70]\n\t"
"stp d12, d13, [x29, #0x80]\n\t"
"stp d14, d15, [x29, #0x90]\n\t"
"stp x3, x4, [x29, #0xa0]\n\t" /* ret_ptr, ret_len */
"mov x18, x6\n\t" /* teb */
"mrs x3, fpcr\n\t"
"mrs x4, fpsr\n\t"
"bfi x3, x4, #0, #32\n\t"
"ldr x4, [x18]\n\t" /* teb->Tib.ExceptionList */
"stp x3, x4, [x29, #0xb0]\n\t"
"stp x1, x2, [x29, #0xa0]\n\t" /* ret_ptr, ret_len */
"mov x18, x4\n\t" /* teb */
"mrs x1, fpcr\n\t"
"mrs x2, fpsr\n\t"
"bfi x1, x2, #0, #32\n\t"
"ldr x2, [x18]\n\t" /* teb->Tib.ExceptionList */
"stp x1, x2, [x29, #0xb0]\n\t"
"ldr x7, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
"sub x3, sp, #0x330\n\t" /* sizeof(struct syscall_frame) */
"str x3, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
"sub x1, sp, #0x330\n\t" /* sizeof(struct syscall_frame) */
"str x1, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
"add x8, x29, #0xc0\n\t"
"stp x7, x8, [x3, #0x110]\n\t" /* frame->prev_frame,syscall_cfa */
"stp x7, x8, [x1, #0x110]\n\t" /* frame->prev_frame,syscall_cfa */
/* switch to user stack */
"mov sp, x1\n\t" /* stack */
"br x5" )
"mov sp, x0\n\t" /* user_sp */
"br x3" )
/***********************************************************************
@ -1186,14 +1201,20 @@ __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 = arm64_thread_data()->syscall_frame;
void *args_data = (void *)((frame->sp - len) & ~15);
ULONG64 sp = (frame->sp - offsetof( struct callback_stack_layout, args_data[len] ) - 16) & ~15;
struct callback_stack_layout *stack = (struct callback_stack_layout *)sp;
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->lr = frame->lr;
stack->sp = frame->sp;
stack->pc = frame->pc;
memcpy( stack->args_data, args, len );
return call_user_mode_callback( sp, ret_ptr, ret_len, pKiUserCallbackDispatcher, NtCurrentTeb() );
}