Design and Implementation of
Gracious Days

昨年の夏からずっとこの問題に悩まされていて、Appleがいまだに修正してくれないので状況をまとめてみた。

macOS を NFS サーバとして使う

VMWare や VirtualBox を使い仮想マシン上で別の OS を動かしている場合、気になるのはストレージの消費である。わたしは主に開発用途で各種 Unix系OSを扱うことが多いが、GitやSubversionでソースファイルを展開し、試作用ブランチも含めて 10-20 GB くらいはすぐに埋まってしまう。

そこで、ホストの macOS を NFS サーバとして、複数の仮想マシンからホームディレクトリを NFS マウントすることで共有する使い方をしている。設定は簡単だ。たとえば/etc/exports に次のように書いて、nfsd enable && nfsd startとすれば良い。仮想マシンが接続される内部ネットワークが完全に管理でき、使用者しかログインしない個人のPC上であれば、export範囲をそのネットワークに限定するだけでセキュリティ対策は十分だろう。

/Users/hrs  -maproot=0:0 -network 172.16.0.0 -mask 255.255.0.0

あとは、仮想マシン上のOSから、ホストOSのIPアドレスにアクセスしてNFSマウントすれば良い。

問題

Mac OS X が macOS に名前を変えた 10.12 以降、上記のような使い方をしていたらマシンが落ちるようになった。クラッシュダンプをみると、いつもnfsdのプロセスの実行コンテキストで落ちている。

Anonymous UUID:       A830AD5D-A7AA-1116-BC69-8E6F8FF79559

Tue Dec  6 05:09:46 2016

*** Panic Report ***
panic(cpu 2 caller 0xffffff800ba05ead): Kernel trap at 0xffffff800bb0821f, type 14=page fault, registers:
CR0: 0x000000008001003b, CR2: 0x0000000100000074, CR3: 0x0000000454779065, CR4: 0x00000000003627e0
RAX: 0xffffff803ddd9f00, RBX: 0xffffff803ecb1201, RCX: 0x000000000000a504, RDX: 0xffffff802b5b89b0
RSP: 0xffffff91fa5c3610, RBP: 0xffffff91fa5c3680, RSI: 0xffffff802b5b89b0, RDI: 0x0000000000000000
R8:  0x0000000100000000, R9:  0x000000000000a504, R10: 0x0000000000000006, R11: 0xffffff803ac01a28
R12: 0xffffff91fa5c3c88, R13: 0x0000000000000000, R14: 0x0000000000000000, R15: 0x0000000000000000
RFL: 0x0000000000010206, RIP: 0xffffff800bb0821f, CS:  0x0000000000000008, SS:  0x0000000000000010
Fault CR2: 0x0000000100000074, Error code: 0x0000000000000000, Fault CPU: 0x2, PL: 0, VF: 1

Backtrace (CPU 2), Frame : Return Address
0xffffff91fa5c32a0 : 0xffffff800b8f211c 
0xffffff91fa5c3320 : 0xffffff800ba05ead 
0xffffff91fa5c3500 : 0xffffff800b8a3743 
0xffffff91fa5c3520 : 0xffffff800bb0821f 
0xffffff91fa5c3680 : 0xffffff800bb159cf 
0xffffff91fa5c3750 : 0xffffff800baacbad 
0xffffff91fa5c37b0 : 0xffffff800ba80fb0 
0xffffff91fa5c3e50 : 0xffffff800bab2743 
0xffffff91fa5c3ef0 : 0xffffff800bab15a8 
0xffffff91fa5c3f50 : 0xffffff800be273ca 
0xffffff91fa5c3fb0 : 0xffffff800b8a3f46 

BSD process name corresponding to current thread: nfsd

Mac OS version:
16C60b

Kernel version:
Darwin Kernel Version 16.3.0: Tue Nov 29 12:39:07 PST 2016; root:xnu-3789.31.2~11/RELEASE_X86_64

NFSが原因であることが分かったため、アクセスパターンに依存性があるかどうかを調べたところ、どうも深いディレクトリをrm -rで削除するタイミングで必ず落ちるようだ。しかし、macOSをNFSクライアントにして、127.0.0.1でループバックマウントして同じ操作をしてみても落ちない。

パケットダンプをして調べてみると、次のタイミングで落ちることが分かった。192.168.0.10が外部のNFSクライアント、192.168.0.122がmacOSである。

03:17:22.388639 IP (tos 0x0, ttl 64, id 22647, offset 0, flags [DF], proto TCP (6), length 288)
    192.168.0.122.2049 > 192.168.0.10.1033309124: reply ok 232 lookup fh Unknown/4E5800005885F95A0000000100000008002B4BBF002B4BBF0000000100000002 DIR 755 ids 0/20001 sz 68 nlink 2 rdev 0/0 fsid 1000002 fileid 2b4bbf a/m/ctime 1481048229.000000 1474554809.000000 1478175928.000000 post dattr: DIR 755 ids 0/20001 sz 102 nlink 3 rdev 0/0 fsid 1000002 fileid 2b4bbe a/m/ctime 1481048229.000000 1474554809.000000 1478175928.000000
03:17:22.388653 IP (tos 0x0, ttl 64, id 3392, offset 0, flags [DF], proto TCP (6), length 172)
    192.168.0.10.1033309125 > 192.168.0.122.2049: 116 rmdir fh Unknown/4E5800005885F95A0000000100000008002B4BBE002B4BBE0000000137000000 "7"
03:17:22.388912 IP (tos 0x0, ttl 64, id 58314, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.0.122.2049 > 192.168.0.10.978: Flags [.], cksum 0x4cb0 (correct), seq 20705, ack 14493, win 4092, options [nop,nop,TS val 527924047 ecr 2310910003], length 0
03:17:22.392801 IP (tos 0x0, ttl 64, id 2870, offset 0, flags [DF], proto TCP (6), length 200)
    192.168.0.122.2049 > 192.168.0.10.1033309125: reply ok 144 rmdir PRE: sz 102 mtime 1474554809.000000 ctime 1478175928.000000 POST: DIR 755 ids 0/20001 sz 68 nlink 2 rdev 0/0 fsid 1000002 fileid 2b4bbe a/m/ctime 1481048229.000000 1481048242.000000 1481048242.000000
03:17:22.392870 IP (tos 0x0, ttl 64, id 3393, offset 0, flags [DF], proto TCP (6), length 172)
    192.168.0.10.1033309126 > 192.168.0.122.2049: 116 lookup fh Unknown/4E5800005885F95A0000000100000008002B4BBE002B4BBE000000022E2E0000 ".."

このダンプは、mkdir -p 1/2/3/4/5/6/7; rm -rf 1 というコマンドを実行した結果である。NFS RMDIR "7"の後、NFS LOOKUP ".."が発行されているのが分かる。落ちるのは、このNFS LOOKUPのRPCを受け取った瞬間だ。ディレクトリの削除でなくても、NFS LOOKUP ".."が発生するアクセスを行なうと、簡単に再現できる。

macOS のNFSクライアントの場合、rmdir で再帰的にディレクトリを削除する時に".."を使わないので落ちない。上位ディレクトリのエントリはキャッシュに入っている値を使うようだ。キャッシュの影響が出ないようなアクセスでcd ..を実行したら、macOSのNFSクライアントでも落すことができた。当初はFreeBSDのNFSクライアントに依存する問題かと思ったが、原因がこの RPC にあるのは確実だろう。

ちなみに検証にはFreeBSDの他、nfsshellを使った。

影響

NFS LOOKUPのRPC一発でOSが落ちてしまうというのは、とてもフラストレーションが溜る状況であるとともに、リモートDoSの危険性がある。幸い、正しいファイルハンドルでなければ受理しないようなので無差別に落せるわけではない。NFSサーバを有効にしないように注意することをお勧めする。

影響する範囲は 10.12 以降のすべて(本エントリの執筆時点では 10.12.3 beta まで確認)である。それ以前では再現しない。バグレポートは送っているものの半年近く修正されないままなので、ちゃんと読まれていないのかも知れない。


Next post: DTrace における同じプローブに対する複数のアクションとcopyin()

Previous post: OSSコミュニティの継続性