如何正确设置sigaltstack?

时间:2013-02-07 15:54:37

标签: c signals

我已经看到至少有三种不同的方法来为sigaltstack设置替代堆栈。我想知道哪一个是最好的方法:

方法#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");
}

方法#2 (我们已经使用了一段时间,但这里分配的内存出现在泄漏检测中('泄漏'命令))

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");
}

方法#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);
}

谢谢,Ákos (Mac OS X 10.8.2)

3 个答案:

答案 0 :(得分:3)

方法#1是最好的。之所以是因为地理位置。假设你使用#2,你的代码流如下:

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

如果您在信号处理程序中耗尽堆栈空间会发生什么?您的堆栈将向下增长并干扰blah指向的内存。如果你在某处有一些浅递归,很容易发生。 #3有同样的问题。

相反,使用mmap是因为它从远离数据堆的不同池分配,并且最好设置防护页面:

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();
...

现在,如果发生堆栈溢出,您将获得SIGSEGV,这比随机内存覆盖更容易调试大约十亿倍。 :)

#2被视为泄漏的原因可能是误报。您正在使用的泄漏工具可能会使用自己的变体覆盖malloc库函数,这是优先使用mmap来完成此任务而非malloc的另一个原因。

答案 1 :(得分:1)

我对sigaltstack()不太熟悉,但我一直在审核the man page。您的3种方法的不同之处似乎在于如何为ss_sp结构成员分配空间:使用mmap(),使用传统的malloc(),或者将其分配给堆栈。

如果我正确理解系统,那么你绝对想要做方法#3:从堆栈分配。退出函数后,堆栈空间将立即回收并重新调整用途(即更改),并将破坏sigaltstack()的功能。

所以我推荐传统的malloc()。如果您喜欢语法,可以使用mmap()。根据我的理解,将NULL地址传递给mmap()相当于malloc()

答案 2 :(得分:0)

您主要关心的似乎是 malloc 情况下的泄漏检查——如果您有一个详尽的泄漏检查程序(这要求在您退出之前释放分配给 malloc 的所有内容)。如果是这种情况,您可以通过记住旧的 sigstack 来恢复它来清理它

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);
}