SIGSEGVをキャッチするシグナルハンドラの書き方は?
質問
SIGSEGVをキャッチするシグナルハンドラを書きたいのですが、どうすればよいでしょうか。 メモリブロックの読み書きは
char *buffer;
char *p;
char a;
int pagesize = 4096;
mprotect(buffer,pagesize,PROT_NONE)
これは、バッファから始まるメモリのページサイズバイトを、あらゆる読み書きの脅威から保護します。
次に、メモリの読み出しを試みます。
p = buffer;
a = *p
これでSIGSEGVが発生し、私のハンドラが呼ばれることになります。 ここまではいいんです。問題は、ハンドラが呼び出された後、メモリのアクセスライトを変更するために
mprotect(buffer,pagesize,PROT_READ);
というように変更し、私のコードの正常な機能を継続させます。私はこの関数を終了させたくありません。 将来同じメモリに書き込むとき、私は再び信号をキャッチし、書き込み権限を変更し、そのイベントを記録したいと思います。
以下は のコードです。 :
#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
char *buffer;
int flag=0;
static void handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lx\n",(long) si->si_addr);
printf("Implements the handler only\n");
flag=1;
//exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
char *p; char a;
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
handle_error("sigaction");
pagesize=4096;
/* Allocate a buffer aligned on a page boundary;
initial protection is PROT_READ | PROT_WRITE */
buffer = memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Start of region: 0x%lx\n", (long) buffer);
printf("Start of region: 0x%lx\n", (long) buffer+pagesize);
printf("Start of region: 0x%lx\n", (long) buffer+2*pagesize);
printf("Start of region: 0x%lx\n", (long) buffer+3*pagesize);
//if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
if (mprotect(buffer + pagesize * 0, pagesize,PROT_NONE) == -1)
handle_error("mprotect");
//for (p = buffer ; ; )
if(flag==0)
{
p = buffer+pagesize/2;
printf("It comes here before reading memory\n");
a = *p; //trying to read the memory
printf("It comes here after reading memory\n");
}
else
{
if (mprotect(buffer + pagesize * 0, pagesize,PROT_READ) == -1)
handle_error("mprotect");
a = *p;
printf("Now i can read the memory\n");
}
/* for (p = buffer;p<=buffer+4*pagesize ;p++ )
{
//a = *(p);
*(p) = 'a';
printf("Writing at address %p\n",p);
}*/
printf("Loop completed\n"); /* Should never happen */
exit(EXIT_SUCCESS);
}
問題は、シグナルハンドラだけが実行され、シグナルをキャッチした後、メイン関数に戻ることができないことです。
どのように解決するのですか?
シグナルハンドラが戻ると (exit や longjmp など、実際に戻ることを妨げるものを呼んでいないと仮定して)、コードはシグナルが発生した地点で継続され、同じ命令を再実行することになります。 この時点では、メモリ保護は変更されていないため、再びシグナルがスローされ、無限ループでシグナル ハンドラーに戻ることになります。
したがって、これを動作させるには、シグナル ハンドラで mprotect を呼び出す必要があります。 残念ながら、Steven Schansker が指摘するように、mprotect は非同期安全ではないので、シグナルハンドラから安全に呼び出すことはできません。 そのため、POSIXに関する限り、あなたは失敗しています。
幸いなことに、ほとんどの実装 (私が知る限り、最近のすべての UNIX および Linux 変種) では、mprotect はシステムコールであるため シグナル ハンドラ内から呼び出しても安全です。 ということで、やりたいことはほとんどできます。 問題は、読み取り後に保護を元に戻したい場合、読み取り後のメイン プログラムでそれを行う必要があることです。
もうひとつの可能性は、シグナルハンドラへの第3引数で何かをすることです。これは、シグナルが発生した場所についての情報を含む、OS やアーカイブ固有の構造体を指すものです。 Linux では、これは ucontext 構造体であり、シグナルが発生した $PC アドレスと他のレジスタの内容に関するマシン固有の情報を含んでいます。 これを変更すると、シグナルハンドラの戻り先が変わるので、ハンドラが戻った後に再実行されないように、$PC をフォールト命令の直後に変更することができます。 これは正しく行うには非常に厄介です(そして非移植可能でもあります)。
編集
は
ucontext
構造体は
<ucontext.h>
. その中で
ucontext
フィールド
uc_mcontext
にはマシンコンテキストが含まれ
その
には、配列
gregs
には一般的なレジスタのコンテキストが含まれています。 ですから、シグナルハンドラで
ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
を実行すると、例外が発生したpcが得られます。 これを読めば、どの命令で障害が発生したのかがわかり それを読んで、どの命令で障害が発生したのかを把握し、違うことをすることができます。
シグナルハンドラで mprotect を呼び出すことの移植性に関しては、SVID 仕様または BSD4 仕様のいずれかに従っているシステムは安全であるべきです -- これらはシグナルハンドラで任意のシステムコール (マニュアルのセクション 2 にあるもの) を呼び出すことを許可しています。
関連
-
解決済み] g++ コンパイルエラー: ')'トークンの前に一次式があることが予想される
-
[解決済み] Xcode - 警告。C99 では関数の暗黙の宣言は無効です。
-
[解決済み] Linuxで特定のテキストを含むすべてのファイルを検索するにはどうすればよいですか?
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] ワイルドカードマッチングに基づいて、現在のフォルダとサブフォルダ内のすべてのファイルを再帰的に検索するにはどうすればよいですか?
-
[解決済み] Linux上で動作するC++コードのプロファイリングを行うにはどうすればよいですか?
-
[解決済み] エクスポートされた環境変数を削除する方法を教えてください。
-
[解決済み] すべてのディレクトリとサブディレクトリを再帰的にgrepするにはどうしたらいいですか?
-
[解決済み] C言語でオブジェクト指向のコードを書くとしたら、どのようにすればよいのでしょうか?[クローズド]
-
[解決済み] LD_PRELOADのトリックとは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
_CRT_SECURE_NO_WARNINGS エラーメッセージ、解決方法
-
構造体の配列--[エラー] '['トークンの前に一次式があることが予想される
-
赤線の位置は必ずしも間違っていない:式は変更可能なlvalueでなければならないエラーは、この文とは別の場所に存在する可能性があります。
-
C++の配列コピー
-
Solve Dev-c++ [エラー] 'for' ループの初期宣言は、C99 または C11 モードでのみ許可されます。
-
警告: 'struct XXX' はパラメータリストの内部で宣言されています。
-
[解決済み] CコードでEOFを表現する?
-
[解決済み] C 言語で const char* を char* に変換するには?
-
[解決済み] 難読化Cコードコンテスト2006。sykes2.cの解説をお願いします。
-
[解決済み] C 言語の配列へのポインタ/ポインタの配列の曖昧さ解消