Linuxのページキャッシュ攻撃対策の影響は大きく、長期化する見通し【翻訳】

# はじめに
Jonathan Corbet 氏によるLWN.netの記事 [Dedfending aginst page-cache attack](https://lwn.net/Articles/776801/) の抄訳、及び関連情報のまとめです。注釈や関連情報を付記しました。
元記事は主に[Linux Kernel Mailing List](https://lkml.org/lkml/2019/1/5/104)で行われた議論、またLinux5.0以降で行われたシステムコールの挙動の変化についての解説となっています。

OSのメモリ管理機構の根本をなす仮想記憶のページキャッシュに脆弱性が報告されるようになりました。ページキャッシュのタイミングによる脆弱性は前々から指摘されていましたが、巧妙な手口が続々と見つかるようになりました。問題提起した論文では、LinuxとWindowsで脆弱性を突いていますが、他のOSも同様に脆弱性が存在します。また、システムコールの挙動が変更されたので、それらを使用しているアプリケーションにも影響があると考えられています。まだrc版（リリース候補版）での変更ですので、通常のカーネルを使用しているユーザには影響はありませんが、将来的には影響があるでしょう。
システムコールやその他機能/用語については参考文献をご覧ください。

# ライセンス
元記事に準じ、[by-sa-4.0](https://creativecommons.org/licenses/by-sa/4.0/)です。

# 概要

- ページキャッシュの脆弱性によりmincore() システムコールの挙動が変更された
    - mincoreはページキャッシュの状態を知るための機能である
    - 関係ない第3者のプロセスも他のプロセスが使用しているキャッシュの状態を知ることができる
        - 最初フォールトしたページのみ返すように変更したが、他プログラムのパフォーマンスへの影響を考え条件付きでキャッシュの状態を返すように緩和
    - ユーザースペースのプログラムへの影響を懸念

- その他のシステムコール、一部ファイルシステム、I/Oドライバにもページキャッシュ脆弱性が含まれていると見られている


# Defending against page-cache attacks
カーネルのページキャッシュ機能は、（ファイルアクセス時の）ディスクI/Oを低減し物理メモリでの共有を増進させることにより、パフォーマンスの向上に貢献してきた。

しかしながら、セキュリティ境界を超えてリソースを共有する他のパフォーマンス強化テクニックと同様、ページキャッシュも秘匿情報を読み出す方法として悪用される恐れがある。

最近のダニエル・グラスと彼の同僚の[論文](https://arxiv.org/pdf/1901.01161.pdf)の中で示されているように、ページキャッシュは種々の攻撃のターゲットになる恐れがあり、[5.0マージウィンドウ](https://lwn.net/Articles/776034/)にてmincore()システムコールの動作が突然変更されたことを紹介している。しかし後の論議で、mincore() は氷山の一角に過ぎないことを明らかにしている。ページキャッシュ攻撃からシステムを守るために本当は何を行うべきか、パフォーマンスがどれだけ犠牲になるかは不明なのである。

ページキャッシュは、メインメモリにファイルの断片的なコピーを保持しておく。プロセスがファイルからデータにアクセスする必要がある時、ページキャッシュにあるデータの存在によりディスクからの読み出しの必要がなくなり、処理が相当早くなる。複数のプロセスが同じファイルにアクセスする場合（例えばCライブラリ等）、同じコピーがページキャッシュで共有され、実行中の処理により要求されるメモリの総量が削減される。コンテナをホストするシステムでは、多くのランタイムシステムがこの方法で共有されている。


ページキャッシュは有用なものだが、この種のキャッシュ共有は時としてプロセス間で情報を開示してしまう恐れがあることが知られてきた。もし、攻撃者がページキャッシュに存在しているファイルを知ることができた場合、システムで実行中のプロセスの行っている事を知ることができてしまう。

<img src="https://qiita-image-store.s3.amazonaws.com/0/202996/79d8e954-8a10-cf6e-bfe9-ff09ce2ae769.png">
*arXiv:1901.01161v1 [cs.CR] 4 Jan 2019より借用*

攻撃者は、特定のページがキャッシュに乗っていることを観察できた場合、ある種のアクセスが行われているタイミングを特定することができる。例えば、特定の関数が呼ばれなかった時や、ある関数を含むページがキャッシュに現れた時を推測することが可能となる。グラスらは多くの脆弱性を突くことができた。それには密かなチャネルやキーストロークのタイミング等の情報が含まれ、キャッシュの情報を利用して完遂した。

ページキャッシュ攻撃の成功には2つの構成要素がある。一つは任意のページがキャッシュに乗っているかどうかを知ることが可能であること。できればプロセスのキャッシュの状態を引っ掻き回さないことが望ましい。もう一つは、そうしつつも特定のページをキャッシュから追い出せる事で、これはターゲットがそれらのページにアクセスするタイミングを見るために不可欠である。件の論文では、十分な量の他のページを用いてターゲットのページをキャッシュから簡単に立ち退かせる事ができた。これは成功したが、実はもっと簡単な方法があるかもしれない。

## mincore() システムコールの修正
開発者コミュニティの焦点は、ページキャッシュの場所情報を取得する機能に向けられて来た。 キャッシュの状態の変化を攻撃者から完全に防ぐことはおそらく不可能であろう（メモリ・コントロール・グループはこの点で助けになるかもしれないが）。だが、もし攻撃者がキャッシュの状態を観察できなければ、ほとんどの攻撃はかなり困難となる。確かに、ターゲットのページの追い出しに成功したかどうかを知ることは困難になるであろう。生憎にも、この情報を安全に守るのは容易ではない。

グラスの論文では[mincore()](http://man7.org/linux/man-pages/man2/mincore.2.html) システムコールを問題にしていた。mincore()システムコールは、ページキャッシュ状態のレポート処理のためのものであることはよく知られている。5.0のマージの結果、mincore()はプロセスから呼び出し時にフォールト[^1] したページについてレポートするだけの振る舞いに変更された[^7]。攻撃者は未だにmincore()を使用してページが立ち退いた時を知ることができるが、もはやページが他のプロセスによりフォールトバックされた時を観察するために使用することはできない。そうするためには、攻撃者は最初にページをフォールトしなければならず、欲しい情報を破棄することになる。

mincore()の動作を変更するというのは重大な変更だ。安定版アップデートではあえて変更は控えていた。現実的な地点に立って考慮すると、ユーザースペースのプログラムの挙動を破壊し、リバートするハメになるかもしれなかったからだ。Kevin EastonはDebianのパッケージで[mincore() システムコールを使用しているもののリスト](https://lwn.net/ml/linux-kernel/20190108085058.GA23237@ip-172-31-15-78/)を作成したが、それでもどれだけのパッケージが壊れてしまうことになるか未だに不明である。おそらくそのリストの中で一番問題として上がるのは[vmtouch](https://hoytech.com/vmtouch/) であるが、仮想マシンの起動を高速化するために既知のワーキングセット[^2] をプリフォルトするいくつかのセッティングで使われている。

この致命的な影響について、Josh Snyder[^10] は「Netflixにとって、mincore システムコールによる正確な情報を失うならば、データベースクラスタのメンテナンス[^9]に数日を要していたのが数カ月に引き伸ばされるだろう」と[報告した](https://lwn.net/ml/linux-kernel/5c3e7de6.1c69fb81.4aebb.3fec@mx.google.com/)。この報告は指導的な開発者たちにオプションを再考するように促し、mincore()を特権付き実行に変更するシステムモードを追加することもそれには含まれた[^8]。そのアイデアは恐らく[Dominique Martinetにより提案・採用されたもの](https://lwn.net/ml/linux-kernel/20190116054613.GA11670@nautica/)に近いと思われる。彼は（mincoreの）呼び出し元（のプロセス）が、マッピング元のファイル書き込みが許可されている時に限り情報が提供されるべきだ、と述べた。これによりシステム実行可能ファイルからのページがモニタリングされる事を防ぎつつ、Netflixのユースケースが解決するだろう。この方法による[パッチの実装](https://lwn.net/ml/linux-kernel/nycvar.YFH.7.76.1901162120000.6626@cbobk.fhfr.pm/)がJiri Kosinaにより投稿された。

## さらに大きな問題
現実的な解決策が見つかったとすれば、もっと大きな問題を解決して終結させようと試みる人もいるだろう。でもこのケースは今の所そのようなものではない。David Chinnerは[preadv2()](http://man7.org/linux/man-pages/man2/readv.2.html) システムコールをRWF_NOWAITフラグと併用すると、ページキャッシュのコンテンツの非破壊テストに使えることを[指摘した](https://lwn.net/ml/linux-kernel/20190108044336.GB27534@dastard/)[^15]。利用可能な解決策としては、RWF_NOWAIT読み込み時にページキャッシュ内のデータ探索失敗時にreadaheadを初期化することで、キャッシュの状態を変更し、同時に一般ユーザのためにも可能な限りパフォーマンス向上ができる。上に掲げたKosinaのパッチにはこれらの変更が含まれている。

Chinnerはこれらのパッチをモグラたたきのようなものであり、なおたくさんのモグラがいる状況のただ中にいると見ている。彼は「多くのカーネルインターフェースはデータが即時使用可能かどうか問い合わせるように設計されている」と述べた（これは一般的にはページキャッシュの中にあるということを意味している）。この情報は正しい方法で取得でき有用なので多数のアプリケーションに利用されている。もう一つの脆弱性への道筋は、彼いわくoverlayfsであり、コンテナ間でのページキャッシュの手段として使用されている。彼によると、mincore() の変更は間違ったアプローチだと言う。

> これは特定の読み出し方法に対する急ごしらえな絆創膏に過ぎず、情報漏洩の実際の範囲を狭める点では何もしない。絆創膏にばかり目が行くと、同じく情報を晒している他の経路や、我々が築いてきたインフラの全てが「セキュリティ境界を超えてカーネル側でページを共有する」という中核的な概念の上にある、ということを見逃してしまうだろう。

その後の論議で、[彼はもう一つの脆弱性の道筋を明らかにした。](https://lwn.net/ml/linux-kernel/20190109043906.GF27534@dastard/) 少なくともいくつかのファイルシステムで、直接I/O読み出しをページ上で行うとページをキャッシュから追い出してしまうことで、（ページキャッシュを）無効化させる事が、攻撃者には非常に容易になる。更にいくつかの白熱する議論が有ったが、「XFSのようにファイルシステムがそうするのは正しいのか」と言う点についてもあった。（[Linus Torvaldsはこれをバグと見なしている](https://lwn.net/ml/linux-kernel/CAHk-=wjc2inOae8+9-DK4jFK78-7ZpNR=TEyZg0Dj57SYwP-ng@mail.gmail.com/))。 しかしながらこの議論から分かる一つのはっきりとした点は、この振る舞いはすぐに変更されるということはなさそうという事だ。

すべての穴が塞がったとしても、鈍器のような問題が引き続き存在する。単純なタイミング攻撃の問題だ。もし特定のページの読み込みが早ければ、そのページはほぼ確実にキャッシュにある。もし時間がかかるなら、それは多分（HDD,SSDのような）永続ストレージからの読み出しであるはずだ。タイミング攻撃は普通煩わしいので容易に気づかれるが、今でも利用することができる。そして新たなる穴が将来出てくるように思える。別の討議の中でChinnerは最近投稿された[virtio pmem デバイス](https://lwn.net/ml/linux-kernel/20190109144736.17452-1-pagupta@redhat.com/)[^5] の機能が同じ仕方で悪用されうることについて[コメントした](https://lwn.net/ml/linux-kernel/20190110012617.GA4205@dastard/)。[io_uringの機能](https://lwn.net/Articles/776703/)[^6]については、現在の形でマージされるとすれば、攻撃者にとってページキャッシュ問い合わせを容易にするであろう。

他の言い方をするならば、この問題はほとんど解決不能なように思われる、少なくとも絶対的な意味では。おそらく行いうる最善の策は敷居を十分に高くし、殆どの攻撃を防ぐよう務める事、であろう。それで、カーネルが「セキュア・モード」に設定されている場合に限り、ページキャッシュ状態への非破壊問い合わせの既知のメカニズムを停止させると思われる。タイミング攻撃を完全に防御するのはあまりにも困難である（もしくはコストがかかる）。 それで、[Linusが投稿したように](https://lwn.net/ml/linux-kernel/CAHk-=wigwXV_G-V1VxLs6BAvVkvW5=Oj+xrNHxE_7yxEVwoe3w@mail.gmail.com/)、完全なセキュリティを待望している人々はいつものように失望することになるだろう。

>我々はすべてのサイドチャネルアタックを防ぐことは**決して**無いだろう。キャッシングのある部分は(タイミングによる影響については特に）とても根本的なものだからだ。
>それで絶対的な線を地面に引くことなんて、**どうやったって** 不可能だ。白黒つけて「君は守られている」なんて言うことはできない。便利さの違いがあるだけだ。

既知のベクタの悪用を封鎖するのは、既存のユーザースペースのアプリケーションに問題を起こさないようにする点で、未だに問題を残している。MeltdownやSpectre同様、カーネル開発者を当分忙しくさせる問題のように思える。


# 原文、1次情報
  - [元記事 - Defending against page-cache attacks](https://lwn.net/Articles/776801/)
  - [ページキャッシュアタック脆弱性の論文 - Daniel Gruss](https://arxiv.org/pdf/1901.01161.pdf)
  - [カーネルメーリングリストのスレッド - [PATCH] mm/mincore: allow for making sys_mincore() privileged　1/30投稿](https://patchwork.kernel.org/patch/10749313/)
  - [mincoreの変更に関するLinus Torvaldsの声明　1/6 時点](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=574823bfab82d9d8fa47f422778043fbb4b4f50e)

# 参考文献
- [ハードウェア非依存の新たなサイドチャネル攻撃“Page Cache Attacks”が発表](https://forest.watch.impress.co.jp/docs/news/1163514.html)
- [The Linux Kernel - 4. メモリ管理](http://archive.linux.or.jp/JF/JFdocs/The-Linux-Kernel-4.html)
- [Linuxカーネルのページキャッシュとbuffer_head、address_spaceとの関係について](https://qiita.com//todok-r/items/334e721533c462831a12)
- [Linuxでページキャッシュを確認・解放してみた](https://qiita.com/toshihirock/items/e2d187e91ee5446c7a69)
- [
いまさら聞けないLinuxとメモリの基礎＆vmstatの詳しい使い方](https://qiita.com/kunihirotanaka/items/70d43d48757aea79de2d)
- [ページフォルト - wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%95%E3%82%A9%E3%83%BC%E3%83%AB%E3%83%88)
- [ワーキングセット - wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AF%E3%83%BC%E3%82%AD%E3%83%B3%E3%82%B0%E3%82%BB%E3%83%83%E3%83%88)
- [mincore - man](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/mincore.2.html)  
- [ファイルがページキャッシュに乗っているかを調べる](https://qiita.com/ryuichi1208/items/bbf221ab66562e1426ca#_reference-b669f942c8f20589b4d3)
- [happycache](https://github.com/hashbrowncipher/happycache) 
- [vmtouchでファイルがキャッシュに乗っているか確認](https://yomon.hatenablog.com/entry/2017/06/30/161551)
- [CAP_SYS_ADMIN](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/capabilities.7.html)
- [preadv2 - man](https://manpages.debian.org/testing/manpages-dev/preadv2.2.en.html) 
- [XFSのダイレクトI/Oによるページキャッシュ回避](https://www.atmarkit.co.jp/ait/articles/0408/24/news093.html)
- [Overlay Filesystem - Neil Brown](https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt)
- [docker - OverlayFS ストレージの使用](http://docs.docker.jp/engine/userguide/storagedriver/overlayfs-driver.html)


# 一方その頃OpenBSDは・・・

> mincoreが嘘をつくようにしてやった。メモリ共有の本性ってのは、他のプロセスがやっている事をスパイできるってことだ。そんな事を我々は望まないので、「メモリがコア（キャッシュ）に乗っている」といつも返すようにしてやった。
[https://twitter.com/OpenBSD_src/status/1089658147294273536](https://twitter.com/OpenBSD_src/status/1089658147294273536)

IntelのHyperThreading機能は脆弱性の巣であるとして、[HyperThreading機能**ごと**無効にしたOpenBSD](https://www.mail-archive.com/source-changes@openbsd.org/msg99141.html)ですが、流石であると言わざるを得ません。そこにしびれる憧れる。

[^1]: プログラムが物理メモリがマップされていない仮想アドレス空間上のページにアクセスしたときにハードウェアが発生する割り込み（または例外）。

[^2]: プロセスがある時点で使用中の仮想メモリページの集合。

[^7]: このパッチはLinus自ら書いた。

[^8]: 具体的にはsysctl_mincore_privilegedが有効な時、CAP_SYS_ADMINがないとEPERMが返る。

[^9]: happycacheという自前のツールを使い、DBメンテナンス時のリブート前のキャッシュをダンプ/リストアさせている。

[^5]: kvmで用いられる、ゲストのページキャッシュを回避するための疑似的な永続化メモリ

[^6]: 旧来の asynchronous I/Oを刷新する目的で導入されるインターフェース

[^10]: NetFlixのクラウドエンジニア

[^15]: preadv2をRWF_NOWAITフラグと同時に使うと、データが即時読みだせない（すなわちキャッシュにない）場合、何も返さないのでmincoreによる検査の代わりに使える。4.14から使用可能。