はじめに
どうにもMacのSIGBUSやSIGILLを出すポリシーがよくわからないので、特徴的なコードをまとめておく。環境依存の可能性大。あとで何か見つけたら追加するかも。
環境はこんな感じ。
- macOS High Sierra 10.13.3
- g++ (Homebrew GCC 7.2.0) 7.2.0
結果
文字列領域への書き込み
文字列領域に何か書き込もうとするとSIGBUSが出ます。
int main(void){
const char *str = "test";
char *s = (char*)str;
s[0] = 'a';
}
不正なジャンプ命令 その1
不正なジャンプ命令を発行するとSIGBUSが出ます。
int main(void){
__asm__("jmp -6");
}
不正なジャンプ命令 その2
不正なジャンプ命令で、飛ぶアドレスを少し変えるとSIGILLが出ます。
int main(void){
__asm__("jmp -8");
}
不正なmov命令 その1
不正なアドレスを指定した間接ストアをすると、普通はSIGSEGVが出ます。
int main(void) {
__asm__("movq $10000, %rax");
__asm__("movl $0, 0(%rbp,%rax,1)");
}
不正なmov命令 その2
しかし、レジスタの値を大きくすると、movでSIGILLが出ます。
int main(void) {
__asm__("movq $10000000000, %rax"); // raxの値を大きくした
__asm__("movl $0, 0(%rbp,%rax,1)");
}
不正なmov命令 その3
デフォルトでrax
の指すアドレスに何か書き込もうとするとSIGBUSが出ます。
int
main(void){
__asm__("movq $0x100000f94, %rbx");
__asm__("movq $0, (%rax)");
}
$ g++ test_mov_rax.cpp -o test_mov_rax.out
$ ./test_mov_rax.out
zsh: bus error ./test_mov_rax.out
gdbを使うと、rax
の値は0x100000f94
であり、これがプログラムの先頭アドレスと一致していることがわかります。
$ objdump -S test_mov_rax.out
test_mov_rax.out: file format Mach-O 64-bit x86-64
Disassembly of section __TEXT,__text:
__text:
100000f94: 55 pushq %rbp
100000f95: 48 89 e5 movq %rsp, %rbp
100000f98: 48 bb 98 0f 00 00 01 00 00 00 movabsq $4294971288, %rbx
100000fa2: 48 c7 00 00 00 00 00 movq $0, (%rax)
100000fa9: b8 00 00 00 00 movl $0, %eax
100000fae: 5d popq %rbp
100000faf: c3 retq
_main:
100000f94: 55 pushq %rbp
100000f95: 48 89 e5 movq %rsp, %rbp
100000f98: 48 bb 98 0f 00 00 01 00 00 00 movabsq $4294971288, %rbx
100000fa2: 48 c7 00 00 00 00 00 movq $0, (%rax)
100000fa9: b8 00 00 00 00 movl $0, %eax
100000fae: 5d popq %rbp
100000faf: c3 retq
そして、その値と同じ値をrbx
に入れてあります。
不正なmov命令 その4
先程、rax
の値は0x100000f94
であり、そこに書き込もうとするとSIGBUSが出ました。rbx
にはrax
と同じ値が入っているので、(%rbx)
にアクセスしてもSIGBUSが出ることが予想されます。
試してみましょう。
int
main(void){
__asm__("movq $0x100000f94, %rbx");
__asm__("movq $0, (%rbx)");
}
さっきのコードの(%rax)
を(%rbx)
に変えただけです。
実行してみましょう。
$ g++ test_mov_rbx.cpp -o test_mov_rbx.out
$ ./test_mov_rbx.out
zsh: segmentation fault ./test_mov_rbx.out
SIGSEGVが出ました。全く同じ場所を触っているのに謎です。
gdbで追いかけてみましょう。
$ gdb ./test_mov_rbx.out
(gdb) b main
Breakpoint 1 at 0x100000f98
(gdb) r
Thread 2 hit Breakpoint 1, 0x0000000100000f98 in main ()
(gdb) stepi # rbxに0x100000f94 を代入
0x0000000100000fa2 in main ()
(gdb) i r rax rbx
rax 0x100000f94 4294971284
rbx 0x100000f94 4294971284 # この時点でrax=rbx
(gdb) stepi
Thread 2 received signal SIGBUS, Bus error. # ←SIGBUSが出てる
0x0000000100000fa2 in main ()
gdbで追いかけると、予想どおりSIGBUSが出ていますが、なぜかプログラム単体で実行するとSIGSEGVが出ます。シグナルが上書きされているのでしょうか・・・?
不正なmov命令 その5
rax
と同じ内容をrbx
に代入した上で(%rbx)
に何か書き込むとSIGSEGVが出ますが、movでrax
の内容をrbx
にコピーしてから(%rbx)
に何か書き込むとSIGBUSが出ます。
int
main(void){
__asm__("movq %rax, %rbx");
__asm__("movq $0, (%rbx)");
}
$ g++ test_mov_raxcopy.cpp -o test_mov_raxcopy.out
$ ./test_mov_raxcopy.out
zsh: bus error ./test_mov_raxcopy.out
まとめ
MacのSIGNALのポリシーは謎です。ちなみにLinuxではtest_mov_sigill.cpp
がSIGBUSで、それ以外は全てSIGSEGVでした。