2012-07-27 5 views
2

멀티 스레드 프로그램에서 스택 오버플로가 발생했다고 판단됩니다. 그러나 임베디드 응용 프로그램이므로 valgrind 등을 사용할 수 없습니다. 또한 우리는 플래그 -fstack-protector-all을 지원하지 않는 GCC 버전 v4.0.0 및 GLIBC v2.3.2를 사용하도록 제한됩니다.이전 GLIBC 버전으로 스택 오버 플로우 감지

우리가보고있는 분할 오류가이 인스턴스에서 스택 오버플로의 결과인지 여부를 감지하는 방법은 무엇입니까? 우리는 모든 스레드의 스택 크기를 두 배로 늘 렸으며 문제가 해결되었지만 이것이 진정한 수정인지 확인하고자합니다.

+1

코드의 해당 부분을 디버깅하여 과도한 스택 사용을 찾으셨습니까? 스택 크기를 늘리면 완벽하게 좋은 해결책이 될 수 있지만 문제를 일으키는 미묘한 버그가있을 수 있습니다. – sean

+2

왜 GCC v4.0.0에 제약이 있습니까? 최신 버전은 이전 버전에 의해 빌드 된 객체 및 라이브러리와 하위 호환이 가능하지만, 어떤 이유로 든 이전 버전을 사용해야하는 경우에도 새로운 버전을 설치하고이 문제를 찾기 위해 사용할 수 있습니다. 과거의 컴파일러로 과거에 살았습니다. GCC 코드의 주요 재 작성에 이어 4.0.0이 처음 출시되었습니다. 적어도 4.0.4는 사용할 수 있습니다. –

+1

@JonathanWakely : 가능하면 컴파일러는 임베디드 칩을 제공 한 공급 업체가 제공합니다. – jxh

답변

1

약간의 보살핌. 할당 한 스택을 사용하도록 프로그램을 설정하면 주어진 스택의 끝을지나 첫 페이지에 대한 읽기 및 쓰기를 "가드 페이지"에 추가 할 수 있습니다. 그런 다음 신호 처리기를 설치하여 신호를 잡아 당김 오류가 해당 가드 페이지 내에서 액세스로 인해 발생했는지 여부를 알려줄 수 있습니다.

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

을뿐만 아니라 당신이 신호가 될 또 다른 스택을 제공 할 필요가에서 코드를 실행하는 데 자신의 스택을 사용 :

이 내가 그렇게 만들 수있는 가장 작은 예입니다이 작업을 수행하는 방법을 보여줍니다 를 사용하여 전달하면 그렇지 않으면 가드 페이지로 인해 신호 전달 자체가 실패합니다.

+0

감사합니다. 훌륭한 아이디어가 많았습니다. – Dunnie

1

코어 파일을 구합니까? GDB에서 코드를 실행하거나 코어 파일에서 스택 추적을 검사하고 충돌이 발생할 때 매우 깊은 호출 스택이 있는지 확인할 수 있어야합니다.

관련 문제