View mode: basic / threaded / horizontal-split · Log in · Help
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On Wednesday, 14 March 2012 at 19:48:28 UTC, deadalnix wrote:
> Le 14/03/2012 18:28, Vladimir Panteleev a écrit :
>> On Wednesday, 14 March 2012 at 17:18:06 UTC, deadalnix wrote:
>>> Le 14/03/2012 18:00, Vladimir Panteleev a écrit :
>>>> On Wednesday, 14 March 2012 at 16:37:45 UTC, deadalnix wrote:
>>>>> Le 14/03/2012 17:08, Vladimir Panteleev a écrit :
>>>>>> On Wednesday, 14 March 2012 at 11:11:54 UTC, deadalnix 
>>>>>> wrote:
>>>>>>> You are loosing EAX in the process.
>>>>>>
>>>>>> When would this matter? EAX is a scratch register per 
>>>>>> ABIs, no?
>>>>>
>>>>> You may want to return from the function the standard way 
>>>>> an resume
>>>>> operations. To implement a moving GC using page protection 
>>>>> for example.
>>>>
>>>> This doesn't have anything to do with turning signals into 
>>>> exceptions.
>>>
>>> No but this does, make sense to catch segfault and act 
>>> according to it
>>> to implement such a functionality. This is a very close 
>>> problem.
>>
>> You can't resume D exceptions.
>
> I'm not talking about Exception anymore. In case of Exception, 
> this isn't a problem, but in case of regular return, this is.

I don't understand how any of your posts are related to this 
thread at all.

This thread is about turning SIGSEGV into an exception that 1) 
you can catch 2) will print a stack trace when uncaught. You've 
brought in stack overflows, moving garbage collectors, etc. I 
assure you, we are well-aware of the problems when using this 
exact code for other purposes.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On 13/03/12 11:09, FeepingCreature wrote:
> 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");
> }

I didn't realize that was possible. Very interesting.
As it stands, though, that's got some pretty serious issues.

You are on the stack of the function that was called, but you don't know 
for sure that it is a valid stack.

asm {
    push EBX;
    mov EBX, ESP;
    mov ESP, 0;    // Look ma, no stack!

    mov int ptr [ESP], 0; // segfault -- null pointer exception

    mov ESP, EBX;
    pop EBX;
}

Now, your user space handler will cause another segfault when it does 
the mov [ESP], 0. I think that gives you an infinite loop.

I think the idea would work, if you had some guarantee that the stack 
pointer was valid. Then, call a separate handler if it is not.
The primary 'trick' in Windows SEH is that it goes to great lengths to 
verify that the stack is valid. I'm not sure that in Linux user space 
you have enough information to verify it. But maybe you do. At least, 
you should be able to check that it's in memory which is owned by your 
process.

Would be awesome if it is possible.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
Le 14/03/2012 21:07, Vladimir Panteleev a écrit :
> On Wednesday, 14 March 2012 at 19:48:28 UTC, deadalnix wrote:
>> Le 14/03/2012 18:28, Vladimir Panteleev a écrit :
>>> On Wednesday, 14 March 2012 at 17:18:06 UTC, deadalnix wrote:
>>>> Le 14/03/2012 18:00, Vladimir Panteleev a écrit :
>>>>> On Wednesday, 14 March 2012 at 16:37:45 UTC, deadalnix wrote:
>>>>>> Le 14/03/2012 17:08, Vladimir Panteleev a écrit :
>>>>>>> On Wednesday, 14 March 2012 at 11:11:54 UTC, deadalnix wrote:
>>>>>>>> You are loosing EAX in the process.
>>>>>>>
>>>>>>> When would this matter? EAX is a scratch register per ABIs, no?
>>>>>>
>>>>>> You may want to return from the function the standard way an resume
>>>>>> operations. To implement a moving GC using page protection for
>>>>>> example.
>>>>>
>>>>> This doesn't have anything to do with turning signals into exceptions.
>>>>
>>>> No but this does, make sense to catch segfault and act according to it
>>>> to implement such a functionality. This is a very close problem.
>>>
>>> You can't resume D exceptions.
>>
>> I'm not talking about Exception anymore. In case of Exception, this
>> isn't a problem, but in case of regular return, this is.
>
> I don't understand how any of your posts are related to this thread at all.
>
> This thread is about turning SIGSEGV into an exception that 1) you can
> catch 2) will print a stack trace when uncaught. You've brought in stack
> overflows, moving garbage collectors, etc. I assure you, we are
> well-aware of the problems when using this exact code for other purposes.

The topic is *Turning a SIGSEGV into a regular function call under 
Linux, allowing throw*, not only Exception. I don't understand what is 
the problem here ? Can't we talk about how we could keep trash register 
clean in case we don't throw  - this doesn't make much sense if we throw 
anyway - ?

What your are mentioning here is already done. Nothing to discuss about 
that. This is why I try to jump into the next topic : how can we do more 
than just throwing.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On Wednesday, 14 March 2012 at 20:20:05 UTC, deadalnix wrote:
> The topic is *Turning a SIGSEGV into a regular function call 
> under Linux, allowing throw*, not only Exception. I don't 
> understand what is the problem here ? Can't we talk about how 
> we could keep trash register clean in case we don't throw  - 
> this doesn't make much sense if we throw anyway - ?
>
> What your are mentioning here is already done. Nothing to 
> discuss about that. This is why I try to jump into the next 
> topic : how can we do more than just throwing.

OK. But (to me, at least) you sounded like you were criticizing 
the implementation for solving that specific task, so it would 
help if you were clearer of your intentions. For example, losing 
the contents EAX is relativery harmless, but the contents of EBP, 
EGS etc. can be very important.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On Wed, 14 Mar 2012 16:08:29 -0400, Don Clugston <dac@nospam.com> wrote:

> Now, your user space handler will cause another segfault when it does  
> the mov [ESP], 0. I think that gives you an infinite loop.

SEGFAULT inside a SEGV signal handler aborts the program (no way to turn  
this off IIRC).

-Steve
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On 14/03/12 21:31, Steven Schveighoffer wrote:
> On Wed, 14 Mar 2012 16:08:29 -0400, Don Clugston <dac@nospam.com> wrote:
>
>> Now, your user space handler will cause another segfault when it does
>> the mov [ESP], 0. I think that gives you an infinite loop.
>
> SEGFAULT inside a SEGV signal handler aborts the program (no way to turn
> this off IIRC).
>
> -Steve

But you're not inside the signal handler when it happens. You returned.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On Wed, 14 Mar 2012 16:45:49 -0400, Don Clugston <dac@nospam.com> wrote:

> On 14/03/12 21:31, Steven Schveighoffer wrote:
>> On Wed, 14 Mar 2012 16:08:29 -0400, Don Clugston <dac@nospam.com> wrote:
>>
>>> Now, your user space handler will cause another segfault when it does
>>> the mov [ESP], 0. I think that gives you an infinite loop.
>>
>> SEGFAULT inside a SEGV signal handler aborts the program (no way to turn
>> this off IIRC).
>>
>> -Steve
>
> But you're not inside the signal handler when it happens. You returned.

Then how does the signal handler do anything?  I mean, doesn't it need a  
stack?  Or does it just affect register variables?  Most signal handlers  
are normal functions, and isn't there some usage of the stack to save  
registers?

It seems there should be a way to turn off the signal handler during the  
time when you are suspicous of the stack being the culprit, then re-engage  
the signal handler before throwing the error.

-Steve
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On 03/14/12 21:08, Don Clugston wrote:
> 
> I didn't realize that was possible. Very interesting.
> As it stands, though, that's got some pretty serious issues.
> 
> You are on the stack of the function that was called, but you don't know for sure that it is a valid stack.
> 
> asm {
>     push EBX;
>     mov EBX, ESP;
>     mov ESP, 0;    // Look ma, no stack!
> 
>     mov int ptr [ESP], 0; // segfault -- null pointer exception
> 
>     mov ESP, EBX;
>     pop EBX;
> }
> 
> Now, your user space handler will cause another segfault when it does the mov [ESP], 0. I think that gives you an infinite loop.
> 

I think that case is sufficiently rare that it'd have to count somewhere between "act of god" and "outright developer malice". The assumption that the stack frame is valid is, I'd say, safe to make in the vast majority of cases. You pretty much have to actively try to break it, for no clearly discernible reason.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
On Mar 14, 2012, at 1:54 PM, FeepingCreature wrote:
> 
> I think that case is sufficiently rare that it'd have to count somewhere between "act of god" and "outright developer malice". The assumption that the stack frame is valid is, I'd say, safe to make in the vast majority of cases. You pretty much have to actively try to break it, for no clearly discernible reason.

The prevalence of buffer overflow attacks might suggest otherwise.
March 14, 2012
Re: Turning a SIGSEGV into a regular function call under Linux, allowing throw
Le 14/03/2012 21:28, Vladimir Panteleev a écrit :
> On Wednesday, 14 March 2012 at 20:20:05 UTC, deadalnix wrote:
>> The topic is *Turning a SIGSEGV into a regular function call under
>> Linux, allowing throw*, not only Exception. I don't understand what is
>> the problem here ? Can't we talk about how we could keep trash
>> register clean in case we don't throw - this doesn't make much sense
>> if we throw anyway - ?
>>
>> What your are mentioning here is already done. Nothing to discuss
>> about that. This is why I try to jump into the next topic : how can we
>> do more than just throwing.
>
> OK. But (to me, at least) you sounded like you were criticizing the
> implementation for solving that specific task, so it would help if you
> were clearer of your intentions. For example, losing the contents EAX is
> relativery harmless, but the contents of EBP, EGS etc. can be very
> important.

I'm not criticizing at all ! I think this is awesome ! I'm just trying 
to discuss way we can get to the next step.

Loosing EAX is harmless in the throwing case, but it is a problem for 
other tasks.

I didn't mentioned this into the topic, but I'm very enthusiastic about 
that !
1 2 3 4
Top | Discussion index | About this forum | D home