Reputation: 3221
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)
Upvotes: 5
Views: 2052
Reputation: 126418
Your main concern seems to be with leak checking in the malloc case -- if you have an exhaustive leak checker (which requires everything allocated with malloc be freed before you exit). If this is the case, you can clean things up by remembering the old sigstack to restore it
stack_t sigstk, orig_sigstk;
setup_alt_sigstack() {
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, &orig_sigstack) < 0) {
sigstk.ss_size = 0;
free (sigstk.ss_sp);
printf ("sigaltstack errno=%d\n", errno);
}
} else {
printf ("malloc (SIGSTKSZ) failed!\n");
}
restore_orig_sigstack() {
if (sigaltstack (&orig_sigstack, 0) < 0) {
printf ("sigaltstack errno=%d\n", errno);
}
sigstk.ss_size = 0;
free (sigstk.ss_sp);
}
Upvotes: 0
Reputation: 20400
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
.
Upvotes: 6
Reputation: 13256
I'm not too familiar with sigaltstack()
, but I have been reviewing the man page. The difference in your 3 approaches seems to be in how to allocate space for ss_sp
structure member: use mmap()
, use traditional malloc()
, or allocate it off the stack.
If I'm understanding the system correctly, then you absolutely don't want to do approach #3: allocate from stack. After exiting the function, the stack space will be immediately reclaimed and repurposed (i.e., altered), and will break the functionality of sigaltstack()
.
So I would recommend traditional malloc()
. You could go with mmap()
if you like the syntax. Per my understanding, passing a NULL
address to mmap()
is equivalent to malloc()
anyway.
Upvotes: 1