この記事は自作OS Advent Calendar 2022の18日目の記事です。前回のあらすじはこちら:
例外を吐いてるのはどこのどいつだぁ?
さて、前回の状況をおさらいすると、
- 0x200fd0の命令がアドレス0x2000000000にアクセスしようとしてページフォルト(0xe)を発生させた。
- ページフォルトを処理している最中に一般保護例外(0xd)が発生し、一般保護例外を処理中にもさらに一般保護例外が発生することで、順次スタックが食い潰されていった。
- 例外ハンドラのスタックが枯渇し、0x3de4bed8にアクセスしようとした段階でページフォルトが発生した。これにより、ダブルフォルトが発生した。
- ダブルフォルトハンドラ内で再度ページフォルトが発生した結果、トリプルフォルトとなってリセットがかかった。
というわけで、まずは最初のきっかけである 0x200fd0とかにある命令がどこのどいつかを特定することにしましょう。
まず、アプリのロード状況を確認します。OSのログを見ると、
Executable found: Elf { name: hello, data: @0x000000003e2d9000 } This ELF seems to be executable! phdr_start: 64, phdr_entry_size: 56, num_of_phdr_entry: 5 type : (rwx) = (101) offset: 0x0000000000000000 vaddr : 0x0000000000200000 fsize : 0x0000000000001B64 vsize : 0x0000000000001B64 align : 0x0000000000001000 type : (rwx) = (110) offset: 0x0000000000001B68 vaddr : 0x0000000000202B68 fsize : 0x00000000000001C8 vsize : 0x00000000000001C8 align : 0x0000000000001000 load_region_start = 0x0000000000200000 load_region_end = 0x0000000000203000 load_region @ 0x000000003d233000 dst_range: 0x0000000000000000 - 0x0000000000001B64 dst_range: 0x0000000000002B68 - 0x0000000000002D30 run the code! entry_point = 0x000000003d2332f0
というふうになっています。これはどういうことかというと…図示したほうがはやそうなので、お絵描きしました。
現在は手抜きをするために、アプリケーションとOSのメモリ空間を分けていないので、アプリの希望通りのメモリレイアウトではなく、相対位置を維持したまま、異なるアドレスにCodeとDataの両方をロードしているのでした。
さて、例外を発生させたメモリアドレス(0x200fd0)は、このロードされたコード(0x3d233000-0x3d234b64)の中…にありませんね…あれ?
…と思って状況をよくよく確認すると、どうもこのアドレスは、アプリがもともと予期していた、コード領域がロードされるべきアドレス範囲(0x200000-0x201b64)の中にあるようです…つまり、アプリケーションが正しく再配置可能ではなかった、もしくはローダーが仕事をサボっているせいで、正しく再配置できなかった、のいずれかの可能性であると考えられます。ちょっとアプリのバイナリのほうを確認してみましょう。
$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -A 3 200fd0 0000000000200fd0 <<core::fmt::Arguments as core::fmt::Display>::fmt>: 200fd0: 48 83 ec 38 sub $0x38,%rsp 200fd4: 48 8b 06 mov (%rsi),%rax 200fd7: 48 8b 76 08 mov 0x8(%rsi),%rsi 200fdb: 48 8b 4f 28 mov 0x28(%rdi),%rcx
objdumpに-Cオプションをつけると、シンボルをデマングル(人間が読みやすい形式に変換)してくれるみたいで、Rustのシンボル名にも対応してるみたいです。ありがとうbinutils!
というわけでですね、この200fd0は、明らかにDisplay::fmtを呼び出しているコードで利用されていて、我々のprintln!のコードパスで通っていそうな雰囲気がひしひしと伝わってきます。
さて、これを呼び出している側のコードはどうなっているのでしょうか?rip相対のcallではこのようなことは起こり得ないので、レジスタ間接でのcallがDisplay::fmtへ制御を移そうとしているはずです。なので、レジスタ間接callを使っている人々をリストアップしましょう。
$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -E -e 'call.*\*' -e ' <.*>:$' | grep 'call.*\*' -B 1 0000000000200860 <<core::panic::panic_info::PanicInfo as core::fmt::Display>::fmt>: 200886: 41 ff 56 18 call *0x18(%r14) 2008bf: ff 50 18 call *0x18(%rax) -- 0000000000201030 <core::fmt::write>: 2010b7: ff 50 18 call *0x18(%rax) 201177: ff 54 08 08 call *0x8(%rax,%rcx,1) 2011d8: ff 50 18 call *0x18(%rax) 2011e9: 42 ff 54 35 08 call *0x8(%rbp,%r14,1) 20122f: ff 51 18 call *0x18(%rcx) 0000000000201250 <core::fmt::Formatter::pad_integral>: 2013f1: ff 55 20 call *0x20(%rbp) 20150a: 41 ff 55 20 call *0x20(%r13) 201557: ff 55 18 call *0x18(%rbp) 201582: 41 ff 55 18 call *0x18(%r13) 2015aa: 41 ff 55 20 call *0x20(%r13) 00000000002015d0 <core::fmt::Formatter::pad_integral::write_prefix>: 2015f1: 41 ff 54 24 20 call *0x20(%r12) 0000000000201630 <core::fmt::Formatter::pad>: 2018ea: 41 ff 56 20 call *0x20(%r14) 20190d: 41 ff 56 18 call *0x18(%r14) 20193f: 41 ff 56 20 call *0x20(%r14)
まあ…たぶんcore::fmt::writeですよね…きっとそうだ…
`brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep '<core::fmt::write>:' -A 155 | grep -z -E -e 'call' --color='always'
$ cat qemu_debug.log | grep hikalium | head -n 3 hikalium: raise_interrupt2: is_int == 0 intno = 14 eip=0x000000000000200FD0 error_code = 0x000000000000000000 hikalium: rsp: 00000000003E286BE0 hikalium: ret_to: 00000000003D2341EE
$ cat com2.log | grep -a -e entry_point entry_point = 0x000000003d2332f0
$ make objdump_hello | grep '<entry>:' 00000000002002f0 <entry>:
$ ./scripts/debug/inspect_app_crash.sh 0000000000201030 <core::fmt::write>: 2011e9: 42 ff 54 35 08 call *0x8(%rbp,%r14,1) 2011ee: 84 c0 test %al,%al
.data.rel.ro
があやしい!
https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/specialsections.html
Chromium Docs - Native Relocations
Relative relocations and RELR | MaskRay
$ `brew --prefix binutils`/bin/objcopy -O binary --only-section=.data.rel.ro generated/bin/hello data.bin [00:34:16] [local] hikalium@t05.z01.hikalium.com: ~/repo/wasabi $ hexdump -C data.bin 00000000 58 01 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |X. .............| 00000010 58 01 20 00 00 00 00 00 01 00 00 00 00 00 00 00 |X. .............| 00000020 58 01 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |X. .............| 00000030 e0 04 20 00 00 00 00 00 08 00 00 00 00 00 00 00 |. .............| 00000040 08 00 00 00 00 00 00 00 20 06 20 00 00 00 00 00 |........ . .....| 00000050 f0 04 20 00 00 00 00 00 c0 05 20 00 00 00 00 00 |. ...... .....| 00000060 60 01 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |`. .............| 00000070 60 01 20 00 00 00 00 00 01 00 00 00 00 00 00 00 |`. .............| 00000080 61 01 20 00 00 00 00 00 08 00 00 00 00 00 00 00 |a. .............| 00000090 60 01 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |`. .............| 000000a0 e0 04 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |. .............| 000000b0 01 00 00 00 00 00 00 00 70 07 20 00 00 00 00 00 |........p. .....| 000000c0 b0 03 20 00 00 00 00 00 80 04 20 00 00 00 00 00 |. ....... .....| 000000d0 e0 04 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |. .............| 000000e0 01 00 00 00 00 00 00 00 70 19 20 00 00 00 00 00 |........p. .....| 000000f0 94 01 20 00 00 00 00 00 13 00 00 00 00 00 00 00 |.. .............| 00000100 37 00 00 00 05 00 00 00 94 01 20 00 00 00 00 00 |7......... .....| 00000110 13 00 00 00 00 00 00 00 53 00 00 00 05 00 00 00 |........S.......| 00000120 db 01 20 00 00 00 00 00 01 00 00 00 00 00 00 00 |. .............| 00000130 dc 01 20 00 00 00 00 00 03 00 00 00 00 00 00 00 |. .............| 00000140 a8 02 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |. .............| 00000150 a8 02 20 00 00 00 00 00 01 00 00 00 00 00 00 00 |. .............| * 00000170 40 1a 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |@. .............| 00000180 01 00 00 00 00 00 00 00 50 08 20 00 00 00 00 00 |........P. .....| 00000190 b8 02 20 00 00 00 00 00 00 00 00 00 00 00 00 00 |. .............| 000001a0 b8 02 20 00 00 00 00 00 02 00 00 00 00 00 00 00 |. .............| 000001b0
https://uclibc.org/docs/elf-64-gen.pdf
Hardening ELF binaries using Relocation Read-Only (RELRO)
https://maskray.me/blog/2021-08-29-all-about-global-offset-table
Airs – Ian Lance Taylor » Linker relro
時間切れ
というわけで追いかけていたら.data.rel.roの沼にはまったので今日はここまで。明日もやるかも。おやすみなさい…。