Module: wine Branch: master Commit: 060a8b4af236b69d131ac49e8f32b9dea073ed9e URL: https://gitlab.winehq.org/wine/wine/-/commit/060a8b4af236b69d131ac49e8f32b9d...
Author: Alexandre Julliard julliard@winehq.org Date: Thu Nov 30 17:15:07 2023 +0100
ntdll: Fix stack layout and unwind information for KiUserApcDispatcher on ARM64.
---
dlls/ntdll/signal_arm64.c | 20 ++++++---- dlls/ntdll/tests/exception.c | 85 ++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_arm64.c | 31 ++++++++++----- 3 files changed, 120 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 985ee28a540..34624f4aa0c 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -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" )
/******************************************************************* diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 7a8090868b8..69bfbf0771d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -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__)
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index ab38d83477f..86bf3c9e349 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -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; }