* sigaction with SA_SIGINFO does not pass the correct return context
@ 2008-09-03 17:26 Alin Jula
2008-09-04 7:33 ` Mikael Pettersson
0 siblings, 1 reply; 3+ messages in thread
From: Alin Jula @ 2008-09-03 17:26 UTC (permalink / raw)
To: linux-kernel; +Cc: alin.jula
Summary: sigaction with SG_SIGINFO passes the incorrect context on
OpenSuse (kernels 2.6.16.21-0.8-smp and 2.6.25.11-0.1-default).
However, it passes the correct one on Ubuntu (kernel
2.6.24-19-generic).
I'm trying to catch the SIGSEGV signal, do something, and then return
control back to the application. I use "sigaction" with the SA_SIGINFO
flag. This flags allows a handler to be called upon a signal, and it
should (POSIX defined) also provide the context of the thread when the
signal was issued. The problem is that the right context does _not_ work
on OpenSuse, but it works on Ubuntu.
So I was wondering if there is a specific action that one needs to take
on OpenSuse when dealing with this issue, or if there is a bug in the
kernel/opensuse ?
On Ubuntu (kernel 2.6.24-19-generic), sigaction correctly provides the
context of the thread that raised the signal. However, on two other
installations of OpenSuse (kernels 2.6.16.21-0.8-smp and
2.6.25.11-0.1-default), sigaction provides a different context.
Below is the code example, with its summary. 4KB get allocated with
mmap, with "not to be accessed" protection. A write to this memory chunk
raises a SIGSEGV signal. This signal is then caught, and the memory
protection is changed to read+write. Then, the control is given back to
the instruction that raised this signal, and the application should
proceed from there. The code works on Ubuntu, but on the both OpenSuse
installations that I tried (see above for kernels), the context passed
by sigaction is the context of the handler instead of the instruction
that raised the signal.
Here is the code:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <ucontext.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
ucontext_t context_trigger, context_sig;
struct sigaction sa_new, sa_old;
static void
handler(int sig, siginfo_t *si, void *context)
{
if ((sig == SIGSEGV) || (sig == SIGBUS) )
{
printf("\nGot SIGSEGV or SIGBUS at address: 0x%lx and context
=%lx\n",(long) si->si_addr, context);
if (mprotect(si->si_addr, 4096 ,PROT_READ | PROT_WRITE) == -1)
handle_error("mprotect failed from the handler");
else
printf("\nmprotect set the protection to RW\n");
#if 1
// The intention here is to switch back to the context that signaled
the SIGSEGV. Unfortunately, it does not
// do that. Instead, it switches the context back to the handler
function, in a recursive call.
swapcontext(&context_sig, (ucontext_t*)context);
#else
// Manually set the context to the point previous to accessing the
memory. This works !
swapcontext(&context_sig, &context_trigger);
#endif
}
}
int
main(int argc, char *argv[])
{
/* Set the handler */
sa_new.sa_flags = SA_SIGINFO;
sigemptyset(&sa_new.sa_mask);
sa_new.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa_new, &sa_old) == -1)
handle_error("sigaction");
/* Allocate 4096 bytes with initial protection PROT_NONE */
char* p = (char*)mmap(0,4096,PROT_NONE, MAP_ANONYMOUS |
MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED)
handle_error("mmap");
getcontext(&context_trigger);
/* Access the protected memory, which will raise a SIGSEGV or SIGBUS
*/
*(p) = 'a';
printf("\nSuccess\n");
exit(EXIT_SUCCESS);
}
Any help/pointer would be greatly appreciated.
Thanks,
Alin Jula
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: sigaction with SA_SIGINFO does not pass the correct return context
2008-09-03 17:26 sigaction with SA_SIGINFO does not pass the correct return context Alin Jula
@ 2008-09-04 7:33 ` Mikael Pettersson
2008-09-05 20:49 ` Alin Jula
0 siblings, 1 reply; 3+ messages in thread
From: Mikael Pettersson @ 2008-09-04 7:33 UTC (permalink / raw)
To: alin.jula; +Cc: linux-kernel
Alin Jula writes:
> Summary: sigaction with SG_SIGINFO passes the incorrect context on
> OpenSuse (kernels 2.6.16.21-0.8-smp and 2.6.25.11-0.1-default).
> However, it passes the correct one on Ubuntu (kernel
> 2.6.24-19-generic).
>
> I'm trying to catch the SIGSEGV signal, do something, and then return
> control back to the application. I use "sigaction" with the SA_SIGINFO
> flag. This flags allows a handler to be called upon a signal, and it
> should (POSIX defined) also provide the context of the thread when the
> signal was issued. The problem is that the right context does _not_ work
> on OpenSuse, but it works on Ubuntu.
>
> So I was wondering if there is a specific action that one needs to take
> on OpenSuse when dealing with this issue, or if there is a bug in the
> kernel/opensuse ?
>
> On Ubuntu (kernel 2.6.24-19-generic), sigaction correctly provides the
> context of the thread that raised the signal. However, on two other
> installations of OpenSuse (kernels 2.6.16.21-0.8-smp and
> 2.6.25.11-0.1-default), sigaction provides a different context.
>
> Below is the code example, with its summary. 4KB get allocated with
> mmap, with "not to be accessed" protection. A write to this memory chunk
> raises a SIGSEGV signal. This signal is then caught, and the memory
> protection is changed to read+write. Then, the control is given back to
> the instruction that raised this signal, and the application should
> proceed from there. The code works on Ubuntu, but on the both OpenSuse
> installations that I tried (see above for kernels), the context passed
> by sigaction is the context of the handler instead of the instruction
> that raised the signal.
>
> Here is the code:
> #include <unistd.h>
> #include <signal.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <sys/mman.h>
> #include <ucontext.h>
>
> #define handle_error(msg) \
> do { perror(msg); exit(EXIT_FAILURE); } while (0)
>
>
> ucontext_t context_trigger, context_sig;
> struct sigaction sa_new, sa_old;
>
> static void
> handler(int sig, siginfo_t *si, void *context)
> {
> if ((sig == SIGSEGV) || (sig == SIGBUS) )
> {
> printf("\nGot SIGSEGV or SIGBUS at address: 0x%lx and context
> =%lx\n",(long) si->si_addr, context);
>
> if (mprotect(si->si_addr, 4096 ,PROT_READ | PROT_WRITE) == -1)
> handle_error("mprotect failed from the handler");
> else
> printf("\nmprotect set the protection to RW\n");
>
> #if 1
> // The intention here is to switch back to the context that signaled
> the SIGSEGV. Unfortunately, it does not
> // do that. Instead, it switches the context back to the handler
> function, in a recursive call.
> swapcontext(&context_sig, (ucontext_t*)context);
> #else
> // Manually set the context to the point previous to accessing the
> memory. This works !
> swapcontext(&context_sig, &context_trigger);
> #endif
> }
>
> }
>
> int
> main(int argc, char *argv[])
> {
> /* Set the handler */
> sa_new.sa_flags = SA_SIGINFO;
> sigemptyset(&sa_new.sa_mask);
> sa_new.sa_sigaction = handler;
> if (sigaction(SIGSEGV, &sa_new, &sa_old) == -1)
> handle_error("sigaction");
>
>
> /* Allocate 4096 bytes with initial protection PROT_NONE */
>
> char* p = (char*)mmap(0,4096,PROT_NONE, MAP_ANONYMOUS |
> MAP_PRIVATE, -1, 0);
> if (p == MAP_FAILED)
> handle_error("mmap");
>
> getcontext(&context_trigger);
>
> /* Access the protected memory, which will raise a SIGSEGV or SIGBUS
> */
> *(p) = 'a';
>
> printf("\nSuccess\n");
> exit(EXIT_SUCCESS);
> }
>
>
> Any help/pointer would be greatly appreciated.
Your're doing this all wrong.
An SA_SIGINFO signal handler gets a pointer to the faulting context
in its third parameter. To modify it you assign fields in it, e.g.
the program counter. Then you return from the signal handler. Upon
return the kernel reloads this possibly modified context.
If you don't modify it the faulting instruction will usually be
rexecuted (though x87 fp exceptions are delayed so the original
faulting instruction will be skipped). In your simple example all
you need to do is to mprotect the page and return without changing
the context.
Last time I checked getcontext/swapcontext weren't system calls,
so what they do is up to whetever libc your code is linked to.
For examples of FP signal handlers look in the runtime system of
the Erlang/OTP system, and I'm sure lots of garbage collectors and
distributed shared memory systems out there use sigsegv handlers.
Finally, a distro specific problem should be taken up with that
distro.
/Mikael
( mikpe at acumem dot com )
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: sigaction with SA_SIGINFO does not pass the correct return context
2008-09-04 7:33 ` Mikael Pettersson
@ 2008-09-05 20:49 ` Alin Jula
0 siblings, 0 replies; 3+ messages in thread
From: Alin Jula @ 2008-09-05 20:49 UTC (permalink / raw)
To: Mikael Pettersson; +Cc: linux-kernel
Thanks for the pointers, Mikael. I appreciate it.
Alin
On Thu, 2008-09-04 at 09:33 +0200, Mikael Pettersson wrote:
> Alin Jula writes:
> > Summary: sigaction with SG_SIGINFO passes the incorrect context on
> > OpenSuse (kernels 2.6.16.21-0.8-smp and 2.6.25.11-0.1-default).
> > However, it passes the correct one on Ubuntu (kernel
> > 2.6.24-19-generic).
> >
> > I'm trying to catch the SIGSEGV signal, do something, and then return
> > control back to the application. I use "sigaction" with the SA_SIGINFO
> > flag. This flags allows a handler to be called upon a signal, and it
> > should (POSIX defined) also provide the context of the thread when the
> > signal was issued. The problem is that the right context does _not_ work
> > on OpenSuse, but it works on Ubuntu.
> >
> > So I was wondering if there is a specific action that one needs to take
> > on OpenSuse when dealing with this issue, or if there is a bug in the
> > kernel/opensuse ?
> >
> > On Ubuntu (kernel 2.6.24-19-generic), sigaction correctly provides the
> > context of the thread that raised the signal. However, on two other
> > installations of OpenSuse (kernels 2.6.16.21-0.8-smp and
> > 2.6.25.11-0.1-default), sigaction provides a different context.
> >
> > Below is the code example, with its summary. 4KB get allocated with
> > mmap, with "not to be accessed" protection. A write to this memory chunk
> > raises a SIGSEGV signal. This signal is then caught, and the memory
> > protection is changed to read+write. Then, the control is given back to
> > the instruction that raised this signal, and the application should
> > proceed from there. The code works on Ubuntu, but on the both OpenSuse
> > installations that I tried (see above for kernels), the context passed
> > by sigaction is the context of the handler instead of the instruction
> > that raised the signal.
> >
> > Here is the code:
> > #include <unistd.h>
> > #include <signal.h>
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <sys/mman.h>
> > #include <ucontext.h>
> >
> > #define handle_error(msg) \
> > do { perror(msg); exit(EXIT_FAILURE); } while (0)
> >
> >
> > ucontext_t context_trigger, context_sig;
> > struct sigaction sa_new, sa_old;
> >
> > static void
> > handler(int sig, siginfo_t *si, void *context)
> > {
> > if ((sig == SIGSEGV) || (sig == SIGBUS) )
> > {
> > printf("\nGot SIGSEGV or SIGBUS at address: 0x%lx and context
> > =%lx\n",(long) si->si_addr, context);
> >
> > if (mprotect(si->si_addr, 4096 ,PROT_READ | PROT_WRITE) == -1)
> > handle_error("mprotect failed from the handler");
> > else
> > printf("\nmprotect set the protection to RW\n");
> >
> > #if 1
> > // The intention here is to switch back to the context that signaled
> > the SIGSEGV. Unfortunately, it does not
> > // do that. Instead, it switches the context back to the handler
> > function, in a recursive call.
> > swapcontext(&context_sig, (ucontext_t*)context);
> > #else
> > // Manually set the context to the point previous to accessing the
> > memory. This works !
> > swapcontext(&context_sig, &context_trigger);
> > #endif
> > }
> >
> > }
> >
> > int
> > main(int argc, char *argv[])
> > {
> > /* Set the handler */
> > sa_new.sa_flags = SA_SIGINFO;
> > sigemptyset(&sa_new.sa_mask);
> > sa_new.sa_sigaction = handler;
> > if (sigaction(SIGSEGV, &sa_new, &sa_old) == -1)
> > handle_error("sigaction");
> >
> >
> > /* Allocate 4096 bytes with initial protection PROT_NONE */
> >
> > char* p = (char*)mmap(0,4096,PROT_NONE, MAP_ANONYMOUS |
> > MAP_PRIVATE, -1, 0);
> > if (p == MAP_FAILED)
> > handle_error("mmap");
> >
> > getcontext(&context_trigger);
> >
> > /* Access the protected memory, which will raise a SIGSEGV or SIGBUS
> > */
> > *(p) = 'a';
> >
> > printf("\nSuccess\n");
> > exit(EXIT_SUCCESS);
> > }
> >
> >
> > Any help/pointer would be greatly appreciated.
>
> Your're doing this all wrong.
>
> An SA_SIGINFO signal handler gets a pointer to the faulting context
> in its third parameter. To modify it you assign fields in it, e.g.
> the program counter. Then you return from the signal handler. Upon
> return the kernel reloads this possibly modified context.
>
> If you don't modify it the faulting instruction will usually be
> rexecuted (though x87 fp exceptions are delayed so the original
> faulting instruction will be skipped). In your simple example all
> you need to do is to mprotect the page and return without changing
> the context.
>
> Last time I checked getcontext/swapcontext weren't system calls,
> so what they do is up to whetever libc your code is linked to.
>
> For examples of FP signal handlers look in the runtime system of
> the Erlang/OTP system, and I'm sure lots of garbage collectors and
> distributed shared memory systems out there use sigsegv handlers.
>
> Finally, a distro specific problem should be taken up with that
> distro.
>
> /Mikael
> ( mikpe at acumem dot com )
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-09-05 20:45 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-03 17:26 sigaction with SA_SIGINFO does not pass the correct return context Alin Jula
2008-09-04 7:33 ` Mikael Pettersson
2008-09-05 20:49 ` Alin Jula
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome