Module: wine Branch: master Commit: 75d0d466ec98830ccd96afc587c180cd8a9c61ec URL: https://gitlab.winehq.org/wine/wine/-/commit/75d0d466ec98830ccd96afc587c180c...
Author: Alexandre Julliard julliard@winehq.org Date: Sun Dec 3 14:16:42 2023 +0100
ntdll: Fix stack layout for KiUserApcDispatcher on ARM.
---
dlls/ntdll/signal_arm.c | 29 +++++++++++--- dlls/ntdll/tests/exception.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_arm.c | 34 +++++++++------- 3 files changed, 137 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 63e058213a8..ece5eaeebf0 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -547,12 +547,29 @@ __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher, /******************************************************************* * KiUserApcDispatcher (NTDLL.@) */ -void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2, - PNTAPCFUNC func ) -{ - func( ctx, arg1, arg2 ); - NtContinue( context, TRUE ); -} +__ASM_GLOBAL_FUNC( KiUserApcDispatcher, + __ASM_SEH(".seh_custom 0xee,0x02\n\t") /* MSFT_OP_CONTEXT */ + "nop\n\t" + __ASM_SEH(".seh_stackalloc 0x18\n\t") + __ASM_SEH(".seh_endprologue\n\t") + __ASM_EHABI(".save {sp}\n\t") /* Restore Sp last */ + __ASM_EHABI(".pad #-(0x80 + 0x0c + 0x0c)\n\t") /* Move back across D0-D15, Cpsr, Fpscr, Padding, Pc, Lr and Sp */ + __ASM_EHABI(".vsave {d8-d15}\n\t") + __ASM_EHABI(".pad #0x40\n\t") /* Skip past D0-D7 */ + __ASM_EHABI(".pad #0x0c\n\t") /* Skip past Cpsr, Fpscr and Padding */ + __ASM_EHABI(".save {lr, pc}\n\t") + __ASM_EHABI(".pad #0x08\n\t") /* Skip past R12 and Sp - Sp is restored last */ + __ASM_EHABI(".save {r4-r11}\n\t") + __ASM_EHABI(".pad #0x2c\n\t") /* Skip past args, ContextFlags and R0-R3 */ + "ldr r0, [sp, #0x04]\n\t" /* arg1 */ + "ldr r1, [sp, #0x08]\n\t" /* arg2 */ + "ldr r2, [sp, #0x0c]\n\t" /* arg3 */ + "ldr ip, [sp]\n\t" /* func */ + "blx ip\n\t" + "add r0, sp, #0x18\n\t" /* context */ + "ldr r1, [sp, #0x10]\n\t" /* alertable */ + "bl " __ASM_NAME("NtContinue") "\n\t" + "udf #1" )
/******************************************************************* diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c34deedcab1..8f360dca529 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -7445,6 +7445,98 @@ static void test_KiUserExceptionDispatcher(void) VirtualProtect(code_ptr, sizeof(saved_code), old_protect, &old_protect); }
+static UINT apc_count; +static UINT alertable_supported; + +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; + ULONG args[3]; + ULONG alertable; + ULONG align; + CONTEXT context; + } *args = stack; + CONTEXT *context = &args->context; + + if (args->alertable == 1) alertable_supported = TRUE; + else context = (CONTEXT *)&args->alertable; + + trace( "stack=%p func=%p args=%lx,%lx,%lx alertable=%lx context=%p pc=%lx sp=%lx (%lx)\n", + args, args->func, args->args[0], args->args[1], args->args[2], + args->alertable, context, context->Pc, context->Sp, + 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 %lx\n", args->args[0] ); + ok( args->args[1] == 0x5678, "wrong arg2 %lx\n", args->args[1] ); + ok( args->args[2] == 0xdeadbeef, "wrong arg3 %lx\n", args->args[2] ); + + if (apc_count && alertable_supported) args->alertable = FALSE; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef ); + + hook_called = TRUE; + memcpy( code_ptr, saved_code, sizeof(saved_code)); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(saved_code)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + WORD hook_trampoline[] = + { + 0x4668, /* mov r0, sp */ + 0xf8df, 0xc006, /* ldr.w r12, [pc, #0x6] */ + 0x47e0, /* blx r12 */ + 0x4700, /* bx r0 */ + 0, 0, /* 1: hook_KiUserApcDispatcher */ + }; + DWORD old_protect; + BOOL ret; + + code_ptr = (void *)(((ULONG_PTR)pKiUserApcDispatcher) & ~1); /* mask thumb bit */ + *(void **)&hook_trampoline[5] = hook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( code_ptr, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, code_ptr, sizeof(saved_code) ); + *(void **)&patched_code[4] = (char *)code_mem + 1; /* thumb */ + memcpy( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, 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( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code)); + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + if (alertable_supported) + { + ok( apc_count == 3, "APC count %u\n", apc_count ); + SleepEx( 0, TRUE ); + } + ok( apc_count == 4, "APC count %u\n", apc_count ); + + VirtualProtect( code_ptr, sizeof(saved_code), old_protect, &old_protect ); +} + #elif defined(__aarch64__)
#define UNW_FLAG_NHANDLER 0 @@ -12348,6 +12440,7 @@ START_TEST(exception)
test_virtual_unwind(); test_KiUserExceptionDispatcher(); + test_KiUserApcDispatcher();
#endif
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 9aa2994290e..fb31cecd1ad 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -184,6 +184,19 @@ struct exc_stack_layout C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x1a0 ); C_ASSERT( sizeof(struct exc_stack_layout) == 0x1f8 );
+/* stack layout when calling KiUserApcDispatcher */ +struct apc_stack_layout +{ + void *func; /* 000 APC to call*/ + ULONG args[3]; /* 004 function arguments */ + ULONG alertable; /* 010 */ + ULONG align; /* 014 */ + CONTEXT context; /* 018 */ + ULONG redzone[2]; /* 1b8 */ +}; +C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x18 ); +C_ASSERT( sizeof(struct apc_stack_layout) == 0x1c0 ); + struct syscall_frame { UINT r0; /* 000 */ @@ -1071,14 +1084,9 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a { struct syscall_frame *frame = arm_thread_data()->syscall_frame; ULONG sp = context ? context->Sp : frame->sp; - struct apc_stack_layout - { - void *func; - void *align; - CONTEXT context; - } *stack; + struct apc_stack_layout *stack;
- sp &= ~15; + sp &= ~7; stack = (struct apc_stack_layout *)sp - 1; if (context) { @@ -1091,14 +1099,14 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a NtGetContextThread( GetCurrentThread(), &stack->context ); stack->context.R0 = status; } + stack->func = func; + stack->args[0] = arg1; + stack->args[1] = arg2; + stack->args[2] = arg3; + frame->sp = (DWORD)stack; frame->pc = (DWORD)pKiUserApcDispatcher; - frame->r0 = (DWORD)&stack->context; - frame->r1 = arg1; - frame->r2 = arg2; - frame->r3 = arg3; - stack->func = func; - frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER; + frame->restore_flags |= CONTEXT_CONTROL; return status; }