Module: wine Branch: master Commit: 175a3649ba04daaad3f2b5b62d6bfe091d6a9e46 URL: https://source.winehq.org/git/wine.git/?a=commit;h=175a3649ba04daaad3f2b5b62...
Author: Paul Gofman pgofman@codeweavers.com Date: Fri Dec 18 20:06:39 2020 +0300
ntdll: Always copy context in call_user_apc_dispatcher() on x86.
Signed-off-by: Paul Gofman pgofman@codeweavers.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/ntdll/tests/exception.c | 48 +++++++++++++++++++++++++ dlls/ntdll/unix/signal_i386.c | 84 +++++++++++++++++++++++++++---------------- 2 files changed, 101 insertions(+), 31 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 3a0a5ac049c..44c03a6e52a 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -2001,6 +2001,53 @@ static void test_kiuserexceptiondispatcher(void) ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); }
+static BOOL test_apc_called; + +static void CALLBACK test_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3) +{ + test_apc_called = TRUE; +} + +static void test_user_apc(void) +{ + NTSTATUS status; + CONTEXT context; + int pass; + + if (!pNtQueueApcThread) + { + win_skip("NtQueueApcThread is not available.\n"); + return; + } + + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&context); + InterlockedIncrement(&pass); + + if (pass == 2) + { + /* Try to make sure context data is far enough below context.Esp. */ + CONTEXT c[4]; + + c[0] = context; + + test_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#x.\n", status); + SleepEx(0, TRUE); + ok(test_apc_called, "Test user APC was not called.\n"); + test_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#x.\n", status); + status = NtContinue(&c[0], TRUE ); + + /* Broken before Win7, in that case NtContinue returns here instead of restoring context after calling APC. */ + ok(0, "Should not get here, status %#x.\n", status); + } + ok(pass == 3, "Got unexpected pass %d.\n", pass); + ok(test_apc_called, "Test user APC was not called.\n"); +} #elif defined(__x86_64__)
#define UNW_FLAG_NHANDLER 0 @@ -8253,6 +8300,7 @@ START_TEST(exception) test_kiuserexceptiondispatcher(); test_extended_context(); test_copy_context(); + test_user_apc();
#elif defined(__x86_64__)
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 11fa61232fa..c8690b51f2f 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1665,6 +1665,42 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) setup_raise_exception( sigcontext, stack, rec, &xcontext ); }
+/* stack layout when calling an user apc function. + * FIXME: match Windows ABI. */ +struct apc_stack_layout +{ + void *context_ptr; + void *ctx; + void *arg1; + void *arg2; + void *func; + CONTEXT context; +}; + +struct apc_stack_layout * WINAPI setup_user_apc_dispatcher_stack( CONTEXT *context, struct apc_stack_layout *stack, + void *ctx, void *arg1, void *arg2, void *func ) +{ + CONTEXT c; + + if (!context) + { + c.ContextFlags = CONTEXT_FULL; + NtGetContextThread( GetCurrentThread(), &c ); + context = &c; + } + memmove( &stack->context, context, sizeof(stack->context) ); + stack->context_ptr = &stack->context; + stack->ctx = ctx; + stack->arg1 = arg1; + stack->arg2 = arg2; + stack->func = func; + stack->context.Eax = STATUS_USER_APC; + return stack; +} + +C_ASSERT( sizeof(struct apc_stack_layout) == 0x2e0 ); +C_ASSERT( offsetof(struct syscall_frame, ret_addr) == 0x14 ); +C_ASSERT( offsetof(struct apc_stack_layout, context) == 20 );
/*********************************************************************** * call_user_apc_dispatcher @@ -1672,41 +1708,27 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) __ASM_GLOBAL_FUNC( call_user_apc_dispatcher, "movl 4(%esp),%esi\n\t" /* context_ptr */ "movl 24(%esp),%edi\n\t" /* dispatcher */ + "leal 4(%esp),%ebp\n\t" "test %esi,%esi\n\t" "jz 1f\n\t" - "movl 0xc4(%esi),%eax\n\t" /* context_ptr->Rsp */ - "leal -0x2f8(%eax),%eax\n\t" /* sizeof(CONTEXT) + offsetof(frame,ret_addr) + params */ - "movl 20(%esp),%ecx\n\t" /* func */ - "movl %ecx,20(%eax)\n\t" - "movl 8(%esp),%ebx\n\t" /* ctx */ - "movl 12(%esp),%edx\n\t" /* arg1 */ - "movl 16(%esp),%ecx\n\t" /* arg2 */ - "leal 4(%eax),%esp\n\t" - "jmp 2f\n" + "movl 0xc4(%esi),%eax\n\t" /* context_ptr->Esp */ + "jmp 2f\n\t" "1:\tmovl %fs:0x1f8,%eax\n\t" /* x86_thread_data()->syscall_frame */ - "leal -0x2cc(%eax),%esi\n\t" - "movl %esp,%ebx\n\t" - "cmpl %esp,%esi\n\t" - "cmovbl %esi,%esp\n\t" - "pushl 20(%ebx)\n\t" /* func */ - "pushl 16(%ebx)\n\t" /* arg2 */ - "pushl 12(%ebx)\n\t" /* arg1 */ - "movl 8(%ebx),%ebx\n\t" /* ctx */ - "movl $0x00010007,(%esi)\n\t" /* context.ContextFlags = CONTEXT_FULL */ - "pushl %esi\n\t" /* context */ - "pushl $0xfffffffe\n\t" - "call " __ASM_STDCALL("NtGetContextThread",8) "\n\t" - "movl $0xc0,0xb0(%esi)\n" /* context.Eax = STATUS_USER_APC */ - "popl %edx\n\t" - "popl %ecx\n\t" - "popl %eax\n\t" - "leal -20(%esi),%esp\n\t" - "movl %eax,16(%esp)\n" /* func */ - "2:\tmovl %ecx,12(%esp)\n\t" /* arg2 */ - "movl %edx,8(%esp)\n\t" /* arg1 */ - "movl %ebx,4(%esp)\n\t" /* ctx */ - "movl %esi,(%esp)\n\t" /* context */ + "leal 0x14(%eax),%eax\n\t" /* &x86_thread_data()->syscall_frame->ret_addr */ + "2:\tsubl $0x2e0,%eax\n\t" /* sizeof(struct apc_stack_layout) */ + "movl %ebp,%esp\n\t" /* pop return address */ + "cmpl %esp,%eax\n\t" + "cmovbl %eax,%esp\n\t" + "pushl 16(%ebp)\n\t" /* func */ + "pushl 12(%ebp)\n\t" /* arg2 */ + "pushl 8(%ebp)\n\t" /* arg1 */ + "pushl 4(%ebp)\n\t" /* ctx */ + "pushl %eax\n\t" + "pushl %esi\n\t" + "call " __ASM_STDCALL("setup_user_apc_dispatcher_stack",24) "\n\t" "movl $0,%fs:0x1f8\n\t" /* x86_thread_data()->syscall_frame = NULL */ + "movl %eax,%esp\n\t" + "leal 20(%eax),%esi\n\t" "movl 0xb4(%esi),%ebp\n\t" /* context.Ebp */ "pushl 0xb8(%esi)\n\t" /* context.Eip */ "jmp *%edi\n" )