LVMのthin poolやdm-thinでマルチテナントの基盤を組んでいるなら、この記事は他人事ではない。
2026年9月24日、Cloudflareが自社のContainersで起きたテナント間のデータ露出を公表した。
研究者が新しく作ったコンテナのディスクを読むと、そこには他人のデータが残っていた。
- ディレクトリ構造
- データベースのページ
- 構造が完全なSQLiteデータベース
4大陸の22ノード、24の配置先で再現している。原因は、ストレージ設定に入っていたたった1つのオプションだ。
skip_block_zeroing
結論から言うと
1. Cloudflareの利用者は対応不要。 修正は9月19日に完了し、利用者側の設定変更は要らない。Cloudflareは保存されていた過去のディスクI/Oの記録を調べ、この手口が悪用された証拠は見つからなかったとしている。
2. 本当の教訓は「消し忘れ」。 64KiBの領域を割り当てて4KiBだけ書くと、残りの60KiB、つまり93.75%に前の利用者のデータが残りうる。
3. 同じ罠は自分の基盤にもありうる。 dm-thinやLVMのthin poolでゼロ埋めを切っていないか、今すぐ確認できる。しかも、設定を戻しただけでは過去に割り当てた領域は消えない。

AI生成の概念イラスト。利用者が替わっても内容が残る危険を表している。実際の流出資料ではない。
事実関係はCloudflareの公式の事後報告と、Linuxカーネルおよびlvmthinの公式ドキュメントによる。93.75%は筆者が64KiBと4KiBから計算した最大値で、実際の残存量はディスクの使われ方で変わる。筆者は攻撃を再現していない。
数字で見る今回の脆弱性
| 項目 | 値 |
|---|---|
| 対象 | Cloudflare Containers、Sandboxes(Containers上で動く) |
| 報告者 | Accomplish社のOren Yomtov氏(HackerOne経由) |
| 報告日時 | 2026年9月4日 15:26 UTC |
| 最初の修正 | 同日 21:27〜23:15 UTC(報告から約6時間後に着手) |
| 完全修正 | 9月19日 15:03 UTC |
| 再現範囲 | 4大陸の22ノード、24の配置先 |
| 検証できたディレクトリブロック | 5,614個。研究者自身のものは0個 |
| 見つかった他人のディレクトリinode | 2,700個 |
| 割り当て単位 | 64KiB(書き込みは4KiB単位) |
5,614個のうち、研究者が自分で作ったものは1つもなかった。読めたディレクトリブロックは、すべて他人のものだったということだ。
ここまでは「クラウド事業者のバグ」の話。本当に怖いのはその先だ。
なぜ新品のディスクに他人のデータが残るのか
thin provisioningは「使うときに割り当てる」
Cloudflare Containersの各コンテナは、Firecrackerの仮想マシン上で動き、書き込み可能なディスクを /dev/vdc として受け取る。このディスクの裏側が、Linuxのdevice mapperが提供するthin provisioning、つまりdm-thinだ。
dm-thinは、仮想ディスクの容量を最初から確保しない。書き込みが来た時点で、共有のデータプールから物理ブロックを割り当てる。複数のテナントが同じ物理プールを使い回す仕組みだ。
そして、カーネルのドキュメントにあるオプションの説明は、たった1行しかない。
Skip the zeroing of newly-provisioned blocks.
新しく割り当てたブロックをゼロで埋めない、という意味だ。
4KiB書くと、60KiBの中身がついてくる
dm-thinの割り当て単位は、最小で64KiBだ。今回の構成も64KiBだった。一方、ファイルシステムは4KiB単位で書き込む。
物理ブロック(64KiB = 4KiB × 16)
前の利用者: [旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧]
次の利用者: [新][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧][旧]
↑ 4KiBだけ書いた ↑ 残り60KiB = 93.75% は前の利用者のデータのまま
ゼロ埋めをしていれば、割り当てた瞬間に64KiB全体が0になる。skip_block_zeroing があると、書いた4KiB以外は前の内容が丸見えになる。
研究者がやったのは「書いて、読むだけ」
Cloudflareの報告によると、概念実証(PoC)の手順は驚くほど単純だった。
- Workers Paidのアカウントでコンテナを作る
- 書き込み可能なディスク
/dev/vdcを開く - ext4の空き領域と重なる、64KiB境界の領域を探す
- 各領域に4KiBのブロックを1つずつ書く
- 割り当てられた64KiBを読み、書いていない部分を調べる
特権の昇格も、カーネルの脆弱性も使っていない。自分のディスクを普通に読んだだけだ。
研究者は、読めたデータが本当に他人のものかを、ext4のディレクトリブロックのチェックサムで確かめた。その結果が、先ほどの「5,614個中、自分のものは0個」だ。
制約もある。攻撃者は標的の顧客やホストを選べず、使用中のディスクは読めず、データが残っている保証もない。他人のデータを改ざんできることも示されていない。研究者は回収したデータを安全に削除し、Cloudflareに提出した資料には中身を含めなかったとしている。
知らないと損する3つの罠
罠1:設定を戻しても、過去の領域は消えない
Cloudflareが最初の修正で skip_block_zeroing を外したのは、報告当日の夜だった。それでも、完全修正まではさらに15日かかった。
理由は、設定を戻してもこれから割り当てる領域しか守れないからだ。修正前に作られた次の2つも処理する必要があった。
- 稼働中のコンテナのディスク
- キャッシュされていたイメージのスナップショット
そのためCloudflareは、利用の少ない時間帯にホストを順番に空け、キャッシュを消し、ディスクとレイヤーをゼロ埋めした状態で作り直した。
LVMのマニュアル(lvmthin(7))も、はっきりこう書いている。
If zeroing mode is changed from 'n' to 'y', previously provisioned blocks are not zeroed.
対策:ゼロ埋めを有効に戻したら、それ以前に作ったボリューム、スナップショット、キャッシュ済みイメージを作り直すこと。
罠2:性能のためにゼロ埋めを切りたくなる
ゼロ埋めは割り当てのたびに書き込みが増えるので、遅い。lvmthinのマニュアルにも「Provisioning of large zeroed chunks reduces performance.」とある。カーネルのドキュメントは、ゼロ埋めしないなら大きめのブロックサイズ(128MiB前後)を勧めている。
つまり、性能チューニングとして正当な選択肢に見えるのが厄介なところだ。単一テナントなら問題ないこの設定が、マルチテナントでは分離を壊す。
対策:プールを複数のテナントで共有するなら、ゼロ埋めは切らないこと。性能が足りないなら、ブロックサイズや配置で解決する。
罠3:「新品のコンテナ」は中身まで新品とは限らない
コンテナを作り直せば、まっさらな環境になる。多くの人がそう思っている。でも実際に新しくなるのは、ファイルシステムが見ている範囲だけだ。その下の物理ブロックは使い回されている。
対策:使い捨ての実行環境は、ブロック層で何が初期化されるのかまで確認すること。
今すぐ確認:自分の環境のゼロ埋め設定
1. dm-thinのプールを直接見る
# thin-poolのテーブルに skip_block_zeroing が含まれていないか
sudo dmsetup table | grep thin-pool
sudo dmsetup table | grep -n skip_block_zeroing && echo "要確認: ゼロ埋めが無効"
thin-poolの行は <metadata dev> <data dev> <data block size> <low water mark> [<feature数> <feature>...] という形式だ。末尾の機能引数に skip_block_zeroing があれば、ゼロ埋めは無効になっている。
2. LVMのthin poolを見る
# zero列が 1(またはzero)なら有効。chunk_sizeも一緒に確認
sudo lvs -o lv_name,vg_name,lv_attr,zero,chunk_size
# lv_attr の8文字目が 'z' ならゼロ埋めが有効
sudo lvs -o lv_name,lv_attr --select 'segtype=thin-pool'
3. 新規プールの既定値を見る
# lvm.conf の既定値(コメントアウトされていればビルトインの既定値)
grep -n "thin_pool_zero" /etc/lvm/lvm.conf
sudo lvmconfig --type full allocation/thin_pool_zero
4. 無効だった場合の直し方
# 既存のプールでゼロ埋めを有効化(未実行の例。VG/プール名は環境に合わせる)
sudo lvchange -Z y VG/ThinPool
ただし、これで守れるのはこれから割り当てるブロックだけだ。 罠1のとおり、既存のボリュームとスナップショットは作り直す必要がある。
比較:同じ「消し忘れ」はどこに潜むか
| 層 | 消し忘れが起きる条件 | 確認方法 |
|---|---|---|
| dm-thin |
skip_block_zeroing が有効 |
dmsetup table |
| LVM thin pool |
-Z n で作成、または thin_pool_zero = 0
|
lvs -o zero |
| スナップショットやイメージキャッシュ | ゼロ埋めを無効にしていた期間に作成された | 作成日時と設定変更の履歴を突き合わせる |
| アプリのキャッシュや一時領域 | テナントをまたいで再利用し、初期化しない | 再利用時に消しているかをコードで確認 |
表から読み取れること:
- 消し忘れはブロック層だけの問題ではない。「領域の持ち主を変える処理」と「中身を消す処理」が分かれている場所なら、どこでも起きる。
- 設定を直す作業と、過去の成果物を作り直す作業は別のチケットとして扱う必要がある。
教訓:持ち主を変えても、中身は消えない
✗ "新しく作ったコンテナだから中身は空" → 物理ブロックは使い回されている
✗ "自分の部屋の鍵しか渡していないから安全" → 鍵は正しくても、引き出しに前の客の書類が残る
✗ "設定を直したから修正完了" → 過去に割り当てた領域とスナップショットは古いまま
✗ "ゼロ埋めは性能のために切っていい" → マルチテナントでは分離を壊す
✗ "特権がないと他人のデータは読めない" → 自分のディスクを普通に読むだけだった
Cloudflareの対応は速かった。報告から約6時間で修正に着手し、過去の記録まで調べて、経緯を公開した。それでも、設定1行が4大陸の基盤で分離を破っていたという事実は変わらない。
クラウドの分離というと、隣の利用者に侵入する攻撃を思い浮かべがちだ。でも今回の教訓は、前の利用者が去った後にも、守るべきデータが残るということだ。
今日、自分の基盤で dmsetup table と lvs -o zero を1回だけ叩いてみてほしい。
参考リンク
How Cloudflare addressed a cross-tenant data exposure vulnerability in Containers - Cloudflare Blog(2026年9月24日)
Thin provisioning - The Linux Kernel documentation
lvmthin(7) - Linux manual page
この記事が役に立ったら、いいねと保存を! 自前でコンテナ基盤や仮想化基盤を運用している仲間にもシェアしてほしい。
コメントで教えてほしい。
- あなたの環境の
lvs -o zeroの結果はどうだった? - 性能のためにゼロ埋めを切ったことはある?
- 「使い捨てのはずが残っていた」経験があれば、ぜひ聞かせてほしい。