メモリー保護機能とは
現代のコンピューターシステムには、メモリー保護機能が実装されている。
メモリー保護機能の目的は、
-
システムの中核部分を、故意や過失による干渉から保護し、システムの安定性や安全性を確保する
-
複数のユーザーがシステムを使用するような場合、他のユーザーのプライベートな情報にアクセスできないように保護する
というものがある。後者の機能は、個人専用のシステムにおいても、ネットワーク経由からの攻撃を緩和する為に利用されている。
メモリー保護機能は、OSによって実現されるが、現代のプロセッサーではメモリー保護の実装を支援するため、以下のような機能を備えている。
-
スーパーバイザーモードとユーザーモード
-
MMU
スーパーバイザーモードとは、特権的な命令を実行できるモードで、OSのカーネルコードを実行するモードとして使用されている。そのため、特権モードや、カーネルモードと呼ばれることがある。
また、スーパーバイザーモードでのみアクセスできるメモリーブロックを設定することができるので、カーネルのみで使用できるメモリー (カーネルメモリー) として使用される。
ユーザーモードは、一般のユーザーのコードを実行するモードで、特権命令の実行や、カーネルメモリーへのアクセスは制限される。
MMU は、仮想メモリーを実現する機能で、仮想メモリーと物理メモリーのアドレスを変換して、透過的にメモリーアクセスする能力を持っている。アドレス変換には、マッピングテーブルが使用される。
マッピングテーブルは、プロセスごとに設定できるので、プロセス間で共有するメモリーブロック、プロセス固有で使用するメモリーブロックという設定が可能になっている。
上記のカーネルメモリーの設定も、このマッピングテーブルが使用されている。
アドレス変換はオーバーヘッドが大きい処理であるため、プロセッサーには TLB と呼ばれるアドレス変換専用のキャッシュ機構が搭載されている。
Meltdown 脆弱性とは
投機実行による領域外メモリーデータ漏洩の可能性については、
Spectre 脆弱性について
https://qiita.com/noumia/items/303e890fb50730bd5115
で解説している。
Meltdown 脆弱性は、このデータ漏洩が、カーネルメモリーにも通用してしまうという問題である。
通常ユーザーモードからは、カーネルメモリーはアクセスできないため、カーネルメモリーにアクセスしようとすると割り込み (例外) が発生し、コードの実行が中断される。
Meltdown 脆弱性のあるプロセッサーでは、投機実行によってカーネルメモリーへの読み込みが行われた後で、割り込み処理が実行される場合がある。
投機実行で読み込まれたデータは破棄されるが、メモリーキャッシュを使用したサイドチャネル攻撃によって、データを推定することが可能となってしまう。
probe_memory : [0x100000] uint8
1) probe_memory をキャッシュからフラッシュする
2) x = *(カーネルメモリーの仮想アドレス) // このコードはアクセス違反例外を引き起こす
3) y = probe_memory[x << 12] // このコードが投機実行されるとメモリーキャッシュに痕跡が残る
例外ハンドラー内:
probe_memory をスキャンして、カーネルメモリーのデータバイトを推定する
Meltdown 脆弱性の緩和策
通常、スーパーバイザーモードから、ユーザーモードへ遷移する際、プロセスの持つアドレスマッピングテーブルを操作して、スーパーバイザー専用メモリーブロックのマッピングを外す処理は行われていなかった。
これは、マッピングがそのままでも、ユーザーモードからカーネルメモリーへのアクセスはブロックされるため、問題はないとみなされていたためである。
また、マッピングテーブルの操作は、TLB のキャッシュを無効化することになるため、TLB ミスが頻発することになり、性能に影響するためでもある。
Meltdown 脆弱性の緩和策として、カーネルメモリーのデータ漏洩を防ぐため、ユーザーモードに遷移させるときに、カーネルメモリーへのマッピングを取り除くという手法がとられた。
そのためこの対策を行うと、カーネルモードとユーザーモードの切り替えが頻発すると、前述のように TLB ミスが増加するものと考えられる。
実際、カーネルモードとユーザーモードの切り替えを伴う I/O 操作が中心となる、高負荷のデーターベースサーバー等のワークロードでは、性能に顕著な影響がある模様である。