2016-09-02 5 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... 
} 

アイデアは(オフセット無効)0にRSPを設定し、何かのためにそれを使用することです。しかし、この2番目のSIGSEGVはハンドラによって捕捉されず、代わりにプロセスを終了します。

どうやら、シグナルハンドラを起動するには、まずは正式なスタックポインタが必要ですが、どうしてですか?これは信号を扱うという考え方に反していないのですか?これを回避する可能性はありますか?

私はLinuxバージョン3.19.0-25-ジェネリックを実行しています。

+1

'sigaltstack()' – EOF

+0

@ EOF - ありがとう。有望に見える。成功すると解決策を投稿します。 – HairyNopper

+0

valgrind内でプログラムを実行できますか? – bruceg

答えて

1

オーケーは、ここ(ヒープ上のシグナルスタックを提供するために、sigaltstack()を使用して)E​​OFさんのコメント以下、上記の問題を解決するには、次のとおりです。

#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."); 
} 

代替シグナル・スタック、ヒープに割り当てられ、sigaltstack(&ss,NULL)を使用して登録されています。また、SA_ONSTACKフラグは、sigaction構造体に設定され、この特定のアクションで代替スタックを使用できるようにします。

これは基本的に私の問題を解決します。今ではSIGSEGVという無限のストリームが捕捉されているのが分かります。結局のところ、上記のcatch_function()は、無効なスタックポインタを修正することはあまりありません。解決策として、sbasemain()の有効なスタックポインタを格納し、無効な場合(保存されたスレッドコンテキストを操作して)、ハンドラでそれを復元するために使用します。

この作業をすべて行うために、インラインアセンブリを修正して値をプッシュするだけでなく、後でポップしてスタックの高さを変更しないように修正しました。再現性のために、私は今回のインクルードも含めました。

関連する問題