-- v5: ntdll: Handle invalid FP state in usr1_handler() on i386. ntdll: Handle invalid FP state in usr1_handler() on x64. ntdll: Initilize segments registers in the frame in call_user_mode_callback().
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/signal_x86_64.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 9e02d2cb9d0..8bc39f35f05 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1723,6 +1723,10 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "leaq 0x10(%rbp),%rax\n\t" "movq %rax,0xa8(%rsp)\n\t" /* frame->syscall_cfa */ "movq 0x378(%r13),%r14\n\t" /* thread_data->syscall_frame */ + "movq 0x78(%r14),%rax\n\t" /* prev_frame->cs */ + "movq %rax,0x78(%rsp)\n\t" /* frame->cs */ + "movq 0x90(%r14),%rax\n\t" /* prev_frame->ss */ + "movq %rax,0x90(%rsp)\n\t" /* frame->ss */ "movq %r14,0xa0(%rsp)\n\t" /* frame->prev_frame */ "movq %rsp,0x378(%r13)\n\t" /* thread_data->syscall_frame */ "testl $1,0x380(%r13)\n\t" /* thread_data->syscall_trace */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/signal_x86_64.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 8bc39f35f05..2c13db79056 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -439,6 +439,7 @@ C_ASSERT( offsetof(struct callback_stack_layout, machine_frame) == 0x30 ); C_ASSERT( sizeof(struct callback_stack_layout) == 0x58 );
#define RESTORE_FLAGS_INSTRUMENTATION CONTEXT_i386 +#define RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT CONTEXT_ARM
struct syscall_frame { @@ -999,6 +1000,28 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex }
+/*********************************************************************** + * fixup_frame_fpu_state + * + * Set FP frame state not saved in __wine_unix_call_dispatcher from sigcontext. + */ +static void fixup_frame_fpu_state( struct syscall_frame *frame, const ucontext_t *sigcontext ) +{ + XSAVE_FORMAT xsave; + + memset( &frame->xstate, 0, sizeof(frame->xstate) ); + if (user_shared_data->XState.CompactionEnabled) + frame->xstate.CompactionMask = 0x8000000000000000 | user_shared_data->XState.EnabledFeatures; + + if (!FPU_sig(sigcontext)) return; + xsave = *FPU_sig(sigcontext); + memcpy( &xsave.XmmRegisters[6], &frame->xsave.XmmRegisters[6], 10 * sizeof(*xsave.XmmRegisters) ); + xsave.MxCsr = frame->xsave.MxCsr; + frame->xsave = xsave; + frame->xstate.Mask = XSTATE_MASK_LEGACY; +} + + /*********************************************************************** * restore_context * @@ -2076,6 +2099,7 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo )
RIP_sig( sigcontext ) = (ULONG64)__wine_unix_call_dispatcher_prolog_end_ptr; R10_sig( sigcontext ) = RCX_sig( sigcontext ); + fixup_frame_fpu_state( frame, sigcontext ); } else if (siginfo->si_code == 4 /* TRAP_HWBKPT */ && is_inside_syscall( RSP_sig(sigcontext) )) { @@ -2430,6 +2454,12 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) return; } context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; + if (frame->restore_flags & RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT) + { + frame->restore_flags &= ~RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT; + frame->eflags = 0x200; + fixup_frame_fpu_state( frame, ucontext ); + } NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features) { @@ -3141,7 +3171,7 @@ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, "popq 0x70(%rcx)\n\t" /* frame->rip */ __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") __ASM_CFI_REG_IS_AT2(rip, rcx, 0xf0,0x00) - "movl $0,0xb4(%rcx)\n\t" /* frame->restore_flags */ + "movl $0x200000,0xb4(%rcx)\n\t" /* frame->restore_flags <- RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT */ __ASM_LOCAL_LABEL("__wine_unix_call_dispatcher_prolog_end") ":\n\t" "movq %rbx,0x08(%rcx)\n\t" __ASM_CFI_REG_IS_AT1(rbx, rcx, 0x08)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/signal_i386.c | 73 +++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 92a01bb37cb..d4fac225430 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -505,6 +505,8 @@ struct syscall_frame
C_ASSERT( sizeof(struct syscall_frame) == 0x280 );
+#define RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT 0x00008000 + struct x86_thread_data { UINT fs; /* 1d4 TEB selector */ @@ -716,7 +718,7 @@ static inline void *init_handler( const ucontext_t *sigcontext ) * * Save the thread FPU context. */ -static inline void save_fpu( CONTEXT *context ) +static inline void save_fpu( I386_FLOATING_SAVE_AREA *fsave ) { struct { @@ -730,11 +732,10 @@ static inline void save_fpu( CONTEXT *context ) } float_status;
- context->ContextFlags |= CONTEXT_FLOATING_POINT; - __asm__ __volatile__( "fnsave %0; fwait" : "=m" (context->FloatSave) ); + __asm__ __volatile__( "fnsave %0; fwait" : "=m" (*fsave) );
/* Reset unmasked exceptions status to avoid firing an exception. */ - memcpy(&float_status, &context->FloatSave, sizeof(float_status)); + memcpy(&float_status, fsave, sizeof(float_status)); float_status.StatusWord &= float_status.ControlWord | 0xffffff80;
__asm__ __volatile__( "fldenv %0" : : "m" (float_status) ); @@ -805,7 +806,58 @@ static inline void save_context( struct xcontext *xcontext, const ucontext_t *si if (!fpu) fpux_to_fpu( &context->FloatSave, fpux ); if (xstate_extended_features && (xs = XState_sig(fpux))) context_init_xstate( context, xs ); } - if (!fpu && !fpux) save_fpu( context ); + if (!fpu && !fpux) + { + save_fpu( &context->FloatSave ); + context->ContextFlags |= CONTEXT_FLOATING_POINT; + } +} + + +/*********************************************************************** + * fixup_frame_fpu_state + * + * Set FP frame state not saved in __wine_unix_call_dispatcher from sigcontext. + */ +static void fixup_frame_fpu_state( struct syscall_frame *frame, const ucontext_t *sigcontext ) +{ + memset( &frame->xstate, 0, sizeof(frame->xstate) ); + if (user_shared_data->XState.CompactionEnabled) + frame->xstate.CompactionMask = 0x8000000000000000 | user_shared_data->XState.EnabledFeatures; + if (FPUX_sig(sigcontext)) + { + if (user_shared_data->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE]) + frame->u.xsave = *FPUX_sig(sigcontext); + else + fpux_to_fpu( &frame->u.fsave, FPUX_sig(sigcontext) ); + frame->xstate.Mask = XSTATE_MASK_LEGACY; + } + else + { + I386_FLOATING_SAVE_AREA *fsave, fsave_buf; + + if (FPU_sig(sigcontext)) fsave = FPU_sig(sigcontext); + else + { + save_fpu( &fsave_buf ); + fsave = &fsave_buf; + } + if (user_shared_data->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE]) + fpu_to_fpux( &frame->u.xsave, fsave ); + else + frame->u.fsave = *fsave; + } + /* Clear register stack. */ + if (user_shared_data->ProcessorFeatures[PF_XMMI_INSTRUCTIONS_AVAILABLE]) + { + frame->u.xsave.TagWord = 0; + frame->u.xsave.StatusWord = 0; + } + else + { + frame->u.fsave.TagWord = 0xffffffff; + frame->u.fsave.StatusWord = 0xffff0000; + } }
@@ -1865,6 +1917,7 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo ) extern void __wine_unix_call_dispatcher_prolog_end(void);
EIP_sig( sigcontext ) = (ULONG)__wine_unix_call_dispatcher_prolog_end; + fixup_frame_fpu_state( frame, sigcontext ); } else if (siginfo->si_code == 4 /* TRAP_HWBKPT */ && is_inside_syscall( ESP_sig(sigcontext) )) { @@ -2135,6 +2188,12 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) return; } context->c.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; + if (frame->restore_flags & RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT) + { + frame->restore_flags &= ~RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT; + frame->eflags = 0x202; + fixup_frame_fpu_state( frame, ucontext ); + } NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features) { @@ -2697,7 +2756,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher_return, */ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, "movl %fs:0x218,%ecx\n\t" /* thread_data->syscall_frame */ - "movl $0,(%ecx)\n\t" /* frame->restore_flags */ + "movl $0x8000,(%ecx)\n\t" /* frame->restore_flags <- RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT */ "popl 0x08(%ecx)\n\t" /* frame->eip */ __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") __ASM_CFI_REG_IS_AT1(eip, ecx, 0x08) @@ -2734,7 +2793,7 @@ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, __ASM_CFI(".cfi_offset %edi,-20\n\t") "call *(%eax,%edx,4)\n\t" "leal 16(%esp),%esp\n\t" - "testl $0xffff,(%esp)\n\t" /* frame->restore_flags */ + "testl $0x7fff,(%esp)\n\t" /* frame->restore_flags */ "jnz " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") "\n\t" "movl 0x08(%esp),%ecx\n\t" /* frame->eip */ /* switch to user stack */
v5: - Remove patch which saves flags in unix_call_dispatcher. Instead set eflags to default in usr1_handler, renamed frame flag to RESTORE_FLAGS_INCOMPLETE_FRAME_CONTEXT, fixup FPU state and clear that flag in handle_syscall_trap().
Test failure (crash in win32u/d3dkmt.c) looks unrelated, I sent a patch for that: https://gitlab.winehq.org/wine/wine/-/merge_requests/9079
And turns out that was already fixed and Testbot git head is a bit behind, as my local tree was where I reproduced that test crash.