PCIの動作とLinuxによるDMAバッファの処理)に既に精通している場合は、実際の質問について3番目のセクションに進んでください。そうでない場合は、PCIデバイスがメモリアクセスを実行する方法の簡単な概要を読んでください。 DMAを使用したデバイスとの通信をカーネルがどのように処理するか。同じ質問をしている人々に役立つ情報を提供することを期待し、私の理解が失われた場合に他の人が私を修正する機会を与えるために、これをここに含めました。
PCIおよびPCIeデバイスの構成スペースには2バイト コマンドレジスタ があり、これにはいくつかの異なるハードウェア機能を有効または無効にするためのビットマスクが含まれています。ビット2は、バスマスターイネーブルビットであり、これを設定すると、デバイスはDMA=要求を開始できます。このビットとその他のビットはコマンドレジスタは、スーパーバイザモードで実行されているソフトウェアによって(通常はカーネルドライバによって)設定され、PCIデバイスに物理的に格納されているにもかかわらず、それによって変更することはできません(おそらく、PCHは各デバイスのPCI構成スペースのシャドウコピーを保持しますか?) 。IOMMUのないハードウェアでは、デバイスは任意の正当なメモリアドレスへの読み取りと書き込みを要求できます。これは、DMA攻撃または悪バスマスタリングと呼ばれることが多く、保護されていないシステムでの問題です。悪意のあるPCIデバイス。IOMMUは、セキュリティとパフォーマンスの両方を向上させるソリューションとなるはずです。参考までに、インテルの実装であるVT-d(正確には、より新しいVT-d2)について具体的に質問します。
ほとんどのシステムは、DMA再マッピングまたはDMAR用に構成できます。 BIOSに含まれるACPIテーブルには、多くの場合、さまざまなPCIグループがすべてのメモリアクセスをルーティングするアドレスのリストを含むDMARテーブルがあります。これはすべて VT-d仕様 のセクション2.5.1.1で説明されています。ドキュメントのグラフィック:
DMARテーブルはBIOSによってハードコードされています。特定のPCIデバイス(または、特定の IOMMUグループ )は、所定のメモリ範囲にのみアクセスできます。カーネルには、そのメモリの場所が通知され、DMA経由で読み取り/書き込み可能にしたくないメモリをそこに割り当てないように指示されます。再マッピング値は、カーネルログバッファーで報告されます。
DMAR:デバイス0000:00:02.0のIDマップの設定[0xad000000-0xaf1fffff] DMAR:デバイス0000:00:14.0のIDマップの設定[0xa95dc000-0xa95e8fff] DMAR :デバイス0000:00:1a.0のIDマップを設定しています[0xa95dc000-0xa95e8fff] DMAR:デバイス0000:00:1d.0のIDマップの設定[0xa95dc000-0xa95e8fff] DMAR:LPCの0-16MiBユニティマッピングの準備 DMAR:デバイス0000:00:1f.0のIDマップの設定[0x0-0xffffff] DMAR:インテル(R)ダイレクトI/Oの仮想化テクノロジー iommu:デバイス0000:00:00.0をグループ0に追加 iommu:デバイス0000:00:01.0をグループ1に追加 iommu:デバイスを追加0000:00:02.0をグループ2 iommuに追加:0000:00:14.0をグループ3 iommuに追加:デバイス0000:00:16.0をグループ4 iommuに追加:デバイス0000:00:1a.0をグループ5に追加 iommu:デバイス0000:00:1b.0をグループ6に追加 iommu:デバイス0000:00:1c.0をグループ7に追加 iommu:デバイス0000:00:1c.2をグループ8に追加 iommu:デバイス0000:00:1c.3をグループ9に追加 iommu:デバイス0000:00を追加: 1c.4からグループ10 iommu:デバイス0000:00:1d.0をグループ11に追加 iommu:デバイス0000:00:1f.0をグループ12に追加します iommu:デバイス0000:00:1f.2をグループ12に追加します iommu:デバイス0000:00を追加します:1f.3からグループ12 iommu:デバイス0000:01:00.0をグループ1に追加 iommu:デバイス0000:03:00.0をグループ13に追加 iommu:デバイスを追加0000:04:00.0からグループ14 iommu:デバイス0000:05:00.0をグループ15 に追加
太字の行から、グループ11には(のみ)デバイス_0000:00:1d.0
_が含まれ、_0xa95dc000 - 0xa95e8fff
_の範囲の13ページのメモリに自由にアクセスできることがわかります。グループ11のデバイスへのすべてのアクセスはそこにのみ書き込むことができ、他のDMAバッファー、または関連しないOSコードの内容を変更することを防ぎます。このようにして、デバイスにバスがあってもマスタービットが設定されている場合、書き込み先を追跡する必要はありません。また、(偶然または悪意により)本来の場所に書き込むことはできません。
カーネルドライバーは、DMAを介してデバイスとやり取りする場合、_void *addr = kmalloc(len, GFP_KERNEL | GFP_DMA)
などを使用して、この目的のために特別にメモリを割り当てます。これはaddr
に、DMAの使用に適したサイズのlen
バイトのメモリの連続セクションを指す仮想メモリアドレスを返します。これはすべての詳細は DMA APIドキュメント で説明されています。ドライバは、この共有メモリ領域を介してPCIデバイスと自由に通信できます。簡略化された一連のイベントは、次のようになります。
カーネルはこれらのバッファーを暗黙的に信頼していますか?DMA=バッファー?悪意のあるまたは侵害されたPCIデバイスは、指定されたバッファー以外に書き込み(IOMMUが他のことを防ぐ)でき、データを悪用してカーネルを侵害できます。それらが共有している構造?明らかな答えは可能性のあるです。これは、メモリの安全でない言語を使用した複雑なデータ構造の共有と解析には、悪用のリスクが伴うためです。しかし、カーネル開発者は、これらのバッファは信頼されており、悪意のあるアクティビティからカーネルを保護するための努力は一切していません(たとえば、非特権ユーザーランドとcopy_from_user()
および同様の関数を介してカーネル間で共有されるデータとは異なります)。 IOMMUの制限にもかかわらず、悪意のあるPCIデバイスがホストを危険にさらすことができるかどうかの答えはおそらくだと思います。
このような脆弱性の悪用は次のように機能します。ここで、buf
はデバイス制御のDMA書き込み可能なアドレス空間にあり、dest
はカーネルメモリの他の場所にあります。
struct { size_t len; char data[32]; char foo[32]; } buf
_としてデータを書き込みます。struct { char data[32]; bool summon_demons; } dest
_のdata
にコピーします。buf.len = sizeof(buf.data) + 1
および_buf.foo[0] = 1
_を悪意を持って設定します。memcpy(dest.data, buf.data, buf.len)
を使用して、データを安全にコピーしません。明らかにこれは不自然な例であり、おそらくそのような明らかなバグがそもそもカーネルに反映されない可能性が高いですが、それは私の要点を示し、私の第一の質問に導きます:
DMAを介して共有されるデータ構造の不適切な処理、またはPCIデバイスからの入力を信頼できるものとして扱う特定のドライバーの脆弱性の例はありますか?
私はその制限を認識しており、デバイスがIOMMUを直接回避する方法、またはシステムの制御を取得するために別の抜け穴を使用する方法を説明しようとする答えは望んでいません。知っている:
* DMAはダイレクトメモリアクセスを意味します。これは、特定のハードウェアインターフェイス(PCIeなど)がCPUを経由せずにシステムメモリへの直接アクセスを要求できるハードウェア機能です。
最近、NDSSS 2019で公開された論文を読んでいましたが、この論文は2月に発表されたので、質問に完全に答えると思います。質問がなされた当時、脆弱性があることに対する答えは はい、しかしそれらは5.0からLinuxカーネルで修正されました のようです。 ネットワークと分散システムのセキュリティシンポジウム2019 で発表された論文に属するスライドは、 こちら にあります。
ThunderboltインターフェースにもDMAがあるため、質問は依然として関連しています。 Linuxのパッチノートは here にあります。