KVMゲストでkernel-4.15.2を使ったら起動しなくなった
Googleから発表のあったCPU脆弱性に対応する為、連休を使って自鯖のアップデートを順次行っていた。
本番系はkernelをパッケージ管理システムで管理しているので、そのままアップデートで問題ないのだが、
テスト鯖はkernelもビルドして構築しているので、対応に合わせてkernelを入れなおす必要があった。
という事で、2018/02/11時点の最新kernelである4.15.2を使ったのだが、
kernelを入れ替えた後、画面がブラックアウトして起動しなくなった… _(:3」∠)_
テスト環境と言っても、キャッシュDNSサーバを兼用していたり、
監視システムの一部を担っているので直さないと不味く、徹夜してなんとか原因っぽい所まで突き止めた。
という事で、今回はハマりかけた事の顛末などを時系列ベースで書いてみようと思う。
免責事項:英文など解釈を間違っていたらゴメンナサイ
- 何が起こったのか
「あ…ありのまま 昨夜起こった事を話すぜ!」
「おれは kernelビルドが終わったので自鯖を再起動したら サーバが起動しなくなってしまった」
「な…何を言っているのかわからねーと思うが おれも何が起きたのかわからなかった…」
「頭がどうにかなりそうだった…」
「ビルド失敗だとかGRUB記述ミスだとかそんなちゃっちなもんじゃあ断じてねえ」
「もっと恐ろしいものの片鱗を味わったぜ……」
kernelパニックになるならデバッグ次第でなんとかなるのでマシなのだが何故かパニックにならず、
GRUBを読み込んだ後にブラックアウトしたまま、CPU使用率100%でブン回るだけだった。
- 切り分け開始
手がかりと言えば、ブラックアウトする直前に表示されるコンソールが1行。
文章からして、kernel-4.15.0で入ったmeltdown/specter対策のコード暴走かと思い、
GRUBに [pti=off spectre_v2=off] を追加して起動してみたが、CPU使用率は上がったままだった。
- kernelのソース解析
「とりあえず、kernelのソース読んで見るか」とプログラムを読めもしないのに読んでみた。
結果、どうやら下記の「retpoline_auto」辺りのソースが怪しい事がわかった。
retpoline_auto:
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
retpoline_amd:
if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
pr_err("LFENCE not serializing. Switching to generic retpoline\n");
goto retpoline_generic;
}
mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD : SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
} else {
retpoline_generic:
mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC : SPECTRE_V2_RETPOLINE_MINIMAL;
setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
}
spectre_v2_enabled = mode;
pr_info("%s\n", spectre_v2_strings[mode]);
}
|
ちなみに、ns-labの自鯖ホスト機はIntelが1機・AMDが2機稼働している。
今回のテスト機が動いているのはAMDホストサーバの方だったので、
『な~んか、CPUエミュレーションしている箇所も怪しいな…』という事と、
CPUベンダーの判定箇所で転けているっぽい事まではわかった。実際の原因は違うのですが…
そもそもの、meltdown/spectre脆弱性はCPUハードウェア設計が起因の多岐に渡る脆弱性ではあるのだが、
AMDのCPUに限って言えば、AMDの公式見解が下記の通りあった。
- meltdownはIntelCPUのみ対象
- Spectre Veriant1はOSレイヤの対応、
- Spectre Veriant2はAMDも対象だが悪用は極めて困難
ただ、前途の通りGRUBでmeltdown/spectreの対応コードをオフにしてもダメだった事から、
大元となる原因は他に存在すると仮説を立てつつ、他の切り口を調べてみた。
- デバッグログ
振り出しに戻ってしまったので、GRUBの起動オプションに [loglevel=7] も追記してリトライ。
すると「random: crng init done」が出た直後に無限ループに入っている事が判明。
このエラーはラズパイでSDカードのファイルシステムが壊れた際に出やすいので、
『まさかHDDクラッシュ……?』と思ったのだが、ファイルシステムが壊れる予兆は全く無かったのと、
kernelを切り戻すと普通に起動する事から違う問題と仮定。
- メーリングリスト
原因が全くわからないので、最後の手段であるLinuxMLをググってみた。
結果、それっぽい事がバグ報告が見つかった (`・ω・´)
斜め読みした所、kernelのメモリ管理をスパース行列にした際、
QEMUのブートプロセスがランダムにハングアップするバグと書いてあった。
kernelが起動しなくなった自鯖環境はQEMUでエミュレーションしているのと、
メモリ管理はスパース型にしている事から状況にも合致しており、恐らくコレが原因だろうと推測。
- 対処方法
不具合の起きた、kernel-4.15.2はstableだったが、上記バグ報告はkernel-4.15.0だった。
なんとなく『kernel-4.15.2にマージされていないのかなぁ~』と思い、
ビルドオプションはkernel-4.15.2と同じままで、kernel-4.15.0に下げてみたら普通にサーバが起動した。
………
業務利用とかでkernelビルドする事は(少なくとも自分は)あまり無いと思うが、
今回の不具合を経験してMLとログの重要性を再認識した。
こんな事が出来るのも自鯖のメリットなので、
楽するのが一番だけれども、自鯖位なら敢えて茨の道を突き進むのもアリだなと思った。
…と、記事を書いていたらkernel-4.16.0rc1が出ていたのでバグが直っているか早速ビルド中。
どうなるかは、本日の筆者twitterをご確認ください ヘ(゚∀゚ヘ)
kernel-4.16.0-rc1で入れ直したら起動出来ました。どうやら、バグコードが修正された模様。