panic
今年のノーベル文学賞は、大方の予想を覆して、ボブ・ディランに決まった。 12月の講演会と受与式には、欠席するとか、お騒がせなこったい。 まあ、スゥーデンの見えざる外交威力に、抵抗してみましたって事かな。
それより、ディランと聞いて、とある言語を思い出しちゃったのは、オイラーだけかな?
Rubyist のための他言語探訪 【第 6 回】 Dylanで、紹介されてたぞ。
Gwydion Dylanで、公開されているみたい。 binaryも有るようだけど、32Bit版だからなあ。ソースからコンパイルするか。
[sakae@fb11 /tmp/gwydion-dylan-2.4.0]$ ./configure --with-gc-prefix=/usr/local : Build scenario: BOOTSTRAPPING_WITH_MINDY configure didn't find a pre-installed Dylan-to-C compiler. This means we have to bootstrap using a Dylan interpreter written in C. The interpreter is called mindy. :
あえなく撃沈。mindyとか言うのを見つけてくれば良いのだな。dylan-2.3.5.tgzの中に 入っていたけど、実行すると
[sakae@fb11 /tmp/z/bin]$ ./mindy Shared object "libm.so.2" not found, required by "mindy" [sakae@fb11 /tmp/z/bin]$ ldd ./mindy ./mindy: libm.so.2 => not found (0) libreadline.so.4 => not found (0) libc.so.4 => not found (0)
徹底的に古すぎて、にっちもさっちもいかない。しょうがないので、ちょっとdemosを 覗いて、体験した事にしておきましょ。
define method fact (x :: <integer>) => res :: <integer>; for (i :: <integer> from x to 2 by -1, result :: <integer> = 1 then result * i) finally result; end; end;
型をきちんと書け。for文が面白いな。その他、#t, #f が有ったりして、schemeから 借りてきてるっぽいな。流行っていたら面白かったろうに。
crashから見えるもの
前回ddbを追っていて、ソース中に埋め込まれたURLを見つけた。普段の生活では 出てこない貴重なページ。そのページの案内で、sendbugなんてコマンドを見ろ。 関連で、crash(8)も見ておけと言われた。
面白いの発見。生きたカーネルをgdbから観測出来るとな。きっとddbより親切なんだろうな。
Analyzing a live kernel Like the tools mentioned above, gdb(1) can be used to analyze a live system as well. This can be accomplished by not specifying a crash dump when selecting the “kvm” target: (gdb) target kvm It is possible to inspect processes that entered the kernel by specifying a process' struct proc address to the kvm proc command: (gdb) kvm proc 0xd69dada0 #0 0xd0355d91 in sleep_finish (sls=0x0, do_sleep=0) at ../../../../kern/kern_synch.c:217 217 mi_switch(); After this, the where command will show a trace of procedure calls, right back to where the selected process entered the kernel.
使うのは、システム備え付けのgdb。パッケージから入れたものには、カーネルメモリとの やり取りする機構が組み込まれていない。
# gdb bsd.gdb GNU gdb 6.3 : (gdb) target kvm #0 mi_switch () at ../../../../kern/sched_bsd.c:422 422 SCHED_ASSERT_LOCKED(); (gdb) bt #0 mi_switch () at ../../../../kern/sched_bsd.c:422 #1 0xffffffff81198836 in sleep_finish (sls=0xffffffff81ab7e90, do_sleep=Variable "do_sleep" is not available.) at ../../../../kern/kern_synch.c:266 #2 0xffffffff81198d72 in tsleep (ident=0xffffffff8194e1e0, priority=4, wmesg=0xffffffff8172285c "scheduler", timo=0) at ../../../../kern/kern_synch.c:148 #3 0xffffffff8117e2a1 in main (framep=Variable "framep" is not available.) at ../../../../kern/init_main.c:558
(gdb) p db_lo db_log db_look_char db_look_token db_loop_count (gdb) p db_log $1 = 1
ddbと違って、補完が利くから、あちこちを見て回るには楽だ。だが、当然ながら、 BPを置いて実行してみるって事は、出来ない相談。これをわきまえて使えば便利な 代物だ。
パニくる
クラッシュなんてmanを読んだものだから、本当にクラッシュさせてcoreを取得したく なった。とは言え、そうそう都合よくクラッシュさせられるのか?
ぐぐったら、ddbに落ちて、panicルーチンを呼べばいいらしい。
OpenBSD 6.0 (EXPL) #1: Thu Nov 10 15:16:51 JST 2016 root@ob.localdomain:/usr/src/sys/arch/amd64/compile/EXPL real mem = 184418304 (175MB) avail mem = 171143168 (163MB) : Stopped at breakpoint+0x9: leave ddb> call panic ddb> uvm_fault(0xffffffff81c05e00, 0x0, 0, 1) -> e fatal page fault in supervisor mode trap type 6 code 0 rip ffffffff812528cb cs 8 rflags 282 cr2 0 cpl d rsp ffff8000055ac0d0 panic: trap type 6, code=0, pc=ffffffff812528cb Starting stack trace... panic() at panic+0x15b trap() at trap+0x3a2 --- trap (number 6) --- kprintf() at kprintf+0x12e vsnprintf() at vsnprintf+0x61 panic() at panic+0xc9 db_fncall() at db_fncall+0x1c4 db_command() at db_command+0x315 db_command_loop() at db_command_loop+0xa9 : --- interrupt --- Xspllower() at Xspllower+0xc mtx_leave() at mtx_leave+0x34 Xsoftclock() at Xsoftclock+0x1f --- interrupt --- end trace frame: 0x0, count: 233 0x8: End of stack trace. syncing disks... done dumping to dev 0,1 offset 168543 dump 191 190 189 188 187 186 185 184 183 182 181 180 : 10 9 8 7 6 5 4 3 2 1 succeeded rebooting... OpenBSD 6.0 (EXPL) #1: Thu Nov 10 15:16:51 JST 2016 root@ob.localdomain:/usr/src/sys/arch/amd64/compile/EXPL real mem = 184418304 (175MB) avail mem = 171143168 (163MB) :
見た通り、パニックして、ちょいとその時の状況を報告してきた。後は証拠保全と言う事で、 swap領域へ自動保存して、OSが再起動してきた。上記のログは、起動後のdmesgから 拾ってきたもの。(ddbでのコマンド入力は保存されないので、手コピペしておいた。)
このままでは、証拠が扱い憎いswapに保存されているので、OS起動後の /etc/rcの実行に よって、証拠物件を、/var/crashと言う、白昼に置いてくれる。
root権限だと、dmesg -s とする事により、etc/rcの実行結果を後から、参照出来る。 下記は、その一部。
Automatic boot in progress: starting file system checks. /dev/wd0a (1b9cf83965a4c381.a): 16079 files, 211995 used, 165948 free (124 frags, 20728 blocks, 0.0% fragmentation) /dev/wd0a (1b9cf83965a4c381.a): MARKING FILE SYSTEM CLEAN setting tty flags pf enabled starting network : savecore: reboot after panic: trap type 6, code=0, pc=ffffffff812528cb savecore: system went down at Thu Nov 21 16:01:51 2016 savecore: /var/crash/bounds: No such file or directory savecore: writing core to /var/crash/bsd.0.core 195952K 194928K 193904K 192880K : 2416K 1392K 368Ksavecore: writing kernel to /var/crash/bsd.0 checking quotas: done. clearing /tmp kern.securelevel: 0 -> 1 creating runtime link editor directory cache. :
/etc/rcによると、autobootが設定されてるとfsckが行われ、その後にcoreの回収が 行われるようです。以下、その抜粋。
# Check and mount remaining file systems and enable additional swap. mount -a swapctl -A -t noblk do_fsck -N mount -a -N # /var/crash should be a directory or a symbolic link to the crash directory # if core dumps are to be saved. if [[ -d /var/crash ]]; then savecore $savecore_flags /var/crash fi
鑑識(の真似事)
下記は、鑑識の人達が精査する物件です。事故に有った航空機からブラックボックスを 回収したとも言えるな。
# ls -lh /var/crash/ total 418568 -rw------- 1 root wheel 2B Nov 21 16:03 bounds -rw------- 1 root wheel 12.9M Nov 21 16:03 bsd.0 -rw------- 1 root wheel 191M Nov 21 16:03 bsd.0.core -rw-r--r-- 1 root wheel 5B Jul 27 03:47 minfree
boundsってのが1になってた。次の証拠物件に付ける通し番号か。bsd.0は、パニくった時の カーネル。bsd.0.coreは、その時のメモリー内容。swapの容量は実メモリーの2倍と よく言われたけど、そこまでは必要なく、実メモリーとカーネルのサイズ分有ればよさそう。
今回の実験用OSは、swapを適当に実メモリーサイズ(256M)に設定してたんで、実メモリーを少々 少な目の192Mとした。それにしても、現代ならcoreと言う名前より、RAMと言う名前の 方が相応しいと思うけど、coreって名前は伝統だな。飲みすぎてcore吐いたとか言うからね。 このシチュエーションで、吞みすぎてRAM吐いたとは、絶対に言えないもの。
ちょっと、鑑識の真似事をしてみます。
# gdb GNU gdb 6.3 : (gdb) file /var/crash/bsd.0 Reading symbols from /var/crash/bsd.0...(no debugging symbols found)...done. (gdb) target kvm /var/crash/bsd.0.core #0 0xffffffff8146b635 in dumpsys () (gdb) bt #0 0xffffffff8146b635 in dumpsys () #1 0xffffffff8146b015 in boot () #2 0xffffffff8123e20f in reboot () #3 0xffffffff81251ac8 in panic () #4 0xffffffff8147938e in trap () #5 0xffffffff8147c032 in calltrap () #6 0xffffffff812528cb in kprintf () #7 0xffffffff8125276e in vsnprintf () #8 0xffffffff81251a2b in panic () #9 0xffffffff811f0170 in db_fncall () #10 0xffffffff811ef924 in db_command () #11 0xffffffff811eff35 in db_command_loop () #12 0xffffffff811f50ef in db_trap () #13 0xffffffff81498f7a in db_ktrap () #14 0xffffffff81479252 in trap () #15 0xffffffff8147c032 in calltrap () #16 0xffffffff81498fed in breakpoint () #17 0xffffffff81498fe2 in Debugger () #18 0xffffffff8181e45d in internal_command () #19 0xffffffff8181e79a in wskbd_translate () #20 0xffffffff8181c7c3 in wskbd_input () #21 0xffffffff8182b147 in pckbd_input () #22 0xffffffff810c4eaf in pckbcintr_internal () #23 0xffffffff810c4d2b in pckbcintr () #24 0xffffffff81493918 in intr_handler () #25 0xffffffff8147e6c9 in Xintr_ioapic_edge1 () #26 0xffffffff8149289c in Xspllower () #27 0xffffffff814928f0 in Xdoreti () #28 0x0000000000000000 in ?? ()
panicに至る、足取りがしっかり記憶されてますなあ。オイラーがdebuggerなんて知らないと 嘘をついても、電磁的証拠がばっちり残ってます。
# ps -N /var/crash/bsd.0 -M /var/crash/bsd.0.core -O paddr PID PADDR TT STAT TIME COMMAND 51146 ffff8000055ee238 C0 Ss+p 0:00.38 (ksh) 5744 ffff8000055eef58 C1 Is+p 0:00.11 (getty) # vmstat -N /var/crash/bsd.0 -M /var/crash/bsd.0.core -m Memory statistics by bucket size Size In Use Free Requests HighWater Couldfree 16 119 137 179 1280 0 32 121 263 1772 640 0 64 1505 31 3122 320 0 128 875 1141 17231 160 1179 : Memory usage type by bucket size Size Type(s) 16 devbuf, pcb, rtable, UFS mount, dirhash, ACPI, exec, VM swap, UVM amap, UVM aobj, temp 32 devbuf, pcb, rtable, ifaddr, sem, dirhash, ACPI, in_multi, ether_multi, exec, UVM amap, temp : Memory statistics by type Type Kern Type InUse MemUse HighUse Limit Requests Limit Limit Size(s) devbuf 496 259K 2360K 27015K 2097 0 0 16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536 pcb 22 10K 10K 27015K 45 0 0 16,32,128,1024 rtable 70 3K 3K 27015K 104 0 0 16,32,64,128,256 ifaddr 27 8K 8K 27015K 29 0 0 32,64,128,256,46 : Memory Totals: In Use Free Requests 2462K 2239K 26921 Memory resource pool statistics Name Size Requests Fail InUse Pgreq Pgrel Npage Hiwat Minpg Maxpg Idle arp 56 2 0 1 1 0 1 1 0 8 0 inpcbpl 288 24 0 5 1 0 1 1 0 8 0 plimitpl 152 13 0 6 1 0 1 1 0 8 0 : phpool 112 289 0 209 7 0 7 7 0 8 0 In use 2415K, total allocated 3788K; utilization 63.8%
メモリーの使用状況が、詳細に報告されてますよ。-m オプションは、普段も使いたいものだ。 思わぬ拾い物だな。
# w -N /var/crash/bsd.0 -M /var/crash/bsd.0.core USER TTY FROM LOGIN@ IDLE WHAT root C0 - 4:03PM 0 - sakae p0 10.0.2.2 4:08PM 0 -
おお、クラッシュの犠牲者も調べられるのね。 その他、fstatとかdmesgとか、色々ある。manして、-Nと-Mをサポートしてるものは、 鑑識に使える道具と思ってよさそうだ。なかなか、充実してるなあ。ハッカーは、自分が 使う道具は、全部自作しちゃうと言うけど、本当だな。実感出来るよ。
panic code
憧れのpanicが何処にあるか探してみた。kern/subr_prf.cに居たぞ。 ここで、ddb関連のsysctl関連も設定されてた。
#ifdef DDB /* * Enter ddb on panic. */ int db_panic = 1; /* * db_console controls if we can be able to enter ddb by a special key * combination (machine dependent). * If DDB_SAFE_CONSOLE is defined in the kernel configuration it allows * to break into console during boot. It's _really_ useful when debugging * some things in the kernel that can cause init(8) to crash. */ #ifdef DDB_SAFE_CONSOLE int db_console = 1; #else int db_console = 0; #endif
経験値がコードになってた。今更知っても、遅いわい。
/* * panic: handle an unresolvable fatal error * * prints "panic: <message>" and reboots. if called twice (i.e. recursive * call) we avoid trying to sync the disk and just reboot (to avoid * recursive panics). */ void panic(const char *fmt, ...) { : #ifdef DDB if (db_panic) Debugger(); else db_stack_dump(); #endif reboot(bootopt); /* NOTREACHED */ }
ddbで何とか出来る人はddbに任せましょ。そうで無い人には、スタックダンプしてから rebootしますとな。
ddbで何とか出来る人は、好きなように、瀕死のカーネルを弄べばよい。まあ、最後は、 boot syncで、syncしてからrebootするか。boot dumpで、syncしてcoredumpして rebootとかを実行する事になる。
他の選択肢として、haltさせるとかいきなりrebootとかpoweroffでマシンを冷却して やるとかが選べます。
ああ、panicは別の所にも定義してあって、それはOSが動き出す以前の(loaderとか)時に どうしようも無い状態を検出した時に使われる。 コードを見ると、panicメッセージを出して、永久ループへ入るみたいだ。