[PATCH] ntdll: Always copy context in call_user_apc_dispatcher() on x86.

Paul Gofman pgofman at codeweavers.com
Fri Dec 18 11:06:39 CST 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 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 48785f229e9..3aa8b7a4eb9 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -2003,6 +2003,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
@@ -8291,6 +8338,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 f1fd2cd2e41..ff9d8a12c14 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -1675,6 +1675,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
@@ -1682,41 +1718,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" )
-- 
2.29.2




More information about the wine-devel mailing list