5

我已经看到至少三种不同的方法来设置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(我们已经使用了一段时间,但是这里分配的内存显示在泄漏检测(leaks命令)中)

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)

4

3 回答 3

4

方法#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.

于 2015-08-04T22:03:38.973 回答
1

我不太熟悉sigaltstack(),但我一直在查看手册页。您的 3 种方法的区别似乎在于如何为ss_sp结构成员分配空间:使用mmap()、使用传统malloc()或从堆栈中分配它。

如果我对系统的理解正确,那么你绝对不想做方法#3:从堆栈分配。退出函数后,堆栈空间将立即被回收和重新利用(即改变),并且会破坏sigaltstack().

所以我会推荐传统malloc()的。mmap()如果你喜欢语法,你可以去。根据我的理解,将NULL地址传递给无论如何mmap()都相当于。malloc()

于 2013-02-08T18:14:32.643 回答
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);
}
于 2021-03-01T21:37:42.533 回答