Turning a SIGSEGV into a regular function call under Linux, allowing throw

FeepingCreature default_357-line at yahoo.de
Tue Mar 13 03:09:54 PDT 2012


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");
}


More information about the Digitalmars-d mailing list