Meltdownのライブパッチ – SUSEエンジニアの研究プロジェクト(パート4 – 結論)

Share
Share

パート1 (Meltdownのライブパッチの主な技術的障害)パート2 (仮想アドレスマッピングとMeltdown脆弱性)およびパート3 (アドレス変換バッファー(TLB)フラッシュプリミティブに必要な変更)の説明で、作業に取りかかる準備がすべて整いました。最後の欠けたピースは、実際にエントリーコードを置換して、カーネルに移行するときとユーザースペースに戻るときに、ページテーブルの2つのバリアント間で相互に切り替えを行わせることです。

最初に説明したように、当初の考えは、エントリーコードを参照するすべてのCPUのポインターを、ライブパッチの置換へのアドレスに置き換えることでした。ただし、この操作は元に戻せる必要があり、最終的にはライブパッチモジュールをアンロードできる必要があります。モジュールをメモリからマッピング解除する前に、エントリーコード置換内のどの場所でもタスクが現在実行されておらず、今後も実行されないことを確実にする必要があります。

たとえば、あるスレッドが、何らかのsyscallを発行して、置換されたエントリーコードからカーネルに移行したとします。このsyscallの実行中に、エントリーコード置換がマッピング解除されてしまったと仮定します。syscallが完了したときには、すでに削除されてしまったエントリーコードへの戻りアドレスがスタックから取得されて、当然この場所に戻ろうとしますが、それによってさまざまな弊害が発生します。実際、いずれかのCPUで現在実行中でないタスクは、カーネルスペース内のどこかでスリープ状態になっており、したがってそのスタックには、エントリーコードを参照する戻りアドレスが格納されています。

これに対処するため、現在エントリーコード置換内で実行中であるか、またはエントリーコード置換への戻りアドレスがスタックに現在格納されているすべてのタスクを、参照カウントを使用して追跡します。このカウントは、カーネルスペースに移行するときにインクリメントし、ユーザースペースに戻る直前に再びデクリメントします。細かいですが重要な注意点として、このインクリメントは、タスクが再びスケジュールアウトされる可能性がある地点に到達する前に行っておく必要があります。そうしないと、スタックに存在するエントリーコードへの参照が失われる可能性があります。同様に、デクリメントは、スケジュールされる可能性が完全になくなった後でのみ行う必要があります。

この参照カウントを実装したライブパッチモジュールのクリーンアップハンドラーは、パッチ適用前の元のエントリーコードへのCPUのポインターを復元し、参照カウントがゼロに戻るまでは実行をブロックします。その後、最後のschedule_on_each_cpu()が実行されることで、カウントがデクリメントされてから実際にユーザースペースに戻るまでのわずかな期間にタスクが実行されていないことが確実になります。

最後に、タスクのforkとexitに特別な対応が必要であることに注意してください。forkではカーネルへの移行がないままカーネルを出ていき、exitではその逆となります。これらの参照カウンターの不均衡に対応するために、kGraftでカーネルのforkおよびexitコードにパッチが適用され、不均衡の調整が行われています。

結論

このように、複雑なMeltdown脆弱性であっても、理論上、kGraftを使用してライブパッチを適用できます。私がこの研究を楽しんだのと同様に、読者にも楽しんでいただけたら幸いです。さらなる参照のために、提案検証(POC)のソースをご紹介します: https://github.com/nicstange/meltdown-livepatch

私を勇気づけ支援してくれた同僚のMiroslav Benes、Jiri KosinaそしてLibor Pechacekに特別な感謝を捧げます。

 

このトピックに関する一連のブログ記事4件を、クイックリファレンスとして以下に示します。

Share
(Visited 1 times, 1 visits today)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

No comments yet

Nicolai Stange
915 views