Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up sigaltstack correctly?

Tags:

c

macos

signals

I have seen at least three different approaches to set up the alternative stack for sigaltstack(). I'm wondering which one is the best approach:

Approach #1

stack_t sigstk;
sigstk.ss_size = 0;
sigstk.ss_flags = 0;
sigstk.ss_sp = mmap (NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (sigstk.ss_sp != MAP_FAILED) {
    sigstk.ss_size = SIGSTKSZ;
    if (sigaltstack (&sigstk, 0) < 0) {
        sigstk.ss_size = 0;
        printf ("sigaltstack errno=%d\n", errno);
    }
} else {
    printf ("malloc (SIGSTKSZ) failed!\n");
}

Approach #2 (We've been using this for a while, but the memory allocated here shows up in leak detection (leaks command))

stack_t sigstk;
sigstk.ss_size = 0;
sigstk.ss_flags = 0;
sigstk.ss_sp = malloc (SIGSTKSZ);
if (sigstk.ss_sp != NULL) {
    sigstk.ss_size = SIGSTKSZ;
    if (sigaltstack (&sigstk, 0) < 0) {
        sigstk.ss_size = 0;
        free (sigstk.ss_sp);
        printf ("sigaltstack errno=%d\n", errno);
    }
} else {
    printf ("malloc (SIGSTKSZ) failed!\n");
}

Approach #3

stack_t sigstk;
static char ssp[SIGSTKSZ];
sigstk.ss_size = SIGSTKSZ;
sigstk.ss_flags = 0;
sigstk.ss_sp = ssp;
sigstk.ss_size = SIGSTKSZ;
if (sigaltstack (&sigstk, 0) < 0) {
    sigstk.ss_size = 0;
    free (sigstk.ss_sp);
    printf ("sigaltstack errno=%d\n", errno);
}

Thanks, Ákos (Mac OS X 10.8.2)

like image 286
Ákos Avatar asked Feb 07 '13 15:02

Ákos


1 Answers

Approach #1 is the best. The reason why is because of location. Suppose you use #2 and your code flow goes like this:

void *blah = malloc (...)
...
stack_t sigstk;
sigstk.ss_size = 0;
sigstk.ss_flags = 0;
sigstk.ss_sp = malloc (SIGSTKSZ);

Now what happens if you exhaust your stack space in your signal handler? Your stack will grow downwards and interfere with the memory pointed to by blah. It can easily happen if you have some shallow recursion somewhere. #3 has the same kind of problem.

Instead, use mmap because it allocates from a different pool, far away from the data heap, and it is a good idea to setup guard pages:

char* mem = mmap (NULL, 
                  SIGSTKSZ + 2*getpagesize(), 
                  PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 
                  -1, 0);
mprotect(mem, getpagesize(), PROT_NONE);
mprotect(mem + getpagesize() + SIGSTKSZ, getpagesize(), PROT_NONE);
sigstk.ss_sp = mem + getpagesize();
...

Now you'll get a SIGSEGV if a stack overflow occurs which is about a billion times easier to debug than random memory overwrites. :)

The reason #2 is counted as a leak is likely a false positive. The leaks tool you are using likely overrides the malloc library function with its own variant, which is another reason to prefer to use mmap for this task rather than malloc.

like image 80
Björn Lindqvist Avatar answered Sep 23 '22 01:09

Björn Lindqvist