Coder Perfect

How can I write a SIGSEGV signal handler?

Problem

I’d want to create a signal handler for SIGSEGV. I use read/write protection to safeguard a memory block.

char *buffer;
char *p;
char a;
int pagesize = 4096;

mprotect(buffer,pagesize,PROT_NONE)

This prevents any reads or writes to the pagesize bytes of memory starting at buffer.

Second, I make an attempt to read the memory:

p = buffer;
a = *p 

This will result in a SIGSEGV, which will trigger my handler. So far, everything has gone well. My issue is that once the handler has been run, I wish to alter the memory access write by doing

mprotect(buffer,pagesize,PROT_READ);

and my code will continue to work normally. I’m not interested in exiting the function. I want to catch the signal again on future writes to the same memory, alter the write rights, and then record the occurrence.

The code is as follows:

#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

char *buffer;
int flag=0;

static void handler(int sig, siginfo_t *si, void *unused)
{
    printf("Got SIGSEGV at address: 0x%lx\n",(long) si->si_addr);
    printf("Implements the handler only\n");
    flag=1;
    //exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    char *p; char a;
    int pagesize;
    struct sigaction sa;

    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = handler;
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
        handle_error("sigaction");

    pagesize=4096;

    /* Allocate a buffer aligned on a page boundary;
       initial protection is PROT_READ | PROT_WRITE */

    buffer = memalign(pagesize, 4 * pagesize);
    if (buffer == NULL)
        handle_error("memalign");

    printf("Start of region:        0x%lx\n", (long) buffer);
    printf("Start of region:        0x%lx\n", (long) buffer+pagesize);
    printf("Start of region:        0x%lx\n", (long) buffer+2*pagesize);
    printf("Start of region:        0x%lx\n", (long) buffer+3*pagesize);
    //if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
    if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
        handle_error("mprotect");

    //for (p = buffer ; ; )
    if(flag==0)
    {
        p = buffer+pagesize/2;
        printf("It comes here before reading memory\n");
        a = *p; //trying to read the memory
        printf("It comes here after reading memory\n");
    }
    else
    {
        if (mprotect(buffer + pagesize * 0, pagesize,PROT_READ) == -1)
        handle_error("mprotect");
        a = *p;
        printf("Now i can read the memory\n");

    }
/*  for (p = buffer;p<=buffer+4*pagesize ;p++ ) 
    {
        //a = *(p);
        *(p) = 'a';
        printf("Writing at address %p\n",p);

    }*/

    printf("Loop completed\n");     /* Should never happen */
    exit(EXIT_SUCCESS);
}

The issue is that after catching the signal, only the signal handler runs, and I am unable to return to the main function.

Asked by Adi

Solution #1

When your signal handler returns (assuming it doesn’t call exit or longjmp or something else that prohibits it from doing so), the code will continue where the signal occurred, reexecuting the same instruction. Because the memory protection has not been modified at this time, it will just throw the signal again, and you will be stuck in an unending loop in your signal handler.

To make it work, you must use the signal handler to call mprotect. Unfortunately, mprotect is not async-safe, so you can’t safely call it from the signal handler, as Steven Schansker points out. So you’re screwed as far as POSIX is concerned.

Fortunately, because mprotect is a system call on most implementations (all recent UNIX and Linux variants as far as I know), it is safe to call from within a signal handler, allowing you to do most of what you want. The issue is that if you want to alter the protections back after the read, you’ll need to do so in the main application.

Another option is to use the signal handler’s third argument, which points to an OS and arch-specific structure that holds information about the signal’s location. This is an ucontext structure on Linux, which holds machine-specific information about the $PC address and other register contents where the signal was received. If you modify this, you change where the signal handler will return to, so you can change the $PC to be just after the faulting instruction so it won’t re-execute after the handler returns. This is very tricky to get right (and non-portable too).

edit

The machine context is contained in uc mcontext, and the general register context is contained in the array gregs. As a result, in your signal handler, you should:

ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];

will show you the computer on which the error happened. You can read it to figure out which instruction was incorrect and then use an alternative approach.

As far as the portability of calling mprotect in the signal handler is concerned, any system that follows either the SVID spec or the BSD4 spec should be safe — they allow calling any system call (anything in section 2 of the manual) in a signal handler.

Answered by Chris Dodd

Solution #2

You’ve fallen into the same mistake that everyone does when dealing with signals for the first time. What is the snare? You’re under the impression that signal handlers can accomplish anything meaningful. Only asynchronous and reentrant-safe library calls are allowed from a signal handler.

For more information, see this CERT warning, which includes a list of safe POSIX functions.

It’s worth noting that printf(), which you’ve already used, isn’t on that list.

mprotect isn’t either. It’s not possible to use it from a signal handler. It might work at first, but I guarantee you’ll run into issues later. Signal handlers should be handled with extreme caution; they’re difficult to get correctly!

EDIT

Because I’m already being a portability jerk, I’ll add that you shouldn’t write to shared (i.e. global) variables without taking the necessary measures.

Answered by Steven Schlansker

Solution #3

On Linux, you can recover from SIGSEGV. Also you can recover from segmentation faults on Windows (you’ll see a structured exception instead of a signal). But the POSIX standard doesn’t guarantee recovery, so your code will be very non-portable.

Take a look at the libsigsegv library.

Answered by Ben Voigt

Solution #4

Returning from the signal handler will result in ambiguous behavior. Rather, use longjmp to get out of it.

Only if the signal is generated in an async-signal-safe function is this acceptable. Otherwise, if the program invokes another async-signal-unsafe function, the behavior is undefined. Hence, the signal handler should only be established immediately before it is necessary, and disestablished as soon as possible.

In fact, I’m only aware of a handful applications for a SIGSEGV handler:

Finally, any action that causes SIGSEGV is almost certainly UB, because it is accessing incorrect memory. If the signal was, say, SIGFPE, this would not be the case.

Answered by Demi

Solution #5

The use of ucontext t or struct ucontext (found in /usr/include/sys/ucontext.h) causes a compilation error.

http://www.mail-archive.com/arch-general@archlinux.org/msg13853.html

Answered by shreshtha

Post is based on https://stackoverflow.com/questions/2663456/how-to-write-a-signal-handler-to-catch-sigsegv