作業中、前触れもなく画面が暗転し、Macが再起動しました。重い処理をしていたわけでも、変なアプリを立ち上げたわけでもありません。していたのは、iPhoneをUSB-Cケーブルでつないでテザリングしていた、それだけです。
再起動後、/Library/Logs/DiagnosticReports/ にカーネルパニックのログが残っていました。この記事は、そのログを読み解いて原因を特定するまでの記録です。
パニックログの冒頭はこうでした。
panic(cpu 1 caller 0xfffffe0051333c60): m_copym_with_hdrs n 0xfffffe2ea3978000 copy overflow @uipc_mbuf.c:3268
暗号のような文字列ですが、分解すると読めます。
m_copym_with_hdrs — 関数名uipc_mbuf.c:3268 — その関数が定義されているファイルと行番号copy overflow — パニックの理由uipc_mbuf.c は macOS のカーネル(XNU)の中でも、mbuf(ネットワークパケットを格納するメモリバッファのチェーン構造)を扱うファイルです。m_copym_with_hdrs はそのmbufチェーンをヘッダーごとコピーする関数。つまりこのパニックは、アプリではなくOSの中核、ネットワーク通信を処理している最中に起きています。
copy overflow は、mbufチェーンが「自分の長さはこれです」と申告している値と、実際にチェーンが持っているバッファの容量が食い違い、コピー処理がバッファの境界を超えて書き込もうとしたことを検知した、という意味です。
パニックの直接の原因(どのパケット・どの通信だったか)はログからは分かりません。ですが、末尾にこんな一行がありました。
last started kext at 2540700876106: com.apple.driver.usb.cdc.ncm 5.0.0
com.apple.driver.usb.cdc.ncm は、iPhoneをUSBケーブルでテザリングしたときに、Macがそのイーサネット通信をエミュレートするために使うドライバです。パニック直前にロードされたkextがこれでした。
もちろん「直前にロードされた」=「原因」と即断はできません。単なる相関の可能性もあります。ただ、この時点で「もしかして」という当たりはつきました。
そのときの状況を思い出すと——していました。iPhoneをUSB-Cケーブルでつないでテザリングしていました。
ログに残っていた物的証拠(usb.cdc.ncm の直前ロード)と、実際にしていたこと(USB-Cテザリング)が一致し、原因はほぼ確定しました。iPhoneのUSBテザリング経由でネットワーク通信が流れていた最中に、mbufチェーンの長さ計算が壊れ、カーネルがoverflowを検知して安全側に倒した、という流れです。
調べてみると、iPhoneのUSBテザリングによるmacOSのカーネルパニックは、2010年代から続く息の長い既知カテゴリでした。
もう少し広く「ネットワークスタックの深い所で起きるカーネルパニック」というくくりで見ると、VPNやセキュリティソフトのようにパケット処理に割り込むソフトウェアが、通信負荷の高い状況で似た系統のパニックを起こす例も見つかりました。
共通しているのは、ネットワークスタックの深い所に割り込む経路(VPN・ファイアウォール・サードパーティNICドライバ・USBネットワークアダプタ)が、通信負荷が高い状況で潜在バグを踏むというパターンです。USBテザリングもまさにこの経路の一つでした。最新のmacOSでも、この種の不具合は根絶されていないようです。
cdc.ncm は関与しないので、有力な切り分けになる。トレードオフとして通信は多少不安定・低速になりうるsudo sysdiagnose をテザリング直後に取っておく — 今回のログは冒頭部分しか手元になかったが、フルの sysdiagnose にはもっと詳細な情報(プロセス名やネットワークの状態)が残るm_copym_with_hdrs)で検索すると、既知の不具合カテゴリかどうかの見当がつく「よく分からないから再起動して様子を見る」で終わらせず、残されたログを1行ずつ読むだけで、専門家でなくても当たりをつけるところまでは辿り着けます。今回の決め手も、複雑な解析ではなく「直前に何が読み込まれたか」という1行でした。