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 ?

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

The Exception should be an Error to comply with nothrow spec.

Reply via email to