我已经看到至少有三种不同的方法来为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)
答案 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);
}