[PATCH v2 1/3] ntdll: Allow raising second chance exceptions from signal handlers.

Jinoh Kang jinoh.kang.kr at gmail.com
Mon Dec 6 08:24:47 CST 2021


On 12/6/21 23:18, Paul Gofman wrote:
> Do I understand correctly that the main purpose of these patches is to provide the debugger an opportunity to see (and potentially handle) second chance exception?

Yes, certainly.

> As it probably doesn't matter too much how exactly the process will fail after second chance exception in the absence of debugger.

Other than the TerminateProcess() exit code, yes.

> In any case, that probably deserves some debugger tests?

I'm already working on the tests.  The reason why I posted the implementation first
is twofold:

1. I'd like to hear some feedback first, in case I did something obviously wrong or
   before my work go completely south.

2. Frankly, I dont't have direct access to Windows 7/8 test machines, so... 😅

> The majority of existing debugger tests is in ntdll/tests/exception.c:test_debugger().
> 
> On 12/6/21 16:55, Jinoh Kang wrote:
>> This is required to implement some instructions that raise second chance
>> exceptions directly, such as those emitted from the __fastfail()
>> intrinsic function.
>>
>> Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
>> ---
>>
>> Notes:
>>      v1 -> v2: proper patch splitting
>>
>>   dlls/ntdll/unix/signal_arm.c    | 18 ++++++++++++------
>>   dlls/ntdll/unix/signal_arm64.c  | 22 ++++++++++++++--------
>>   dlls/ntdll/unix/signal_i386.c   | 21 ++++++++++++++-------
>>   dlls/ntdll/unix/signal_x86_64.c | 18 ++++++++++++------
>>   dlls/ntdll/unix/thread.c        | 28 +++++++++++++++++++---------
>>   dlls/ntdll/unix/unix_private.h  |  1 +
>>   6 files changed, 72 insertions(+), 36 deletions(-)
>>
>> diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c
>> index ebc08984adf..dbc2770f72e 100644
>> --- a/dlls/ntdll/unix/signal_arm.c
>> +++ b/dlls/ntdll/unix/signal_arm.c
>> @@ -576,7 +576,7 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline,
>>    *
>>    * Modify the signal context to call the exception raise function.
>>    */
>> -static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>> +static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, BOOL first_chance )
>>   {
>>       struct
>>       {
>> @@ -591,13 +591,19 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>>       rec->ExceptionAddress = (void *)PC_sig(sigcontext);
>>       save_context( &context, sigcontext );
>>   -    status = send_debug_event( rec, &context, TRUE );
>> +    status = send_debug_event( rec, &context, first_chance );
>>       if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
>>       {
>>           restore_context( &context, sigcontext );
>>           return;
>>       }
>>   +    if (!first_chance)
>> +    {
>> +        handle_second_chance_exception( rec );
>> +        return;
>> +    }
>> +
>>       stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec );
>>       stack->rec = *rec;
>>       stack->context = context;
>> @@ -834,7 +840,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           break;
>>       }
>>       if (handle_syscall_fault( context, &rec )) return;
>> -    setup_exception( context, &rec );
>> +    setup_exception( context, &rec, TRUE );
>>   }
>>     @@ -858,7 +864,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.NumberParameters = 1;
>>           break;
>>       }
>> -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -915,7 +921,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
>>           break;
>>       }
>> -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -944,7 +950,7 @@ static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>   {
>>       EXCEPTION_RECORD rec = { EXCEPTION_WINE_ASSERTION, EH_NONCONTINUABLE };
>>   -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
>> index 680a14223c6..ef70d4df44b 100644
>> --- a/dlls/ntdll/unix/signal_arm64.c
>> +++ b/dlls/ntdll/unix/signal_arm64.c
>> @@ -618,7 +618,7 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline,
>>    *
>>    * Modify the signal context to call the exception raise function.
>>    */
>> -static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>> +static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, BOOL first_chance )
>>   {
>>       struct
>>       {
>> @@ -634,13 +634,19 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>>       rec->ExceptionAddress = (void *)PC_sig(sigcontext);
>>       save_context( &context, sigcontext );
>>   -    status = send_debug_event( rec, &context, TRUE );
>> +    status = send_debug_event( rec, &context, first_chance );
>>       if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
>>       {
>>           restore_context( &context, sigcontext );
>>           return;
>>       }
>>   +    if (!first_chance)
>> +    {
>> +        handle_second_chance_exception( rec );
>> +        return;
>> +    }
>> +
>>       stack = virtual_setup_exception( stack_ptr, (sizeof(*stack) + 15) & ~15, rec );
>>       stack->rec = *rec;
>>       stack->context = context;
>> @@ -861,7 +867,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>                                                 (void *)SP_sig(context) );
>>       if (!rec.ExceptionCode) return;
>>       if (handle_syscall_fault( context, &rec )) return;
>> -    setup_exception( context, &rec );
>> +    setup_exception( context, &rec, TRUE );
>>   }
>>     @@ -874,7 +880,7 @@ static void ill_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>   {
>>       EXCEPTION_RECORD rec = { EXCEPTION_ILLEGAL_INSTRUCTION };
>>   -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -887,7 +893,7 @@ static void bus_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>   {
>>       EXCEPTION_RECORD rec = { EXCEPTION_DATATYPE_MISALIGNMENT };
>>   -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -911,7 +917,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.NumberParameters = 1;
>>           break;
>>       }
>> -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -968,7 +974,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
>>           break;
>>       }
>> -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     @@ -997,7 +1003,7 @@ static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>   {
>>       EXCEPTION_RECORD rec = { EXCEPTION_WINE_ASSERTION, EH_NONCONTINUABLE };
>>   -    setup_exception( sigcontext, &rec );
>> +    setup_exception( sigcontext, &rec, TRUE );
>>   }
>>     diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c
>> index bf3abc1a587..e18aed2c222 100644
>> --- a/dlls/ntdll/unix/signal_i386.c
>> +++ b/dlls/ntdll/unix/signal_i386.c
>> @@ -1400,7 +1400,8 @@ static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *r
>>    * Change context to setup a call to a raise exception function.
>>    */
>>   static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
>> -                                   EXCEPTION_RECORD *rec, struct xcontext *xcontext )
>> +                                   EXCEPTION_RECORD *rec, struct xcontext *xcontext,
>> +                                   BOOL first_chance )
>>   {
>>       CONTEXT *context = &xcontext->c;
>>       size_t stack_size;
>> @@ -1420,7 +1421,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
>>     C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) );
>>   -    NTSTATUS status = send_debug_event( rec, context, TRUE );
>> +    NTSTATUS status = send_debug_event( rec, context, first_chance );
>>         if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
>>       {
>> @@ -1428,6 +1429,12 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout))
>>           return;
>>       }
>>   +    if (!first_chance)
>> +    {
>> +        handle_second_chance_exception( rec );
>> +        return;
>> +    }
>> +
>>       /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
>>       if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--;
>>   @@ -1482,7 +1489,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>>       struct xcontext xcontext;
>>       void *stack = setup_exception_record( sigcontext, rec, &xcontext );
>>   -    setup_raise_exception( sigcontext, stack, rec, &xcontext );
>> +    setup_raise_exception( sigcontext, stack, rec, &xcontext, TRUE );
>>   }
>>     /* stack layout when calling an user apc function.
>> @@ -1675,7 +1682,7 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
>>           rec->ExceptionInformation[0] = context->Eax;
>>           rec->ExceptionInformation[1] = context->Ecx;
>>           rec->ExceptionInformation[2] = context->Edx;
>> -        setup_raise_exception( sigcontext, stack, rec, xcontext );
>> +        setup_raise_exception( sigcontext, stack, rec, xcontext, TRUE );
>>           return TRUE;
>>       default:
>>           return FALSE;
>> @@ -1848,7 +1855,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           break;
>>       }
>>       if (handle_syscall_fault( ucontext, stack, &rec, &xcontext.c )) return;
>> -    setup_raise_exception( ucontext, stack, &rec, &xcontext );
>> +    setup_raise_exception( ucontext, stack, &rec, &xcontext, TRUE );
>>   }
>>     @@ -1894,7 +1901,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.ExceptionInformation[2] = 0; /* FIXME */
>>           break;
>>       }
>> -    setup_raise_exception( sigcontext, stack, &rec, &xcontext );
>> +    setup_raise_exception( sigcontext, stack, &rec, &xcontext, TRUE );
>>   }
>>     @@ -1939,7 +1946,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
>>           break;
>>       }
>> -    setup_raise_exception( sigcontext, stack, &rec, &xcontext );
>> +    setup_raise_exception( sigcontext, stack, &rec, &xcontext, TRUE );
>>   }
>>     diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
>> index 9e9a96db832..ff4a40c7933 100644
>> --- a/dlls/ntdll/unix/signal_x86_64.c
>> +++ b/dlls/ntdll/unix/signal_x86_64.c
>> @@ -2110,7 +2110,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size )
>>   /***********************************************************************
>>    *           setup_raise_exception
>>    */
>> -static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
>> +static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext, BOOL first_chance )
>>   {
>>       void *stack_ptr = (void *)(RSP_sig(sigcontext) & ~15);
>>       CONTEXT *context = &xcontext->c;
>> @@ -2136,13 +2136,19 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
>>           context->EFlags &= ~0x100;  /* clear single-step flag */
>>       }
>>   -    status = send_debug_event( rec, context, TRUE );
>> +    status = send_debug_event( rec, context, first_chance );
>>       if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
>>       {
>>           restore_context( xcontext, sigcontext );
>>           return;
>>       }
>>   +    if (!first_chance)
>> +    {
>> +        handle_second_chance_exception( rec );
>> +        return;
>> +    }
>> +
>>       /* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
>>       if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--;
>>   @@ -2193,7 +2199,7 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
>>         rec->ExceptionAddress = (void *)RIP_sig(sigcontext);
>>       save_context( &context, sigcontext );
>> -    setup_raise_exception( sigcontext, rec, &context );
>> +    setup_raise_exception( sigcontext, rec, &context, TRUE );
>>   }
>>     @@ -2461,7 +2467,7 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
>>       default:
>>           return FALSE;
>>       }
>> -    setup_raise_exception( sigcontext, rec, xcontext );
>> +    setup_raise_exception( sigcontext, rec, xcontext, TRUE );
>>       return TRUE;
>>   }
>>   @@ -2614,7 +2620,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           break;
>>       }
>>       if (handle_syscall_fault( sigcontext, &rec, &context.c )) return;
>> -    setup_raise_exception( sigcontext, &rec, &context );
>> +    setup_raise_exception( sigcontext, &rec, &context, TRUE );
>>   }
>>     @@ -2658,7 +2664,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
>>           rec.ExceptionInformation[0] = 0;
>>           break;
>>       }
>> -    setup_raise_exception( sigcontext, &rec, &context );
>> +    setup_raise_exception( sigcontext, &rec, &context, TRUE );
>>   }
>>     diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
>> index 455272e07e0..d7204b2fd3b 100644
>> --- a/dlls/ntdll/unix/thread.c
>> +++ b/dlls/ntdll/unix/thread.c
>> @@ -87,6 +87,24 @@ static inline int get_unix_exit_code( NTSTATUS status )
>>   }
>>     +/***********************************************************************
>> + *           handle_second_chance_exception
>> + *
>> + * Handle a second chance exception.
>> + */
>> +void handle_second_chance_exception( EXCEPTION_RECORD *rec )
>> +{
>> +    if (rec->ExceptionFlags & EH_STACK_INVALID)
>> +        ERR_(seh)("Exception frame is not in stack limits => unable to dispatch exception.\n");
>> +    else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION)
>> +        ERR_(seh)("Process attempted to continue execution after noncontinuable exception.\n");
>> +    else
>> +        ERR_(seh)("Unhandled exception code %x flags %x addr %p\n",
>> +                  rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
>> +
>> +    NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
>> +}
>> +
>>   /***********************************************************************
>>    *           fpux_to_fpu
>>    *
>> @@ -1509,15 +1527,7 @@ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL
>>         if (first_chance) return call_user_exception_dispatcher( rec, context );
>>   -    if (rec->ExceptionFlags & EH_STACK_INVALID)
>> -        ERR_(seh)("Exception frame is not in stack limits => unable to dispatch exception.\n");
>> -    else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION)
>> -        ERR_(seh)("Process attempted to continue execution after noncontinuable exception.\n");
>> -    else
>> -        ERR_(seh)("Unhandled exception code %x flags %x addr %p\n",
>> -                  rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
>> -
>> -    NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
>> +    handle_second_chance_exception( rec );
>>       return STATUS_SUCCESS;
>>   }
>>   diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
>> index 0dcb09ad641..d5414474d2e 100644
>> --- a/dlls/ntdll/unix/unix_private.h
>> +++ b/dlls/ntdll/unix/unix_private.h
>> @@ -172,6 +172,7 @@ extern void server_init_process_done(void) DECLSPEC_HIDDEN;
>>   extern void server_init_thread( void *entry_point, BOOL *suspend ) DECLSPEC_HIDDEN;
>>   extern int server_pipe( int fd[2] ) DECLSPEC_HIDDEN;
>>   +void handle_second_chance_exception( EXCEPTION_RECORD *rec );
>>   extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux ) DECLSPEC_HIDDEN;
>>   extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu ) DECLSPEC_HIDDEN;
>>   extern void *get_cpu_area( USHORT machine ) DECLSPEC_HIDDEN;
> 
> 

-- 
Sincerely,
Jinoh Kang



More information about the wine-devel mailing list