BSD界隈四方山話

第9回カーネル内部のセキュリティを強化、ネストカーネルでメモリ区画化

ネストカーネル - カーネル内メモリアクセスを区画化

2015年6月10日、カナダのオタワで開催されたFreeBSD開発者らによるデベロッパサミットFreeBSD Developer Summit, June, 2015において、カーネルモードにおけるメモリアクセスを区画化し、カーネルモードでの動作をより安全にする取り組み「ネストカーネル(Nested Kernel⁠⁠」が紹介されました。イリノイ大学アーバナ・シャンペーン校の研究者らによって取り組まれたものです。FreeBSD 9.0を対象に実施されており面白いものでした。開発も活発で今後FreeBSD本家へ取り込まれる可能性もありそうです。

UNIX系オペレーティングシステムはソフトウェアをプロセッサの特権モードのうち、2つのモードのどちらかで実行します。すべての機能を使用できるスーパバイザモードか、機能が制限されたユーザモードかです。通常、カーネルがスーパバイザモードで動作し、それ以外のソフトウェアがユーザモードで動作します。システムに影響を与えるような処理はカーネルにまとめておき、ユーザモードで動作するソフトウェアがそれらの機能を必要とした場合には、システムコールを呼び出してカーネルに処理をお願いします。

このような仕組みにすることで、一般のソフトウェアがほかのソフトウェアに与える影響を抑え、バグや、または故意に組まれた悪意あるコードによってシステムがおかしくなることを防いでいます。しかしこれは反面、カーネル自体はなにも保護されていない、ということも意味しています。

ユーザモードで動作しているソフトウェアはシステムコールを呼ぶことでカーネルに処理を依頼できるわけですが、さまざまな仕組みを使うことで故意にカーネルに誤った処理をさせることができる可能性があります。バグがあればそこが使われますし、最終的にバッファオーバーフローなどを引き起こさせて任意のコードを実行させたり、機密性の高い情報が抜き出されるなどの恐れがあります。カーネルという最後のリゾートが突破されてしまうと、セキュリティもなにもないわけです。

ゼウス・カーネル故のもろさ

カーネルの何が問題かといえば、カーネルは全メモリのどこにでも自由にアクセスできてしまう、というのが問題です。カーネルにバグがあれば、またはセキュリティ脆弱性を突かれてしまえば、あとはメモリ上のデータをやりたい放題に書き換えることができますので、もうそこにリソース保護やセキュリティといったものはないわけです。

近年、特にセキュリティへの高い必要性から、こうしたモノリシック指向のカーネルにおいて、どうやってセキュリティを向上させるかといった研究が盛んに行われています。研究内容は多岐に渡りますが、今回カナダのデベロッパサミットで発表された「ネストカーネル(Nested Kernel⁠⁠」は、MMUやプロセッサの機能を使ってカーネルがアクセスするメモリ領域を区画化および保護化し、特権を超えた処理ができないようにしよう、というものです。

カーネル内メモリアクセスを区画化またはモニタリングして、不適切なアクセスを防止するという取り組みはほかにもありますが、ネストカーネルは既存のモノリシックカーネルと相性が良いこと、カーネルの変更が少ないこと、ネステッドカーネルのコードもかなり短いこと、といった特徴があります。さらにカーネルサイズもほとんど変わらず、パフォーマンスの低下も目をつぶれるほど小さいという特徴があります。

FreeBSDカーネルをベースとしたメモリ区画化の研究はネストカーネルに限らないため、この技術が最終的にマージされるとは限りませんが、今後さらに高まるセキュリティへの需要を受けて、将来どこかのタイミングでこうした機能がマージされることは間違いのないように思います。

ネストカーネルの抜本的なアイディア

ネストカーネルでは、次の過程がアイディアの根幹をなしています。

メモリ管理ユニット(MMU:Memory Management Unit)のページテーブルへのアクセスを規制することができれば、カーネル内部においてもメモリの区画化を実現できるのではないか。たとえそれが単一のハードウェア特権レベルで動作しているのであったとしても。

カーネルはこれまでスーパバイザモードで動作し、その世界はすべての特権を持つものでした。ネストカーネルのアイディアは、このスーパバイザモードで動作するカーネルを仮想化して、そこでのメモリアクセスをより細かに規制して保護すればよいのではないか、というものです。

ここで従来のカーネルは次の2つのカーネルに分かれます。従来のカーネルに相当するものは「アウタカーネル」と呼ばれ、新しく追加されるカーネルが「ネストカーネル」と呼ばれています。そして、ユーザランドからシステムコールが呼ばれると、カーネルは次のような処理をするようになります。

  1. システムコールが呼ばれる
  2. アウタカーネルが呼ばれる
  3. アウタカーネルはネストカーネルを呼ぶ
  4. ネストカーネルが処理を実施する

処理は「アウタカーネル」「ネストカーネル」の2つに分かれています。そして従来と異なり、それぞれ次のような規制が加わります。

  • アウタカーネル
    • ページテーブルは常にリードオンリー
  • ネストカーネル
    • カーネルコードはリードオンリー
    • カーネルデータは実行不可能(Non-executable)
    • ユーザコードはSMEP(Supervisor Mode Execution Protection)
    • ユーザデータはSMEP(Supervisor Mode Execution Protection)
    • ページテーブルは常にリードオンリー

アウタカーネルもネストカーネルも動作するのは同じ特権レベルであるスーパバイザモードですが、今までなんでも操作できたカーネルと異なり、自ら規制を敷いて、操作できる内容を限定化しています。このようにすることでメモリアクセスや変更できる領域または特権を区画化し、従来よりも悪さがしにくい構造にしています。

なお、ネストカーネル側では書き込みへの介入およびチェック、書き込みのロギングなども実施し、不正な処理を検出するようになっています。

ネストカーネルは結構速い

こういった機能を追加すると処理が重くなるのではないか、という懸念があります。いくらセキュリティが向上すると言っても、重い処理は使われなくなるのが世の常です。しかし、研究者らの発表によれば、マイクロベンチマークが示す数値の差はとても小さく(1~3%⁠⁠、両者のバランスを見てみても採用した方が利益が大きいように思えました。

さらにFreeBSD 9.0に対しての変更、カーネル構造の変更に伴う内容が次のように小さいという特徴があります。

  • 変更が加えられたのは52ファイル
  • 変更したコードは1,900行未満
  • 削除したコードは100行未満

ネストカーネルとスキャナのコードも次のように軽量です。

  • 4,000行ほどのC言語ソースコード
  • 500行ほどのアセンブラソースコード
  • 250行ほどのPythonスキャナコード

前提条件としては、プロセッサおよびロジックボードがIOMMU、SMP、NX、SMEPをサポートしている必要があります。研究者らは今後の作業として、現在取り組んでいる部分の機能の実装をコンプリートすることと、FreeBSD-CURRENTへの移植、リファクタリングの実施、VT-xなどのプロセッサが提供している仮想化機能を活用する機能の追加などを課題として挙げていました。

BSDCanや併設されるFreeBSD DevSummitにはこうした興味深い発表が多くありますので、興味のある方は参加を検討してみてください。

告知 FreeBSD勉強会

  • 第40回FreeBSD勉強会
    • 2015年7月9日(木)19:00~21:30
    • The Design and Implementation of the FreeBSD Operating System (2nd Edition) 読書会 61ページ Return from the Kernel~
  • 第41回FreeBSD勉強会
    • 2015年7月23日(木)19:00~21:30
    • The Design and Implementation of the FreeBSD Operating System (2nd Edition) 読書会

おすすめ記事

記事・ニュース一覧