diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index da112de111d..b921acec9ce 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -652,30 +652,40 @@ __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher, /******************************************************************* * KiUserApcDispatcher (NTDLL.@) */ -void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - void (CALLBACK *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*) ) -{ - func( arg1, arg2, arg3, context ); - NtContinue( context, TRUE ); -} - __ASM_GLOBAL_FUNC( KiUserApcDispatcher, - "addq $0x8,%rsp\n\t" - "mov 0x98(%rcx),%r10\n\t" /* context->Rsp */ - "mov 0xf8(%rcx),%r11\n\t" /* context->Rip */ - "mov %r11,-0x8(%r10)\n\t" - "mov %rbp,-0x10(%r10)\n\t" - "lea -0x10(%r10),%rbp\n\t" - __ASM_SEH(".seh_pushreg %rbp\n\t") - __ASM_SEH(".seh_setframe %rbp,0\n\t") - __ASM_SEH(".seh_endprologue\n\t") - __ASM_CFI(".cfi_signal_frame\n\t") - __ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t") - __ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t") - __ASM_CFI(".cfi_rel_offset %rip,0x8\n\t") - __ASM_CFI(".cfi_rel_offset %rbp,0\n\t") - "call " __ASM_NAME("dispatch_apc") "\n\t" - "int3") + __ASM_SEH(".seh_pushframe\n\t") + __ASM_SEH(".seh_stackalloc 0x4d0\n\t") /* sizeof(CONTEXT) */ + __ASM_SEH(".seh_savereg %rbx,0x90\n\t") + __ASM_SEH(".seh_savereg %rbp,0xa0\n\t") + __ASM_SEH(".seh_savereg %rsi,0xa8\n\t") + __ASM_SEH(".seh_savereg %rdi,0xb0\n\t") + __ASM_SEH(".seh_savereg %r12,0xd8\n\t") + __ASM_SEH(".seh_savereg %r13,0xe0\n\t") + __ASM_SEH(".seh_savereg %r14,0xe8\n\t") + __ASM_SEH(".seh_savereg %r15,0xf0\n\t") + __ASM_SEH(".seh_endprologue\n\t") + __ASM_CFI(".cfi_signal_frame\n\t") + __ASM_CFI(".cfi_def_cfa_offset 0\n\t") + __ASM_CFI(".cfi_offset %rbx,0x90\n\t") + __ASM_CFI(".cfi_offset %rbp,0xa0\n\t") + __ASM_CFI(".cfi_offset %rsi,0xa8\n\t") + __ASM_CFI(".cfi_offset %rdi,0xb0\n\t") + __ASM_CFI(".cfi_offset %r12,0xd8\n\t") + __ASM_CFI(".cfi_offset %r13,0xe0\n\t") + __ASM_CFI(".cfi_offset %r14,0xe8\n\t") + __ASM_CFI(".cfi_offset %r15,0xf0\n\t") + __ASM_CFI(".cfi_offset %rip,0x4d0\n\t") + __ASM_CFI(".cfi_offset %rsp,0x4e8\n\t") + "movq 0x00(%rsp),%rcx\n\t" /* context->P1Home = arg1 */ + "movq 0x08(%rsp),%rdx\n\t" /* context->P2Home = arg2 */ + "movq 0x10(%rsp),%r8\n\t" /* context->P3Home = arg3 */ + "movq 0x18(%rsp),%rax\n\t" /* context->P4Home = func */ + "movq %rsp,%r9\n\t" /* context */ + "callq *%rax\n\t" + "movq %rsp,%rcx\n\t" /* context */ + "movl $1,%edx\n\t" /* alertable */ + "call " __ASM_NAME("NtContinue") "\n\t" + "int3" ) /******************************************************************* diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index fac83c1c695..e176c6add41 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -79,6 +79,7 @@ static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feat static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask); static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD); +static void *pKiUserApcDispatcher; static void *pKiUserExceptionDispatcher; #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 @@ -4930,6 +4931,94 @@ static void test_KiUserExceptionDispatcher(void) ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); } + +static BYTE saved_KiUserApcDispatcher[12]; +static BOOL apc_called; + +static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + ok( arg1 == 0x1234, "wrong arg1 %Ix\n", arg1 ); + ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 ); + ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 ); + apc_called = TRUE; +} + +static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context) +{ + struct machine_frame *frame = (struct machine_frame *)(context + 1); + UINT i; + + trace( "context %p, context->Rip %#Ix, context->Rsp %#Ix (%#Ix), ContextFlags %#lx.\n", + context, context->Rip, context->Rsp, + (char *)context->Rsp - (char *)context, context->ContextFlags ); + + ok( context->P1Home == 0x1234, "wrong p1 %#Ix\n", context->P1Home ); + ok( context->P2Home == 0x5678, "wrong p2 %#Ix\n", context->P2Home ); + ok( context->P3Home == 0xdeadbeef, "wrong p3 %#Ix\n", context->P3Home ); + ok( context->P4Home == (ULONG_PTR)apc_func, "wrong p4 %#Ix / %p\n", context->P4Home, apc_func ); + + /* machine frame offset varies between Windows versions */ + for (i = 0; i < 16; i++) + { + if (frame->rip == context->Rip) break; + frame = (struct machine_frame *)((ULONG64 *)frame + 2); + } + trace( "machine frame %p (%#Ix): rip=%#Ix cs=%#Ix eflags=%#Ix rsp=%#Ix ss=%#Ix\n", + frame, (char *)frame - (char *)context, + frame->rip, frame->cs, frame->eflags, frame->rsp, frame->ss ); + ok( frame->rip == context->Rip, "wrong rip %#Ix / %#Ix\n", frame->rip, context->Rip ); + ok( frame->rsp == context->Rsp, "wrong rsp %#Ix / %#Ix\n", frame->rsp, context->Rsp ); + + hook_called = TRUE; + memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + BYTE hook_trampoline[] = + { + 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */ + 0x48, 0xb8, /* movabs hook_KiUserApcDispatcher,%rax */ + 0,0,0,0,0,0,0,0, /* offset 5 */ + 0xff, 0xd0, /* callq *rax */ + 0xff, 0xe0, /* jmpq *rax */ + }; + + BYTE patched_KiUserApcDispatcher[12]; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + *(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) ); + ptr = patched_KiUserApcDispatcher; + /* mov $code_mem, %rax */ + *ptr++ = 0x48; + *ptr++ = 0xb8; + *(void **)ptr = code_mem; + ptr += sizeof(ULONG64); + /* jmp *rax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + + hook_called = FALSE; + apc_called = FALSE; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_called, "APC was not called\n" ); + ok( hook_called, "hook was not called\n" ); + + VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect ); +} + static BOOL got_nested_exception, got_prev_frame_exception; static void *nested_exception_initial_frame; @@ -11476,6 +11565,7 @@ START_TEST(exception) X(RtlGetExtendedFeaturesMask); X(RtlCopyContext); X(RtlCopyExtendedContext); + X(KiUserApcDispatcher); X(KiUserExceptionDispatcher); #undef X @@ -11637,6 +11727,7 @@ START_TEST(exception) test_dpe_exceptions(); test_wow64_context(); test_KiUserExceptionDispatcher(); + test_KiUserApcDispatcher(); test_nested_exception(); test_collided_unwind(); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 482c578828e..c87f2a0b642 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -365,6 +365,16 @@ C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x4f0 ); C_ASSERT( offsetof(struct exc_stack_layout, machine_frame) == 0x590 ); C_ASSERT( sizeof(struct exc_stack_layout) == 0x700 ); +/* stack layout when calling KiUserApcDispatcher */ +struct apc_stack_layout +{ + CONTEXT context; /* 000 */ + struct machine_frame machine_frame; /* 4d0 */ + ULONG64 align; /* 4f8 */ +}; +C_ASSERT( offsetof(struct apc_stack_layout, machine_frame) == 0x4d0 ); +C_ASSERT( sizeof(struct apc_stack_layout) == 0x500 ); + /* flags to control the behavior of the syscall dispatcher */ #define SYSCALL_HAVE_XSAVE 1 #define SYSCALL_HAVE_XSAVEC 2 @@ -373,18 +383,6 @@ C_ASSERT( sizeof(struct exc_stack_layout) == 0x700 ); static unsigned int syscall_flags; -/* stack layout when calling an user apc function. - * FIXME: match Windows ABI. */ -struct apc_stack_layout -{ - ULONG64 save_regs[4]; - void *func; - ULONG64 align; - CONTEXT context; - ULONG64 rbp; - ULONG64 rip; -}; - struct syscall_frame { ULONG64 rax; /* 0000 */ @@ -1492,15 +1490,16 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a NtGetContextThread( GetCurrentThread(), &stack->context ); stack->context.Rax = status; } + stack->context.P1Home = arg1; + stack->context.P2Home = arg2; + stack->context.P3Home = arg3; + stack->context.P4Home = (ULONG64)func; + stack->machine_frame.rip = stack->context.Rip; + stack->machine_frame.rsp = stack->context.Rsp; frame->rbp = stack->context.Rbp; - frame->rsp = (ULONG64)stack - 8; + frame->rsp = (ULONG64)stack; frame->rip = (ULONG64)pKiUserApcDispatcher; - frame->rcx = (ULONG64)&stack->context; - frame->rdx = arg1; - frame->r8 = arg2; - frame->r9 = arg3; - stack->func = func; - frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER; + frame->restore_flags |= CONTEXT_CONTROL; return status; }