Messages in this thread | | | Subject | Re: sigaction with SA_SIGINFO does not pass the correct return context | From | Alin Jula <> | Date | Fri, 05 Sep 2008 13:49:06 -0700 |
| |
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 )
-- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |