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メッセージを出して、永久ループへ入るみたいだ。

その他

死亡例その1 (わざと殺した)

FreeBSDでのKernel Core or Crash Dumpの取得方法