Currently, if call_user_apc_dispatcher() is called with nonzero context, there is no guarantee that the provided context is stored above the rsp = context_ptr->Rsp - (sizeof(CONTEXT) + offsetof(frame,ret_addr)) being set.
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v3: - properly align / reserve stack for setup_user_apc_dispatcher_stack() call.
dlls/ntdll/unix/signal_x86_64.c | 62 +++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 7181b48728c..3ae3b4c141e 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -250,6 +250,22 @@ C_ASSERT((offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout))) C_ASSERT( sizeof(XSTATE) == 0x140 ); C_ASSERT( sizeof(struct stack_layout) == 0x590 ); /* Should match the size in call_user_exception_dispatcher(). */
+/* 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; +}; + +/* Should match size and offset in call_user_apc_dispatcher(). */ +C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x30 ); +C_ASSERT( sizeof(struct apc_stack_layout) == 0x510 ); + struct syscall_frame { ULONG64 xmm[10 * 2]; /* xmm6-xmm15 */ @@ -266,6 +282,9 @@ struct syscall_frame ULONG64 ret_addr; };
+/* Should match the offset in call_user_apc_dispatcher(). */ +C_ASSERT( offsetof( struct syscall_frame, ret_addr ) == 0xf0); + struct amd64_thread_data { DWORD_PTR dr0; /* 02f0 debug registers */ @@ -2013,31 +2032,46 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) /*********************************************************************** * call_user_apc_dispatcher */ +struct apc_stack_layout * WINAPI setup_user_apc_dispatcher_stack( CONTEXT *context, struct apc_stack_layout *stack ) +{ + CONTEXT c; + + if (!context) + { + c.ContextFlags = CONTEXT_FULL; + NtGetContextThread( GetCurrentThread(), &c ); + context = &c; + } + memmove( &stack->context, context, sizeof(stack->context) ); + return stack; +} + __ASM_GLOBAL_FUNC( call_user_apc_dispatcher, "movq 0x28(%rsp),%rsi\n\t" /* func */ "movq 0x30(%rsp),%rdi\n\t" /* dispatcher */ "movq %gs:0x30,%rbx\n\t" - "jrcxz 1f\n\t" - "movq 0x98(%rcx),%rax\n\t" /* context_ptr->Rsp */ - "leaq -0x5c0(%rax),%rsp\n\t" /* sizeof(CONTEXT) + offsetof(frame,ret_addr) */ - "andq $~15,%rsp\n\t" - "jmp 2f\n" - "1:\tmovq 0x328(%rbx),%rax\n\t" /* amd64_thread_data()->syscall_frame */ - "leaq -0x4d0(%rax),%rsp\n\t" - "andq $~15,%rsp\n\t" "movq %rdx,%r12\n\t" /* ctx */ "movq %r8,%r13\n\t" /* arg1 */ "movq %r9,%r14\n\t" /* arg2 */ - "movq %rsp,%rdx\n\t" /* context */ - "movl $0x10000b,0x30(%rdx)\n\t" /* context.ContextFlags */ - "movq $~1,%rcx\n\t" - "call " __ASM_NAME("NtGetContextThread") "\n\t" - "movq %rsp,%rcx\n\t" /* context */ + "jrcxz 1f\n\t" + "movq 0x98(%rcx),%rdx\n\t" /* context->Rsp */ + "jmp 2f\n\t" + "1:\tmovq 0x328(%rbx),%rax\n\t" /* amd64_thread_data()->syscall_frame */ + "leaq 0xf0(%rax),%rdx\n\t" /* &amd64_thread_data()->syscall_frame->ret_addr */ + "2:\tsubq $0x510,%rdx\n\t" /* sizeof(struct apc_stack_layout) */ + "andq $~0xf,%rdx\n\t" + "addq $8,%rsp\n\t" /* pop return address */ + "cmpq %rsp,%rdx\n\t" + "cmovbq %rdx,%rsp\n\t" + "subq $0x20,%rsp\n\t" + "call " __ASM_NAME("setup_user_apc_dispatcher_stack") "\n\t" + "movq %rax,%rsp\n\t" + "leaq 0x30(%rsp),%rcx\n\t" /* context */ "movq $0xc0,0x78(%rcx)\n\t" /* context.Rax = STATUS_USER_APC */ "movq %r12,%rdx\n\t" /* ctx */ "movq %r13,%r8\n\t" /* arg1 */ "movq %r14,%r9\n" /* arg2 */ - "2:\tmovq $0,0x328(%rbx)\n\t" + "movq $0,0x328(%rbx)\n\t" /* amd64_thread_data()->syscall_frame */ "movq %rsi,0x20(%rsp)\n\t" /* func */ "movq 0xa0(%rcx),%rbp\n\t" /* context.Rbp */ "pushq 0xf8(%rcx)\n\t" /* context.Rip */
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - fix build for other archs.
dlls/ntdll/tests/exception.c | 53 +++++++++++++++++++++++++++++++++ dlls/ntdll/thread.c | 28 ++++++++++++++++- dlls/ntdll/unix/signal_x86_64.c | 23 ++++++++++++-- 3 files changed, 101 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 5686e39ab9e..3a0a5ac049c 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -37,6 +37,8 @@ static void *code_mem;
static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*); +static NTSTATUS (WINAPI *pNtQueueApcThread)(HANDLE handle, PNTAPCFUNC func, + ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec); static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID); static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); @@ -4061,6 +4063,55 @@ static void test_nested_exception(void) ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n"); }
+static CONTEXT test_unwind_apc_context; +static BOOL test_unwind_apc_called; + +static void CALLBACK test_unwind_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3) +{ + EXCEPTION_RECORD rec; + + test_unwind_apc_called = TRUE; + memset(&rec, 0, sizeof(rec)); + pRtlUnwind((void *)test_unwind_apc_context.Rsp, (void *)test_unwind_apc_context.Rip, &rec, (void *)0xdeadbeef); + ok(0, "Should not get here.\n"); +} + +static void test_unwind_from_apc(void) +{ + NTSTATUS status; + int pass; + + if (!pNtQueueApcThread) + { + win_skip("NtQueueApcThread is not available.\n"); + return; + } + + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&test_unwind_apc_context); + InterlockedIncrement(&pass); + + if (pass == 2) + { + test_unwind_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#x.\n", status); + SleepEx(0, TRUE); + ok(0, "Should not get here.\n"); + } + if (pass == 3) + { + ok(test_unwind_apc_called, "Test user APC was not called.\n"); + test_unwind_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#x.\n", status); + NtContinue(&test_unwind_apc_context, TRUE ); + ok(0, "Should not get here.\n"); + } + ok(pass == 4, "Got unexpected pass %d.\n", pass); + ok(test_unwind_apc_called, "Test user APC was not called.\n"); +} #elif defined(__arm__)
static void test_thread_context(void) @@ -8073,6 +8124,7 @@ START_TEST(exception) #define X(f) p##f = (void*)GetProcAddress(hntdll, #f) X(NtGetContextThread); X(NtSetContextThread); + X(NtQueueApcThread); X(NtReadVirtualMemory); X(NtClose); X(RtlUnwind); @@ -8241,6 +8293,7 @@ START_TEST(exception) skip( "Dynamic unwind functions not found\n" ); test_extended_context(); test_copy_context(); + test_unwind_from_apc();
#elif defined(__aarch64__)
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 578c7a5436c..5bf456ec040 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -78,7 +78,7 @@ int __cdecl __wine_dbg_output( const char *str ) /******************************************************************* * KiUserApcDispatcher (NTDLL.@) */ -void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2, +void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2, PNTAPCFUNC func ) { func( ctx, arg1, arg2 ); @@ -86,6 +86,32 @@ void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1 }
+#ifdef __x86_64__ +__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") +#else +void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2, + PNTAPCFUNC func ) +{ + dispatch_apc( context, ctx, arg1, arg2, func ); +} +#endif + /*********************************************************************** * RtlExitUserThread (NTDLL.@) */ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 3ae3b4c141e..837cd9ed654 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2073,9 +2073,28 @@ __ASM_GLOBAL_FUNC( call_user_apc_dispatcher, "movq %r14,%r9\n" /* arg2 */ "movq $0,0x328(%rbx)\n\t" /* amd64_thread_data()->syscall_frame */ "movq %rsi,0x20(%rsp)\n\t" /* func */ - "movq 0xa0(%rcx),%rbp\n\t" /* context.Rbp */ + "movq %rdi,%r10\n\t" + /* Set nonvolatile regs from context. */ + "movq 0xa0(%rcx),%rbp\n\t" + "movq 0x90(%rcx),%rbx\n\t" + "movq 0xa8(%rcx),%rsi\n\t" + "movq 0xb0(%rcx),%rdi\n\t" + "movq 0xd8(%rcx),%r12\n\t" + "movq 0xe0(%rcx),%r13\n\t" + "movq 0xe8(%rcx),%r14\n\t" + "movq 0xf0(%rcx),%r15\n\t" + "movdqa 0x200(%rcx),%xmm6\n\t" + "movdqa 0x210(%rcx),%xmm7\n\t" + "movdqa 0x220(%rcx),%xmm8\n\t" + "movdqa 0x230(%rcx),%xmm9\n\t" + "movdqa 0x240(%rcx),%xmm10\n\t" + "movdqa 0x250(%rcx),%xmm11\n\t" + "movdqa 0x260(%rcx),%xmm12\n\t" + "movdqa 0x270(%rcx),%xmm13\n\t" + "movdqa 0x280(%rcx),%xmm14\n\t" + "movdqa 0x290(%rcx),%xmm15\n\t" "pushq 0xf8(%rcx)\n\t" /* context.Rip */ - "jmp *%rdi" ) + "jmp *%r10" )
/***********************************************************************