FlawOpen

flawopen.com/Incidents/rust-unsafe-smallvec-overflow-january-2021

● servo/rust-smallvec · CVE-2021-25900 · January 2021 · CVSS 9.8 · 緊急
セキュリティ研究 · FlawOpen

CVE-2021-25900:Rust smallvecのヒープバッファオーバーフロー

SmallVec::insert_manyはsize_hintの下限を信頼し、イテレーターが多く返すとヒープバッファを超えて書き込みました。0.6.14と1.6.1で修正。

💡 わかりやすい解説 (ELI5)

ロボットがすべての棚を確認してから物を置くので安全だと評判の倉庫があります。いくつかの作業ではロボットを止め、人が手作業で、注意すると約束して作業します。そのうちの一人が運転手に「だいたい何箱持ってきた?」と尋ね、その大きさの棚を組んで積み始めました。見積もりは推測であって保証ではなく、約束より多くの箱が届いたとき、山は棚の端からはみ出しました。

主要な概念と専門用語

SmallVec
少数の要素を内部に直接持ち、大きくなるとヒープ上のバッファに切り替える Rust のコレクション。その速さは、バッファを手作業で管理する unsafe なコードによるものです。
size_hint
残りの要素数の推定下限と上限を返す Iterator のメソッド。あくまでヒントで、安全なコードは誤った値を含め、どんな値でも返せます。
unsafe Rust
unsafe ブロック内のコードは、生ポインター経由の書き込みなど、コンパイラーが確認できない処理を行えます。作者はあらゆる安全な呼び出し元に対してメモリ安全性を保つ責任を負います。
健全性(soundness)
どんなに変わった安全なコードでも、その API を通じて未定義動作を引き起こせないなら、安全な API は健全です。insert_many は、正当な size_hint でメモリを壊せたため健全ではありませんでした。

フレームワーク対応

インシデントのタイムライン

2021-01-08

Yechan Bae 氏とジョージア工科大学 SSLab の Rust グループがバグを報告する。RustSec が RUSTSEC-2021-0003 を公開し、smallvec 0.6.14 と 1.6.1 で修正が出る。

2021-01

CVE-2021-25900 が割り当てられ、cargo audit が依存関係ツリー内の影響を受けるバージョンを指摘するようになる。

ステップ・バイ・ステップの攻撃フロー

ステップ 1

イテレーターが長さを少なく申告する

size_hint の下限が実際に出す要素数より小さいイテレーターを渡して insert_many を呼び出します。安全なコードにはそれが許されています。

ステップ 2

ヒントを基に領域が確保される

insert_many は下限分の領域だけを確保し、既存の要素をその分だけずらします。

ステップ 3

それでも全要素が書き込まれる

unsafe ブロックの中でループが生ポインター経由で要素を一つずつ書き込むため、余った要素は確保した領域の末尾の先に書かれます。

ステップ 4

ヒープが壊れる

オーバーフローは隣接するヒープのメモリを上書きします。信頼できない入力の影響を受けるイテレーターを insert_many に渡すプログラムが危険にさらされます。

ソースコード比較:脆弱 vs 堅牢化

脆弱な実装
// *ヒント*の値に基づいて確保する……
let (lower, _) = iter.size_hint();
self.reserve(lower);

unsafe {
    let ptr = self.as_mut_ptr().add(index);
    for (i, item) in iter.enumerate() {
        // ……しかし実際には届いた分だけ
        // 項目を書き込む。
        ptr::write(ptr.add(i), item);
    }
}
堅牢化されたセキュアパッチ
// 項目ごとに容量を再確認する。
// 安全なコードの値に
// unsafe コードで使う上限を決めさせない。
for (i, item) in iter.enumerate() {
    self.reserve(1);
    unsafe {
        let ptr = self.as_mut_ptr()
                      .add(index + i);
        ptr::write(ptr, item);
    }
}

// 実際の修正ではさらに
// unsafe の範囲を大幅に縮小した。

エンジニアリング&システム堅牢化チェックリスト

参考資料