On 03/13/12 11:23, deadalnix wrote:
> Le 13/03/2012 11:09, FeepingCreature a écrit :
>> Note: I worked out this method for my own language, Neat, but the basic 
>> approach should be portable to D's exceptions as well.
>>
>> I've seen it argued a lot over the years (even argued it myself) that it's 
>> impossible to throw from Linux signal handlers. This is basically correct, 
>> because they constitute an interruption in the stack that breaks exceptions' 
>> ability to unroll properly.
>>
>> However, there is a method to turn a signal handler into a regular function 
>> call that you can throw from.
>>
>> Basically, what we need to do is similar to a stack buffer overflow exploit. 
>> Under Linux, the extended signal handler that is set with sigaction is 
>> called with three arguments: the signal, a siginfo_t* and a ucontext_t* as 
>> the third.
>>
>> The third parameter is what we're interested in. Deep inside the ucontext_t 
>> struct is uc.mcontext.gregs[REG_EIP], the address of the instruction that 
>> caused the segfault. This is the location that execution returns to when the 
>> signal handler returns. By overwriting this location, we can turn a return 
>> into a function call.
>>
>> First, gregs[REG_EAX] = gregs[REG_EIP];
>>
>> We can safely assume that the function that caused the segfault doesn't 
>> really need its EAX anymore, so we can reuse it to reconstruct a proper 
>> stackframe to throw from later.
>>
>> Second, gregs[REG_EIP] = cast(void*)&sigsegv_userspace_handler;
>>
>> Note that the naked attribute was not used. If used, it can make this code 
>> slightly easier.
>>
>> extern(C) void sigsegv_userspace_handler() {
>>    // done implicitly
>>    // asm { push ebp; }
>>    // asm { mov ebp, esp; }
>>    asm { mov ebx, [esp]; } // backup the pushed ebp
>>    asm { mov [esp], eax; } // replace it with the correct return address
>>                            // which was originally left out due to the
>>                            // irregular way we entered this function (via a 
>> ret).
>>    asm { push ebx; }       // recreate the pushed ebp
>>    asm { mov ebp, esp; }   // complete stackframe.
>>    // originally, our stackframe (because we entered this function via a ret)
>>    // was [ebp]. Now, it's [return address][ebp], as is proper for cdecl.
>>    // at this point, we can safely throw
>>    // (or invoke any other non-handler-safe function).
>>    throw new SignalException("SIGSEGV");
>> }
> 
> And is this Exception recoverable in a safe way ?
> 

I'm not familiar with recovering. Note that you can _not_ safely return from 
the userspace handler, because we overwrote EAX to make space for our ESI 
backup.

You'd need to find somewhere else to stick that backup, like a TLS global 
variable or some known part of the stack.

> The ucontext_t struct is system dependent. So this is tricky.
> 

Yeah, this is Linux only.

Reply via email to