2016-12-04 7 views
0

でセグメンテーションフォールトを起こししようとすると、私はmmapでカーネルからいくつかのメモリを割り当てるしようとしているとは、それは私が欲しいの後に任意のメモリアクセスは、セグメンテーションフォールトを引き起こすように保護ビットを設定しましたセグメンテーションフォールトが再び発生しないように保護ビットを設定しようとする。ただ、物事の仕組みを理解することはmmap

sigactionのためのLinuxのmanページがsiginfo構造体のsi_addr機能に障害が発生したアドレスが含まれていることを述べているにもかかわらず、MPROTECTへの呼び出しは失敗し、si_addrのアドレスが間違っています。アドレスはmain()関数で割り当てられたアドレスではありません。 コードMAC

#define _XOPEN_SOURCE 

#include <iostream> 
#include <signal.h> 
#include <ucontext.h> 
#include <sys/mman.h> 
#include <string.h> 
#include <cstdlib> 

using std::cout; 
using std::cerr; 
using std::endl; 

void handle_signal(int signal_number, siginfo_t* signal_info, void* context); 
void register_signal_handler(); 

int counter = 0; 

int main() { 
    register_signal_handler(); 
    int* page_mapped = (int*) mmap(nullptr, 100, PROT_NONE, 
      MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 
    if (page_mapped == MAP_FAILED) { 
     cerr << "mmap failed" << endl; 
    } 
    cout << "page mapped is " << reinterpret_cast<uintptr_t>(page_mapped) 
     << endl; 

    // cause the segmentation fault 
    cout << *page_mapped << endl; 

    return 0; 
} 

void handle_signal(int, siginfo_t* siginfo, void*) { 
    cout << "Handled a segmentation fault" << endl; 
    cout << "The segmentation fault was caused by the address " 
     << reinterpret_cast<uintptr_t>(siginfo->si_addr) << endl; 
    if (mprotect(siginfo->si_addr, 100, PROT_READ | PROT_WRITE) == -1) { 
     cerr << "mprotect failed" << endl; 
     exit(1); 
    } 

    // stop an infinite loop 
    ++counter; 
    if (counter == 3) { 
     cerr << "Counter got to 3, probably going into an infinite loop.. " 
      "stopping" << endl; 
     exit(1); 
    } 
} 

void register_signal_handler() { 
    struct sigaction sigaction_information; 
    memset(&sigaction_information, 0, sizeof(struct sigaction)); 
    sigaction_information.sa_sigaction = &handle_signal; 
    sigaction(SIGSEGV, &sigaction_information, nullptr); 
} 

答えて

0

に正常に動作しますがthis answerを参照してください。これは、SIGSEGVシグナルハンドラが変化マシン状態が、そうでなければ同じマシン命令が再起動される必要があることを説明し、カーネルが同じ信号は、(同一の「コンテキスト」に)送信されるが、それゆえに変換することを、いくつかの例外を与えますループ。

シグナルハンドラ内でC++ I/O(または<stdio.h>)を使用すると間違っています(非同期シグナルセーフ機能を使用しているため)。慎重にsignal(7)を読んでください。シグナルハンドラは、多くの関数(非同期シグナルセーフではない関数)を呼び出すことは禁じられています。

そしてmprotect(2)への通話は間違って(と失敗)です。サイズはページサイズの倍数(通常は4K)にする必要があります。また、アドレスは倍数にする必要があります(mprotectのアドレス引数としてではなくpage_mappedを使用する必要があります)。siginfo->si_addr前回の4Kページの倍数)。私はあなたのプログラム(g++ -O -Wall curious.cc -o curiousでGCC 6 & Linuxカーネル4.8を使ってDebian/x86-64でコンパイルした)をmprotect failedEINVALのエラー、perror(3)で与えられます)という名前で実行します。

strace(1)を使用して、起こっていることをより正確に理解できます。最後に

、あなたのcountervolatileとして宣言する必要があります。両方counterpage_mappedのような揮発性グローバル変数を宣言することで

volatile int counter; 
int*volatile page_mapped; 

をしてhandle_signalの内側に次のコードを持つことで(私のシステムでは、ページサイズは4Kです):

if (mprotect(page_mapped, 4096, PROT_READ | PROT_WRITE) == -1) { 
    /// this is still wrong in theory, 
    /// .... since we are using non-async signal safe functions 
    perror("mprotect"); 
    exit(EXIT_FAILURE); 
    /// but in practice mprotect is successful 
} 

mprotectが失敗しておらず、最終値がcounter(最後のmain)は1です(あなたが望むように)。

+0

* SIGSEGVシグナルハンドラは、シグナルハンドラがセグメンテーション違反を引き起こしたページからの保護を削除し(したがって、同じマシン命令を再起動しても同じ信号をリレイズべきではない)場合は、なぜこれが必要である*マシンの状態を変更する必要がありますか? – Leon

+0

保護を変更すると、マシンの状態が変更されます(例:MMUで)。 –

+0

したがって、セグメンテーションフォルトの原因となったアドレスを適切に保護するためにmprotectを取得するにはどうすればよいですか? – Curious

関連する問題