我们怀疑我们在多线程程序中遇到了堆栈溢出。然而,由于它是一个嵌入式应用程序,我们一直无法让 valgrind 等为它工作。此外,我们受限于使用不支持 flag 的 GCC 版本 v4.0.0 和 GLIBC v2.3.2 -fstack-protector-all
。
在这种情况下,我们如何检测我们看到的分段错误是否是堆栈溢出的结果?我们将所有线程的堆栈大小增加了一倍,这解决了问题,但我们希望确定这是一个真正的修复。
你有核心文件吗?您应该能够检查堆栈跟踪(通过在 GDB 或核心文件中运行代码)并查看崩溃时是否存在非常深的调用堆栈
你可以自己解决这个问题。如果您将程序设置为使用您分配的堆栈,则可以添加一个“保护页”来捕获对给定堆栈末尾后第一页的读取和写入。然后,您可以安装一个信号处理程序来捕获信号并告诉您段错误是否是由该保护页面中的访问引起的。
这是我能做的最小的例子,它展示了如何做到这一点:
#include <stdio.h>
#include <ucontext.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <malloc.h>
#include <signal.h>
static char *guard = NULL;
static const int pagesize = getpagesize();
static void handler(int sig, siginfo_t *info, void *ctx) {
if ((char*)info->si_addr >= guard && (char*)info->si_addr - guard <= pagesize) {
write(2, "stack overflow\n", 15);
}
write(2, "sigsegv caught\n", 15);
_exit(-1);
}
static void install_handler() {
// register sigsegv handler:
static struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO|SA_ONSTACK;
// give the signal handler an alternative stack
static char stack[4096];
stack_t ss;
ss.ss_size = sizeof(stack);
ss.ss_sp = stack;
if (sigaltstack(&ss, 0)) {
perror("sigaltstack");
fprintf(stderr,"failed to set sigstack\n");
exit(-1);
}
if (sigaction(SIGSEGV, &act, NULL)) {
perror("sigaction");
fprintf(stderr,"failed to set handler\n");
exit(-1);
}
}
static int overflow() {
return overflow() + 1;
}
static void test()
{
install_handler();
puts("start test");
// real code that might overflow
// test non-overflow segv
//*(char*)0 = 0;
// test overflow
overflow();
puts("finish test");
}
int main()
{
// create a stack and guard page:
const int pagesize = getpagesize();
char *st1=(char*)memalign(pagesize,1+(pagesize*4));
guard = st1+(pagesize*4);
if (mprotect(guard, pagesize, PROT_NONE)) {
perror("mprotect");
fprintf(stderr,"failed to protect guard page: %p \n", guard);
return -1;
}
ucontext_t ctx[2];
getcontext(&ctx[1]);
ctx[1].uc_stack.ss_sp = st1;
ctx[1].uc_stack.ss_size = 4*pagesize;
ctx[1].uc_link = &ctx[0];
makecontext(&ctx[1], test, 0);
swapcontext(&ctx[0], &ctx[1]);
return 0;
}
除了使用您自己的堆栈来运行您的代码之外,您还必须为要传递的信号提供另一个堆栈,否则信号传递本身将由于保护页而失败。