Messages in this thread | | | Subject | sigaction with SA_SIGINFO does not pass the correct return context | From | Alin Jula <> | Date | Wed, 03 Sep 2008 10:26:51 -0700 |
| |
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
-- 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/
| |