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.