From 58a00854c3c22708a0dda9ea9a9b167247b81bce Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 30 Nov 2023 17:39:08 +0100 Subject: [PATCH] ntdll: Fix stack layout for KiUserExceptionDispatcher on i386. --- dlls/ntdll/tests/exception.c | 21 ++++++--- dlls/ntdll/unix/signal_i386.c | 82 +++++++++++++++++++++-------------- dlls/wow64/struct32.h | 7 +++ dlls/wow64/syscall.c | 38 ++++++++-------- 4 files changed, 91 insertions(+), 57 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 3057ceb3f20..b32dbc20813 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1908,15 +1908,22 @@ static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTE /* Use CDECL to leave arguments on stack. */ static void * CDECL hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context) { - trace("rec %p, context %p.\n", rec, context); - trace("context->Eip %#lx, context->Esp %#lx, ContextFlags %#lx.\n", - context->Eip, context->Esp, context->ContextFlags); + CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); + + trace( "rec %p context %p context->Eip %#lx, context->Esp %#lx (%x), ContextFlags %#lx.\n", + rec, context, context->Eip, context->Esp, + (char *)context->Esp - (char *)&rec, context->ContextFlags); + trace( "xstate %lx = %p (%x) %lx\n", xctx->XState.Offset, (char *)xctx + xctx->XState.Offset, + (char *)xctx + xctx->XState.Offset - (char *)&rec, xctx->XState.Length ); + + ok( (char *)rec->ExceptionInformation <= (char *)context && + (char *)(rec + 1) >= (char *)context, "wrong ptrs %p / %p\n", rec, context ); + ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset ); + ok( xctx->All.Length >= sizeof(CONTEXT) + sizeof(CONTEXT_EX), "wrong All.Length %lx\n", xctx->All.Length ); + ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset ); + ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length ); hook_called = TRUE; - /* Broken on Win2008, probably rec offset in stack is different. */ - ok(rec->ExceptionCode == 0x80000003 || broken(!rec->ExceptionCode), - "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); - hook_KiUserExceptionDispatcher_eip = (void *)context->Eip; hook_exception_address = rec->ExceptionAddress; memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index a5aa9623b07..59b3d9797c2 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -435,6 +435,21 @@ enum i386_trap_code #endif }; +/* stack layout when calling KiUserExceptionDispatcher */ +struct exc_stack_layout +{ + EXCEPTION_RECORD *rec_ptr; /* 000 first arg for KiUserExceptionDispatcher */ + CONTEXT *context_ptr; /* 004 second arg for KiUserExceptionDispatcher */ + EXCEPTION_RECORD rec; /* 008 */ + CONTEXT context; /* 058 */ + CONTEXT_EX context_ex; /* 324 */ + BYTE xstate[sizeof(XSTATE)+64]; /* 33c extra space to allow for 64-byte alignment */ + DWORD align; /* 4bc */ +}; +C_ASSERT( offsetof(struct exc_stack_layout, context) == 0x58 ); +C_ASSERT( offsetof(struct exc_stack_layout, xstate) == 0x33c ); +C_ASSERT( sizeof(struct exc_stack_layout) == 0x4c0 ); + struct syscall_frame { WORD syscall_flags; /* 000 */ @@ -1417,23 +1432,8 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, EXCEPTION_RECORD *rec, struct xcontext *xcontext ) { CONTEXT *context = &xcontext->c; - size_t stack_size; XSTATE *src_xs; - - struct stack_layout - { - EXCEPTION_RECORD *rec_ptr; /* first arg for KiUserExceptionDispatcher */ - CONTEXT *context_ptr; /* second arg for KiUserExceptionDispatcher */ - CONTEXT context; - CONTEXT_EX context_ex; - EXCEPTION_RECORD rec; - DWORD ebp; - DWORD eip; - char xstate[0]; - } *stack; - -C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) ); - + struct exc_stack_layout *stack; NTSTATUS status = send_debug_event( rec, context, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) @@ -1445,23 +1445,17 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--; - stack_size = sizeof(*stack); - if ((src_xs = xstate_from_context( context ))) - { - stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr - - sizeof(XSTATE)) & ~(ULONG_PTR)63); - } - - stack = virtual_setup_exception( stack_ptr, stack_size, rec ); + stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec ); + stack->rec_ptr = &stack->rec; + stack->context_ptr = &stack->context; stack->rec = *rec; stack->context = *context; - if (src_xs) + if ((src_xs = xstate_from_context( context ))) { - XSTATE *dst_xs = (XSTATE *)stack->xstate; + XSTATE *dst_xs = (XSTATE *)(((ULONG_PTR)stack->xstate + 63) & ~63); - assert(!((ULONG_PTR)dst_xs & 63)); - context_init_xstate( &stack->context, stack->xstate ); + context_init_xstate( &stack->context, dst_xs ); memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000004 : 0; if (src_xs->Mask & 4) @@ -1475,8 +1469,6 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) context_init_xstate( &stack->context, NULL ); } - stack->rec_ptr = &stack->rec; - stack->context_ptr = &stack->context; ESP_sig(sigcontext) = (DWORD)stack; EIP_sig(sigcontext) = (DWORD)pKiUserExceptionDispatcher; /* clear single-step, direction, and align check flag */ @@ -1560,11 +1552,35 @@ void call_raise_user_exception_dispatcher(void) NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) { struct syscall_frame *frame = x86_thread_data()->syscall_frame; - void **stack = (void **)frame->esp; + ULONG esp = (frame->esp - sizeof(struct exc_stack_layout)) & ~3; + struct exc_stack_layout *stack = (struct exc_stack_layout *)esp; + XSTATE *src_xs; if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--; - *(--stack) = context; - *(--stack) = rec; + + stack->rec_ptr = &stack->rec; + stack->context_ptr = &stack->context; + stack->rec = *rec; + stack->context = *context; + + if ((src_xs = xstate_from_context( context ))) + { + XSTATE *dst_xs = (XSTATE *)(((ULONG_PTR)stack->xstate + 63) & ~63); + + context_init_xstate( &stack->context, dst_xs ); + memset( dst_xs, 0, offsetof(XSTATE, YmmContext) ); + dst_xs->CompactionMask = xstate_compaction_enabled ? 0x8000000000000004 : 0; + if (src_xs->Mask & 4) + { + dst_xs->Mask = 4; + memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); + } + } + else + { + context_init_xstate( &stack->context, NULL ); + } + frame->esp = (ULONG)stack; frame->eip = (ULONG)pKiUserExceptionDispatcher; return STATUS_SUCCESS; diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index 92cd3d6e37e..482f1297b28 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -73,6 +73,13 @@ typedef struct ULONG DefaultNonPagedPoolCharge; } OBJECT_TYPE_INFORMATION32; +typedef struct +{ + CONTEXT_CHUNK All; + CONTEXT_CHUNK Legacy; + CONTEXT_CHUNK XState; +} CONTEXT_EX32; + typedef struct { UNICODE_STRING32 ObjectName; diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c index 67b8a20109d..cd1f3b4f8bb 100644 --- a/dlls/wow64/syscall.c +++ b/dlls/wow64/syscall.c @@ -190,16 +190,24 @@ static void call_user_exception_dispatcher( EXCEPTION_RECORD32 *rec, void *ctx32 { case IMAGE_FILE_MACHINE_I386: { - struct stack_layout + /* stack layout when calling 32-bit KiUserExceptionDispatcher */ + struct exc_stack_layout32 { - ULONG rec_ptr; /* first arg for KiUserExceptionDispatcher */ - ULONG context_ptr; /* second arg for KiUserExceptionDispatcher */ - EXCEPTION_RECORD32 rec; - I386_CONTEXT context; + ULONG rec_ptr; /* 000 */ + ULONG context_ptr; /* 004 */ + EXCEPTION_RECORD32 rec; /* 008 */ + I386_CONTEXT context; /* 058 */ + CONTEXT_EX32 context_ex; /* 324 */ + BYTE xstate[sizeof(XSTATE)+64]; /* 33c */ + DWORD align; /* 4bc */ } *stack; - I386_CONTEXT *context, ctx = { CONTEXT_I386_ALL }; + I386_CONTEXT ctx = { CONTEXT_I386_ALL }; CONTEXT_EX *context_ex, *src_ex = NULL; - ULONG size, flags; + ULONG flags; + + C_ASSERT( offsetof(struct exc_stack_layout32, context) == 0x58 ); + C_ASSERT( offsetof(struct exc_stack_layout32, xstate) == 0x33c ); + C_ASSERT( sizeof(struct exc_stack_layout32) == 0x4c0 ); pBTCpuGetContext( GetCurrentThread(), GetCurrentProcess(), NULL, &ctx ); @@ -222,20 +230,16 @@ static void call_user_exception_dispatcher( EXCEPTION_RECORD32 *rec, void *ctx32 flags = ctx.ContextFlags; if (src_ex) flags |= CONTEXT_I386_XSTATE; - RtlGetExtendedContextLength( flags, &size ); - size = ((size + 15) & ~15) + offsetof(struct stack_layout,context); - stack = (struct stack_layout *)(ULONG_PTR)(ctx.Esp - size); - stack->rec_ptr = PtrToUlong( &stack->rec ); - stack->rec = *rec; + stack = (struct exc_stack_layout32 *)ULongToPtr( ctx.Esp & ~3 ) - 1; + stack->rec_ptr = PtrToUlong( &stack->rec ); + stack->context_ptr = PtrToUlong( &stack->context ); + stack->rec = *rec; + stack->context = ctx; RtlInitializeExtendedContext( &stack->context, flags, &context_ex ); - context = RtlLocateLegacyContext( context_ex, NULL ); - *context = ctx; - context->ContextFlags = flags; /* adjust Eip for breakpoints in software emulation (hardware exceptions already adjust Rip) */ if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && (wow64info->CpuFlags & WOW64_CPUFLAGS_SOFTWARE)) - context->Eip--; - stack->context_ptr = PtrToUlong( context ); + stack->context.Eip--; if (src_ex) {