メモリアクセス権限を制御する新しい方法「Intel MPK」

西村啓佑氏:西村が「Intel MPK入門」というタイトルで発表します。よろしくお願いします。

本日の発表内容です。Intel MPK(Memory Protection Keys)を一言で言うと、プロセス内のメモリアクセスの権限を効率的に制御する仕組みです。従来はPage Tableの設定がすなわちアクセス権限の設定なんですが、MPKはPage Tableを拡張して、新しい方法で権限の制御をすることが可能です。

さらに効率性に関しては、Page Tableの設定はRing 3からはできずにTLBinvalidationとかが発生するんですが、MPKはRing 3にいながらアクセスできる範囲を設定可能です。

ちょっとわかりづらいと思うので、ポンチ絵を用意しました。mprotect()を利用する場合は、システムコールなので必ずコンテキストスイッチが発生します。この図は、オレンジ色のメモリ領域のリード領域を落とそうとしているポンチ絵なんですが、2回コンテキストスイッチが発生しています。

一方、MPKを使う場合は、あらかじめPage TableにPkeyと呼ばれる属性を設定する必要があるんですが、ユーザーランドで実行可能なwrpkruという命令を発行するだけで、先ほどと同じような結果を得られます。このwrpkruは書き換えをする命令なんですが、このPKRUの引数に適切な値を設定することでこのようなことが実現できます。

ハードウェアではどのように実現をしているか

実際ハードウェアではどう実現されているかですが、主に2つの観点からなっています。

1つ目がPage Tableのエントリの拡張ですね。各Page Tableのエントリは、0から15のいずれかのPkeyを設定できるように拡張されています。これはSDM(Intel® 64 and IA-32 Architectures Software Developer Manuals)から持ってきた図です。この赤い部分がプロテクションキーと呼ばれるところで、ここに0から15のいずれかの値を新しく代入します。

さらにもう1つ、PKRUレジスタというアプリケーションから触れるレジスタで、wrpkru命令で設定できるものがあるんですが、このレジスタに各Pkeyに対するアクセス権限を設定します。現状では16Keyとされています。具体的にどうアクセス権限を設定していくかですが、Page Tableの設定にPKRUの内容を踏まえたものが実際のアクセス権限になります。

この図を見ると、書いているとおりという感じなんですが、注目すべき点はこれです。Page TableにWriteの権限がないんですが、Pkeyで仮にWrite Disableをオフにしていたとしても、実際のアクセス権限にWriteが入ってくることはありません。

また、ちょっとこれはおもしろいポイントなんですが、MPKを用いることで読めないけれど実行はできるという、すごくおもしろいメモリ空間を作れます。これはmprotect()などで内部的に使われているそうです。

次にソフトウェアがどれぐらいサポートされているかを説明します。Linuxでは、基本的に4.9からユーザーが使えるシステムコールが3つ入ってきました。pkey_alloc()、pkey_mprotect()が主に使われる命令ですが、pkey_alloc()はカーネル内部のビットマップ構造体を参照して、1ビット分のpkeyを発行するだけです。

pkey_mprotect()は、Page Tableのプロテクションキーに引数のPkeyをセットします。前後して申し訳ないんですが、このプロテクションキーは上の図ですね。赤で囲ったプロテクションキーに設定します。

また、PKRUを読み書きするwrpkru、rdpkruなどのアセンブリの命令のラッパの関数もglibcから提供されています。アプリケーション的な観点では、JITエンジンの一部やOpenSSLに適用する研究もあるんですが、有名なアプリケーションOSS側で導入されている話は今のところ私は把握していません。

ソフトウェア全体のアーキテクチャに影響を与える機能なので、簡単に導入するのは難しいと思うんですが、JITエンジンやハートブリードとかがあったSSLのライブラリなど、そういったものを導入するモチベーションはあってもいいと思います。

効率性について、先ほど紹介したシステムレイヤ的なサポートにどれぐらいのサイクルがかかるかという情報があったので、ここで紹介します。mprotect()と、この3つを比較すると2回以上アクセス権限を変更する場合、すごくザックリとした見積もりですが、MPKのほうが高速になると言えると思います。

MPKに関する3つの研究の紹介

残り時間は、MPKに関する研究を3つだけ紹介したいと思います。1つ目が、ATC(International Conference on Advanced Technologies for Communications )の2019年に発表された「libmpk」という研究です。15ページのペーパーを一言で言うとすると、高機能なMPKのライブラリを作るという研究です。

例えばハードウェアでは、Pkeyの数は16個に制限されているんですが、ソフトウェアレイヤーで仮想化することで、16個以上のPkeyをサポートできます。また、確保したKeyをフリーしたとしてもPage Tableに設定されたpkey_mprotect()の値は変更されません。

変更すると、このシステムコールが発行された時にPage Tableのトラバーサルが発生して、パフォーマンスがけっこう厳しくなるのでPkeyは変更しません。そこでキーのUse-after-freeの攻撃が考えられると思うんですが、そのMitigationをライブラリのレイヤーでやろうというのがこの研究です。

ただこの研究は、「制御が乗っ取られて意図しないwrpkruが実行されないこと」が前提になっています。

一方で、UsenixのSecurity(USENIX Security Symposium)で発表された「ERIM」は、アプリケーションをUntrustedなコンポーネントとtrustedなコンポーネントに分けて、MPKでアイソレーションする研究です。実行可能なページをスキャンすることで、wrpkru命令などの出現を検出して、それをよしなに書き換えることで乗っ取りに対して堅牢になります。

この書き換えの戦略は、ディスアセンブルしてよしなにするという、ちょっとアドホックな感じなんですが、20万個ぐらいのバイナリで試したらほとんど全部動いたということなので、基本的な正確性に関しては問題ないかと思います。

性能に関しては評価が載っているんですが、例えばNGINXの性能だとNativeの95パーセントぐらいが出そうです。もちろんかなりアプリケーションに依存するんですが、このような結果が出ています。ただこれはWriteとExecutionが同居していないことが前提なので、JITエンジンでこれを単純に適用するのは難しいのかなと思います。

もう1つ紹介するのがlibhermitmpkという研究です。これはVEE(Virtual Execution Environments)で2020年に発表された内容なんですが、これもすごくおもしろくて、一言で言うと、Unikernelの内部をMPKでアイソレーションします。ライブラリOSは基本的にシングルアドレススペースというのが売りで、それによってパフォーマンスを得ています。

裏を返すと、セキュリティとのトレードオフがあると言えますが、これはMPKを導入することによって、そのトレードオフをもっと良い点で探そうと考えている研究です。

最後に関連するトピックはこのようなものがあります。例えばソフトウェアベースのSFIもMPKとちょっと似たようなことを達成しようとしている研究です。VM-FUNCというMPKと同時期に流行っているCPUに導入された新しい機能もあります。

今回の発表のまとめです。MPKという効率的にプロセス内のメモリアクセス権限制御をする機能を紹介しました。これはRing 3で動作できて、コンテキストスイッチがいらないので高速です。Linuxなどではサポートされていますが、実際にどれぐらい使われているのかはちょっと不明です。これからの応用や研究や開発に期待したいと思います。こちらが参考文献です。以上です。ありがとうございます。