我們懷疑我們在多線程程序中遇到堆棧溢出。然而,由於它是一個嵌入式應用程序,我們一直無法讓Valgrind等爲它工作。此外,我們不得不使用GCC版本v4.0.0和GLIBC v2.3.2,它們不支持標記-fstack-protector-all
。用舊的GLIBC版本檢測堆棧溢出
我們該如何去檢測我們看到的分段錯誤是否是在這種情況下堆棧溢出的結果?我們已經將所有線程的堆棧大小加倍,這就解決了這個問題,但我們希望確保這是一個真正的解決方案。
我們懷疑我們在多線程程序中遇到堆棧溢出。然而,由於它是一個嵌入式應用程序,我們一直無法讓Valgrind等爲它工作。此外,我們不得不使用GCC版本v4.0.0和GLIBC v2.3.2,它們不支持標記-fstack-protector-all
。用舊的GLIBC版本檢測堆棧溢出
我們該如何去檢測我們看到的分段錯誤是否是在這種情況下堆棧溢出的結果?我們已經將所有線程的堆棧大小加倍,這就解決了這個問題,但我們希望確保這是一個真正的解決方案。
您可以通過一點護理。如果您將程序設置爲使用您分配的堆棧,則可以添加一個「保護頁」來讀取和寫入到給定堆棧末尾的第一頁。然後,您可以安裝一個信號處理程序來捕獲信號,並告訴您segfault是否由該防護頁內的訪問引起。
這是我可以做的是,最小的例子展示瞭如何做到這一點:
#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;
}
除了使用自己的堆棧代碼運行在您需要提供另一組的信號是交付使用,否則信號傳遞本身將失敗,因爲守衛頁面。
謝謝,在那裏有很多偉大的想法,非常有幫助。 – Dunnie 2012-08-01 11:19:12
你有corefile嗎?您應該能夠檢查堆棧跟蹤(通過在GDB中運行代碼或從核心文件運行)並查看崩潰時是否有非常深的調用堆棧
您是否調試過代碼的這些部分以查找過多的堆棧使用情況?增加堆棧大小可能是一個完美的解決方案,但也可能存在導致此問題的微妙錯誤。 – sean 2012-07-27 15:42:13
你爲什麼受限於GCC v4.0.0?新版本與先前版本構建的對象和庫向後兼容,但即使您由於某種原因被迫使用舊版本,仍然可以安裝較新版本並使用它來發現此問題,然後返回到用你的舊編譯器生活在過去。 4.0.0是GCC代碼重大改寫後的第一次發佈,當然你至少可以使用4.0.4? – 2012-07-27 16:13:14
@JonathanWakely:可能,編譯器是由提供嵌入式芯片的供應商提供的。 – jxh 2012-07-27 16:20:27