Skip to content

Commit

Permalink
ntdll: Add a machine frame to the KiUserApcDispatcher stack on x86-64.
Browse files Browse the repository at this point in the history
  • Loading branch information
julliard committed Nov 30, 2023
1 parent 061c612 commit 8b24139
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 42 deletions.
56 changes: 33 additions & 23 deletions dlls/ntdll/signal_x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -652,30 +652,40 @@ __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
/*******************************************************************
* KiUserApcDispatcher (NTDLL.@)
*/
void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
void (CALLBACK *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*) )
{
func( arg1, arg2, arg3, context );
NtContinue( context, TRUE );
}

__ASM_GLOBAL_FUNC( KiUserApcDispatcher,
"addq $0x8,%rsp\n\t"
"mov 0x98(%rcx),%r10\n\t" /* context->Rsp */
"mov 0xf8(%rcx),%r11\n\t" /* context->Rip */
"mov %r11,-0x8(%r10)\n\t"
"mov %rbp,-0x10(%r10)\n\t"
"lea -0x10(%r10),%rbp\n\t"
__ASM_SEH(".seh_pushreg %rbp\n\t")
__ASM_SEH(".seh_setframe %rbp,0\n\t")
__ASM_SEH(".seh_endprologue\n\t")
__ASM_CFI(".cfi_signal_frame\n\t")
__ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t")
__ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t")
__ASM_CFI(".cfi_rel_offset %rip,0x8\n\t")
__ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
"call " __ASM_NAME("dispatch_apc") "\n\t"
"int3")
__ASM_SEH(".seh_pushframe\n\t")
__ASM_SEH(".seh_stackalloc 0x4d0\n\t") /* sizeof(CONTEXT) */
__ASM_SEH(".seh_savereg %rbx,0x90\n\t")
__ASM_SEH(".seh_savereg %rbp,0xa0\n\t")
__ASM_SEH(".seh_savereg %rsi,0xa8\n\t")
__ASM_SEH(".seh_savereg %rdi,0xb0\n\t")
__ASM_SEH(".seh_savereg %r12,0xd8\n\t")
__ASM_SEH(".seh_savereg %r13,0xe0\n\t")
__ASM_SEH(".seh_savereg %r14,0xe8\n\t")
__ASM_SEH(".seh_savereg %r15,0xf0\n\t")
__ASM_SEH(".seh_endprologue\n\t")
__ASM_CFI(".cfi_signal_frame\n\t")
__ASM_CFI(".cfi_def_cfa_offset 0\n\t")
__ASM_CFI(".cfi_offset %rbx,0x90\n\t")
__ASM_CFI(".cfi_offset %rbp,0xa0\n\t")
__ASM_CFI(".cfi_offset %rsi,0xa8\n\t")
__ASM_CFI(".cfi_offset %rdi,0xb0\n\t")
__ASM_CFI(".cfi_offset %r12,0xd8\n\t")
__ASM_CFI(".cfi_offset %r13,0xe0\n\t")
__ASM_CFI(".cfi_offset %r14,0xe8\n\t")
__ASM_CFI(".cfi_offset %r15,0xf0\n\t")
__ASM_CFI(".cfi_offset %rip,0x4d0\n\t")
__ASM_CFI(".cfi_offset %rsp,0x4e8\n\t")
"movq 0x00(%rsp),%rcx\n\t" /* context->P1Home = arg1 */
"movq 0x08(%rsp),%rdx\n\t" /* context->P2Home = arg2 */
"movq 0x10(%rsp),%r8\n\t" /* context->P3Home = arg3 */
"movq 0x18(%rsp),%rax\n\t" /* context->P4Home = func */
"movq %rsp,%r9\n\t" /* context */
"callq *%rax\n\t"
"movq %rsp,%rcx\n\t" /* context */
"movl $1,%edx\n\t" /* alertable */
"call " __ASM_NAME("NtContinue") "\n\t"
"int3" )


/*******************************************************************
Expand Down
91 changes: 91 additions & 0 deletions dlls/ntdll/tests/exception.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feat
static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask);
static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD);

static void *pKiUserApcDispatcher;
static void *pKiUserExceptionDispatcher;

#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
Expand Down Expand Up @@ -4930,6 +4931,94 @@ static void test_KiUserExceptionDispatcher(void)
ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
}


static BYTE saved_KiUserApcDispatcher[12];
static BOOL apc_called;

static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
{
ok( arg1 == 0x1234, "wrong arg1 %Ix\n", arg1 );
ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 );
ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 );
apc_called = TRUE;
}

static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context)
{
struct machine_frame *frame = (struct machine_frame *)(context + 1);
UINT i;

trace( "context %p, context->Rip %#Ix, context->Rsp %#Ix (%#Ix), ContextFlags %#lx.\n",
context, context->Rip, context->Rsp,
(char *)context->Rsp - (char *)context, context->ContextFlags );

ok( context->P1Home == 0x1234, "wrong p1 %#Ix\n", context->P1Home );
ok( context->P2Home == 0x5678, "wrong p2 %#Ix\n", context->P2Home );
ok( context->P3Home == 0xdeadbeef, "wrong p3 %#Ix\n", context->P3Home );
ok( context->P4Home == (ULONG_PTR)apc_func, "wrong p4 %#Ix / %p\n", context->P4Home, apc_func );

/* machine frame offset varies between Windows versions */
for (i = 0; i < 16; i++)
{
if (frame->rip == context->Rip) break;
frame = (struct machine_frame *)((ULONG64 *)frame + 2);
}
trace( "machine frame %p (%#Ix): rip=%#Ix cs=%#Ix eflags=%#Ix rsp=%#Ix ss=%#Ix\n",
frame, (char *)frame - (char *)context,
frame->rip, frame->cs, frame->eflags, frame->rsp, frame->ss );
ok( frame->rip == context->Rip, "wrong rip %#Ix / %#Ix\n", frame->rip, context->Rip );
ok( frame->rsp == context->Rsp, "wrong rsp %#Ix / %#Ix\n", frame->rsp, context->Rsp );

hook_called = TRUE;
memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher));
return pKiUserApcDispatcher;
}

static void test_KiUserApcDispatcher(void)
{
BYTE hook_trampoline[] =
{
0x48, 0x89, 0xe1, /* mov %rsp,%rcx */
0x48, 0xb8, /* movabs hook_KiUserApcDispatcher,%rax */
0,0,0,0,0,0,0,0, /* offset 5 */
0xff, 0xd0, /* callq *rax */
0xff, 0xe0, /* jmpq *rax */
};

BYTE patched_KiUserApcDispatcher[12];
DWORD old_protect;
BYTE *ptr;
BOOL ret;

*(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserApcDispatcher;
memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));

ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher),
PAGE_EXECUTE_READWRITE, &old_protect );
ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );

memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) );
ptr = patched_KiUserApcDispatcher;
/* mov $code_mem, %rax */
*ptr++ = 0x48;
*ptr++ = 0xb8;
*(void **)ptr = code_mem;
ptr += sizeof(ULONG64);
/* jmp *rax */
*ptr++ = 0xff;
*ptr++ = 0xe0;
memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) );

hook_called = FALSE;
apc_called = FALSE;
pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
SleepEx( 0, TRUE );
ok( apc_called, "APC was not called\n" );
ok( hook_called, "hook was not called\n" );

VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect );
}

static BOOL got_nested_exception, got_prev_frame_exception;
static void *nested_exception_initial_frame;

Expand Down Expand Up @@ -11476,6 +11565,7 @@ START_TEST(exception)
X(RtlGetExtendedFeaturesMask);
X(RtlCopyContext);
X(RtlCopyExtendedContext);
X(KiUserApcDispatcher);
X(KiUserExceptionDispatcher);
#undef X

Expand Down Expand Up @@ -11637,6 +11727,7 @@ START_TEST(exception)
test_dpe_exceptions();
test_wow64_context();
test_KiUserExceptionDispatcher();
test_KiUserApcDispatcher();
test_nested_exception();
test_collided_unwind();

Expand Down
37 changes: 18 additions & 19 deletions dlls/ntdll/unix/signal_x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x4f0 );
C_ASSERT( offsetof(struct exc_stack_layout, machine_frame) == 0x590 );
C_ASSERT( sizeof(struct exc_stack_layout) == 0x700 );

/* stack layout when calling KiUserApcDispatcher */
struct apc_stack_layout
{
CONTEXT context; /* 000 */
struct machine_frame machine_frame; /* 4d0 */
ULONG64 align; /* 4f8 */
};
C_ASSERT( offsetof(struct apc_stack_layout, machine_frame) == 0x4d0 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x500 );

/* flags to control the behavior of the syscall dispatcher */
#define SYSCALL_HAVE_XSAVE 1
#define SYSCALL_HAVE_XSAVEC 2
Expand All @@ -373,18 +383,6 @@ C_ASSERT( sizeof(struct exc_stack_layout) == 0x700 );

static unsigned int syscall_flags;

/* stack layout when calling an user apc function.
* FIXME: match Windows ABI. */
struct apc_stack_layout
{
ULONG64 save_regs[4];
void *func;
ULONG64 align;
CONTEXT context;
ULONG64 rbp;
ULONG64 rip;
};

struct syscall_frame
{
ULONG64 rax; /* 0000 */
Expand Down Expand Up @@ -1492,15 +1490,16 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR a
NtGetContextThread( GetCurrentThread(), &stack->context );
stack->context.Rax = status;
}
stack->context.P1Home = arg1;
stack->context.P2Home = arg2;
stack->context.P3Home = arg3;
stack->context.P4Home = (ULONG64)func;
stack->machine_frame.rip = stack->context.Rip;
stack->machine_frame.rsp = stack->context.Rsp;
frame->rbp = stack->context.Rbp;
frame->rsp = (ULONG64)stack - 8;
frame->rsp = (ULONG64)stack;
frame->rip = (ULONG64)pKiUserApcDispatcher;
frame->rcx = (ULONG64)&stack->context;
frame->rdx = arg1;
frame->r8 = arg2;
frame->r9 = arg3;
stack->func = func;
frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER;
frame->restore_flags |= CONTEXT_CONTROL;
return status;
}

Expand Down

0 comments on commit 8b24139

Please sign in to comment.