2016-09-02 7 views
1

나는 버그가있는 코드로 작업 중이며 충돌에 대한 자세한 정보를 얻기 위해 SIGSEGV 핸들러를 설치하려고합니다. 그러나 내 처리기가 호출되지 않은 것으로 나타났습니다.손상된 스택에 의해 트리거되었을 때 SIGSEGV 잡기

나는 이유를 찾고 있었고 손상된 스택 포인터 값과 관련이있는 것으로 보인다. 여기에 내가 확인하기 위해 쓴 몇 가지 개념 증명 코드입니다 :

static void catch_function(int sig, siginfo_t *info, void *cntxt) 
{ 
    puts("handler works"); 
} 

void main(int argc, char **argv) 
{  
    struct sigaction sa; 

    sa.sa_sigaction = (void *)catch_function; 
    sigemptyset (&sa.sa_mask); 
    sa.sa_flags = SA_SIGINFO | SA_NODEFER ; 

    sigaction(SIGSEGV, &sa, NULL); 

    puts("testing handler"); 
    raise(SIGSEGV); 
    puts("back"); 

    __asm__ ( "xor %rax, %rax\n\t" 
       "mov %rax, %rsp\n\t" 
       "push 0" 
      ); 

    // never reached... 
} 

아이디어는 RSP를 0으로 설정하는 것입니다 (오프셋 무효) 다음 일을 위해 사용합니다. 그러나이 두 번째 SIGSEGV는 처리기에 의해 포착되지 않고 대신 프로세스를 종료합니다.

분명히 신호 처리기를 호출하려면 정상적인 스택 포인터가 있어야합니다.하지만 그 이유는 무엇입니까? 이것은 신호를 다루는 것에 반대하지 않습니까? 이 문제를 해결할 가능성은?

저는 Linux 버전 3.19.0-25-generic을 사용하고 있습니다. 대안 신호 스택 힙에 할당되고, sigaltstack(&ss,NULL)을 사용하여 등록

#include <stdio.h> 

#define __USE_GNU 
#include <signal.h> 
#include <stdlib.h> 
#include <ucontext.h> 

static long long int sbase; 

static void catch_function(int sig, siginfo_t *info, void *cntxt) 
{ 
    puts("handler works"); 

    /* reset RSP if invalid */ 
    ucontext_t *uc_context = (ucontext_t *)cntxt; 
    if(!uc_context->uc_mcontext.gregs[REG_RSP]) 
    { 
     puts("resetting RSP"); 
     uc_context->uc_mcontext.gregs[REG_RSP] = sbase; 
    } 
} 

void main(int argc, char **argv) 
{  
    /* RSP during main */ 
    sbase = (long long int)&argv; 

    stack_t ss; 
    struct sigaction sa; 

    ss.ss_sp = malloc(SIGSTKSZ); 
    ss.ss_size = SIGSTKSZ; 
    ss.ss_flags = 0; 
    sigaltstack(&ss, NULL); 

    sa.sa_sigaction = (void *)catch_function; 
    sigemptyset (&sa.sa_mask); 
    sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; 

    sigaction(SIGSEGV, &sa, NULL); 

    puts("testing handler"); 
    raise(SIGSEGV); 
    puts("back"); 

    __asm__ (
      "xor %rax, %rax\n\t" 
      "mov %rax, %rsp\n\t" 
      "push %rax\n\t" 
      "pop %rax" ); 

    puts("exiting."); 
} 

:

+1

'sigaltstack()'. – EOF

+0

@ EOF - 감사합니다. 그것은 유망 해 보인다. 성공하면 솔루션을 게시합니다. – HairyNopper

+0

valgrind에서 프로그램을 실행할 수 있습니까? – bruceg

답변

1

좋아 여기 (힙 신호 스택을 제공 sigaltstack() 사용) EOF의 코멘트 다음, 상기 문제에 대한 해결책 . 또한 SA_ONSTACK 플래그가 sigaction 구조체에 설정되어이 특정 작업에 대체 스택을 사용할 수 있습니다.

기본적으로 내 문제가 해결됩니다. 이제 SIGSEGV의 끝없는 스트림이 잡혔습니다. 결국 위의 catch_function()은 잘못된 스택 포인터를 고치는 작업을 많이하지 않습니다. 해결책으로, sbasemain()에 대한 유효한 스택 포인터를 저장하고 유효하지 않은 경우 (저장된 스레드 컨텍스트 조작을 통해) 처리기에서이를 복원하는 데 사용합니다.

이 작업을 모두 수행하기 위해 인라인 어셈블리를 수정하여 값을 푸시하지 않고 이후에 다시 팝하기 만하면 스택 높이가 변경되지 않습니다. 복제 가능성을 위해 이번에 포함 된 내용도 포함했습니다.