[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