SEGV起こすアドレスを事前チェック
未確保なアドレスやガードページに対して参照、実行しようすると
SEGVで落とされるが、アクセスする前にダメなアドレスかどうか
判定できないかと思い色々試してみた。今回は読み書きのみ対象。
OSはLinuxで2.6カーネル。
1. SEGVシグナル無視
POSIXでは無視した場合の動作は未定義とのこと。
SIG_IGNセットは普通にSEGV食らって落とされたのでダミーでハンドラセットしてみた。
int main(int argc,char ** argv){ struct sigaction sa; int *p; void *i,*j; memset(&sa,0,sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); sa.sa_handler = segv_handler; sigaction(SIGSEGV,&sa,NULL); j = (void*)0xaaaaaaaa; p = (int *)j; i = (void*)*p; printf("%p\n",i); return 0; } void segv_handler(int signum){ return; }
実行すると無限ループ。gdb経由でステップ実行してみると
シグナルハンドラからの戻りが、SEGV起こした箇所( i = (void*)*p; )な為ループする模様。
まあ、予想通りダメ。
2. mprotectでチェック
未確保アドレスならエラーが返ってくるのでは無いかと思い試してみる。
int main(int argc,char ** argv){ int ret; void *ptr,*aptr; void *buff_heap = malloc(77); void *buff_stack = calloc(77,1); ptr = (void *)0xdeadbeef; aptr = (void *)((unsigned long)ptr & (0xFFFFFFFFUL << 12)); if((ret = mprotect(aptr,256,PROT_READ | PROT_WRITE)) != 0){ perror("mprotect"); } else{ printf("PASS\n"); } ptr = (void *)buff_stack; aptr = (void *)((unsigned long)ptr & (0xFFFFFFFFUL << 12)); if((ret = mprotect(aptr,256,PROT_READ | PROT_WRITE)) != 0){ perror("mprotect"); } else{ printf("PASS\n"); } ptr = (void *)buff_heap; aptr = (void *)((unsigned long)ptr & (0xFFFFFFFFUL << 12)); if((ret = mprotect(aptr,256,PROT_READ | PROT_WRITE)) != 0){ perror("mprotect"); } else{ printf("PASS\n"); } return 0;
結果は以下。
mprotect: Cannot allocate memory PASS PASS
一応意図したことはできるが、ページ単位に合わせる必要があるので粒度が荒いのと
成功するとアクセス保護が変更されてしまう。(ptr = main等でも成功するが実行ビット落ちて即死)
3. /procを読む
/proc/
中身はこんなの。詳細はman proc等参照。
address perms offset dev inode pathname 0035e000-00377000 r-xp 00000000 08:01 1366850 /lib/ld-2.5.so 00377000-00378000 r-xp 00018000 08:01 1366850 /lib/ld-2.5.so 00378000-00379000 rwxp 00019000 08:01 1366850 /lib/ld-2.5.so 00380000-004b7000 r-xp 00000000 08:01 1368813 /lib/libc-2.5.so 004b7000-004b9000 r-xp 00137000 08:01 1368813 /lib/libc-2.5.so 004b9000-004ba000 rwxp 00139000 08:01 1368813 /lib/libc-2.5.so 08047000-080f2000 r-xp 00000000 08:01 2831364 /bin/bash 080f2000-080f7000 rw-p 000ab000 08:01 2831364 /bin/bash b7f3c000-b7f3d000 rw-p b7f3c000 00:00 0 bfc7e000-bfc94000 rw-p bfc7e000 00:00 0 [stack]
判定する毎に毎回openして読み込む必要があるが、これが一番確実かもしれず。
何かもう少しスマートな方法は無いのだろうか。
そもそもSEGVしてる時点でアレというのはあるがソースコード無いし。