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>
This commit is contained in:
Paul Gofman 2020-12-18 20:06:39 +03:00 committed by Alexandre Julliard
parent 73281bf132
commit 175a3649ba
2 changed files with 101 additions and 31 deletions

View File

@ -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__)

View File

@ -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" )