ntdll: Fix stack layout for KiUserApcDispatcher on ARM.

This commit is contained in:
Alexandre Julliard 2023-12-03 14:16:42 +01:00
parent 89f3c59739
commit 75d0d466ec
3 changed files with 137 additions and 19 deletions

View file

@ -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" )
/*******************************************************************

View file

@ -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

View file

@ -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;
}