2011년 2월 25일 금요일

stack overflow 상태에서 sigaction처리 하기.

sigaction()
signal handler를 작성해서 특정 시그널에 따라 어떤 조치를 취하도록 할 수 있다.

시그널이  발생했을 때 에러 원인을 파악하기 위해

 backtrace()
backtrace_symbol()
함수를 이용해서 호출 스택까지도 출력할 수 있다.

문제는 stack overflow가 발생해서 SIGSEGV 가 된경우 이를 처리할 함수 조차 호출 할 수 없다.

이를 해결 하기 위해 sigaltstack()함수로 미리 스택영역을 잡아두는 방법이 있다.



$cat stack_overflow.c
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define STACK_SIZE ( 4 * 1024 )
static int is_altstack_defined =0 ;
static char tmp_stack[STACK_SIZE];
void sig_segv(int signo);

int foo()
{
    return foo();
}
void sig_segv(int signo)
{
    printf("stack overflow\n");
    exit(-1);
}

static void register_sigaltstack()
{
    stack_t newSS, oldSS;

    if(is_altstack_defined)
    {
        return;
    }

    newSS.ss_sp = tmp_stack;

    newSS.ss_size = STACK_SIZE;
    newSS.ss_flags = 0;

    if(sigaltstack(&newSS, &oldSS) < 0)
    {
        printf ("error altstack");
    }

    is_altstack_defined = 1;
}


int main(int argc, char * argv[])
{
    int i = 0;
    struct sigaction sigsegv;
 
    register_sigaltstack();

    sigsegv.sa_handler = sig_segv;
    sigemptyset(&sigsegv.sa_mask);
    sigsegv.sa_flags = SA_ONSTACK;

    if( sigaction(SIGSEGV, &sigsegv, 0 ) == -1)
    {
        printf("signal SIGSEGV error");
        return -1;
    }

    foo();
}





위코드를 스레드로 변경 하면 동작 하지 않는다.
스레드에서 foo()  무한 재귀 함수를 호출 해서 스택 오버플로우가 발생했을때는 동작 하지 않는다.

여러 삽질 끝에 답을 알아 냈는데.

스레드 마다. sigaltstack() 을 해줘야 동작 한다.

여기에 관해서 찾은 글귀..
Operating in a multithreaded environment
If a process calls sigaction and then spawns pthreads within it, then those pthreads will inherit the signal handlers that were already installed. Apparently, this is not the case forsigaltstack: If a signal handler is installed with sigaction using a sigaltstack, and a thread spawned from that process is killed with the right signal, then the installed stack will not be found! The signal handler must instead be installed on each pthread individually. I’m not sure whether this is a bug in Linux or just a quirk of POSIX; in any case, I couldn’t find it documented anywhere.

댓글 없음: