Paul Gofman : ntdll: Always copy context in call_user_apc_dispatcher() on x86.

Alexandre Julliard julliard at winehq.org
Tue Dec 22 15:49:57 CST 2020


Module: wine
Branch: master
Commit: 175a3649ba04daaad3f2b5b62d6bfe091d6a9e46
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=175a3649ba04daaad3f2b5b62d6bfe091d6a9e46

Author: Paul Gofman <pgofman at 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 at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at 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" )




More information about the wine-cvs mailing list