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

This commit is contained in:
Alexandre Julliard 2023-11-30 17:15:07 +01:00
parent 9bbdf6c7b5
commit 060a8b4af2
3 changed files with 120 additions and 16 deletions

View file

@ -587,13 +587,19 @@ __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
/*******************************************************************
* KiUserApcDispatcher (NTDLL.@)
*/
void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
PNTAPCFUNC apc )
{
void (CALLBACK *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*) = (void *)apc;
func( arg1, arg2, arg3, context );
NtContinue( context, TRUE );
}
__ASM_GLOBAL_FUNC( KiUserApcDispatcher,
__ASM_SEH(".seh_context\n\t")
"nop\n\t"
__ASM_SEH(".seh_stackalloc 0x30\n\t")
__ASM_SEH(".seh_endprologue\n\t")
"ldp x16, x0, [sp]\n\t" /* func, arg1 */
"ldp x1, x2, [sp, #0x10]\n\t" /* arg2, arg3 */
"add x3, sp, #0x30\n\t" /* context (FIXME) */
"blr x16\n\t"
"add x0, sp, #0x30\n\t" /* context */
"ldr w1, [sp, #0x20]\n\t" /* alertable */
"bl " __ASM_NAME("NtContinue") "\n\t"
"brk #1" )
/*******************************************************************

View file

@ -8666,6 +8666,90 @@ static void test_KiUserExceptionDispatcher(void)
VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_code), old_protect, &old_protect);
}
static UINT apc_count;
static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
{
ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 );
ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 );
ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 );
apc_count++;
}
static void * WINAPI hook_KiUserApcDispatcher(void *stack)
{
struct
{
void *func;
ULONG64 args[3];
ULONG64 alertable;
ULONG64 align;
CONTEXT context;
} *args = stack;
trace( "stack=%p func=%p args=%Ix,%Ix,%Ix alertable=%Ix context=%p pc=%Ix sp=%Ix (%Ix)\n",
args, args->func, args->args[0], args->args[1], args->args[2],
args->alertable, &args->context, args->context.Pc, args->context.Sp,
args->context.Sp - (ULONG_PTR)stack );
ok( args->func == apc_func, "wrong func %p / %p\n", args->func, apc_func );
ok( args->args[0] == 0x1234 + apc_count, "wrong arg1 %Ix\n", args->args[0] );
ok( args->args[1] == 0x5678, "wrong arg2 %Ix\n", args->args[1] );
ok( args->args[2] == 0xdeadbeef, "wrong arg3 %Ix\n", args->args[2] );
if (apc_count) args->alertable = FALSE;
pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef );
hook_called = TRUE;
memcpy( pKiUserApcDispatcher, saved_code, sizeof(saved_code));
FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(saved_code));
return pKiUserApcDispatcher;
}
static void test_KiUserApcDispatcher(void)
{
ULONG hook_trampoline[] =
{
0x910003e0, /* mov x0, sp */
0x5800006f, /* ldr x15, 1f */
0xd63f01e0, /* blr x15 */
0xd61f0000, /* br x0 */
0, 0, /* 1: hook_KiUserApcDispatcher */
};
DWORD old_protect;
BOOL ret;
*(void **)&hook_trampoline[4] = hook_KiUserApcDispatcher;
memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code),
PAGE_EXECUTE_READWRITE, &old_protect );
ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
memcpy( saved_code, pKiUserApcDispatcher, sizeof(saved_code) );
*(void **)&patched_code[3] = code_mem;
memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) );
FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(patched_code));
hook_called = FALSE;
apc_count = 0;
pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
SleepEx( 0, TRUE );
ok( apc_count == 2, "APC count %u\n", apc_count );
ok( hook_called, "hook was not called\n" );
memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) );
FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(patched_code));
pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
SleepEx( 0, TRUE );
ok( apc_count == 3, "APC count %u\n", apc_count );
SleepEx( 0, TRUE );
ok( apc_count == 4, "APC count %u\n", apc_count );
VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code), old_protect, &old_protect );
}
#endif /* __aarch64__ */
#if defined(__i386__) || defined(__x86_64__)
@ -11929,6 +12013,7 @@ START_TEST(exception)
test_continue();
test_virtual_unwind();
test_KiUserExceptionDispatcher();
test_KiUserApcDispatcher();
#elif defined(__arm__)

View file

@ -138,6 +138,19 @@ struct exc_stack_layout
C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x390 );
C_ASSERT( sizeof(struct exc_stack_layout) == 0x440 );
/* stack layout when calling KiUserApcDispatcher */
struct apc_stack_layout
{
void *func; /* 000 APC to call*/
ULONG64 args[3]; /* 008 function arguments */
ULONG64 alertable; /* 020 */
ULONG64 align; /* 028 */
CONTEXT context; /* 030 */
ULONG64 redzone[2]; /* 3c0 */
};
C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x30 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x3d0 );
struct syscall_frame
{
ULONG64 x[29]; /* 000 */
@ -979,7 +992,7 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a
{
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
ULONG64 sp = context ? context->Sp : frame->sp;
struct apc_stack_layout { CONTEXT context; } *stack;
struct apc_stack_layout *stack;
sp &= ~15;
stack = (struct apc_stack_layout *)sp - 1;
@ -994,14 +1007,14 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a
NtGetContextThread( GetCurrentThread(), &stack->context );
stack->context.X0 = status;
}
frame->sp = (ULONG64)stack;
frame->pc = (ULONG64)pKiUserApcDispatcher;
frame->x[0] = (ULONG64)&stack->context;
frame->x[1] = arg1;
frame->x[2] = arg2;
frame->x[3] = arg3;
frame->x[4] = (ULONG64)func;
frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER;
stack->func = func;
stack->args[0] = arg1;
stack->args[1] = arg2;
stack->args[2] = arg3;
frame->sp = (ULONG64)stack;
frame->pc = (ULONG64)pKiUserApcDispatcher;
frame->restore_flags |= CONTEXT_CONTROL;
syscall_frame_fixup_for_fastpath( frame );
return status;
}