FirecrackerのCVE-2026-122192を読む:メモリの解放に失敗すると、ゲストからVMMのメモリを読み書きできる仕組みと対象条件

FirecrackerのCVE-2026-122192を読む:メモリの解放に失敗すると、ゲストからVMMのメモリを読み書きできる仕組みと対象条件

Firecracker の CVE-2026-122192 は、スナップショットから復元した VM でメモリの解放に失敗しても処理が続き、ゲストの root が Firecracker プロセスのメモリを読み書きできる問題です。仕組みを図で整理し、対象の条件と対策をまとめます。
2026.11.07

どうも!オペ部の西村祐二です!

2026 年 11 月 4 日、AWS のセキュリティ速報 2026-169-AWS で、Firecracker の脆弱性 CVE-2026-122192 が公表されました。特定のホストのメモリ状況では、スナップショットから復元した microVM で、ゲストの root がホスト側の Firecracker プロセスのメモリを読み書きできた、という内容です。AWS は、AWS Lambda など自社のサービスには影響がないとしています。一方、Firecracker を自分で運用している環境は対象になりえます。

この記事では、修正の PR とアドバイザリを読んで「なぜメモリに穴が開くのか」を図で整理し、自分の環境が対象かどうかの確かめ方と対策をまとめます。

先に結論:対象の条件と対策

次の 3 つすべてに当てはまる場合が対象です。

確認すること 対象になる条件
Firecracker のバージョン v0.23.0〜v1.16.1(サポート中の系列では v1.16.0 と v1.16.1)
スナップショットの復元方法 PUT /snapshot/load で File メモリバックエンドを使っている
VM のデバイス balloon(ゲストのメモリをホストに返すデバイス)か virtio-mem(メモリを後から追加・削除するデバイス)がある

対策は v1.16.2 か v1.17.0 以降への更新です。どちらもスナップショットの形式が変わっているので、保存済みのスナップショットは作り直しが要ります。すぐに更新できない場合は、Uffd バックエンドで復元するのが緩和策です。スナップショットを使わずに起動した VM と、Uffd バックエンドで復元した VM は対象外です。

ホストカーネルのバージョンは、アドバイザリの対象条件には含まれていません。カーネルのバージョンによって穴が残るかどうかが変わる点は、「なぜ成立するのか」で説明します。

何が起きたのか

AWS の速報と Firecracker のアドバイザリ GHSA-48ww-rqpp-2mgm で確認できた内容は次のとおりです。

  • 内容: ゲストメモリのある範囲を解放(discard)する処理が失敗すると、その範囲の一部がマップされていない状態のまま、Firecracker が動き続けていました。その後、Firecracker が同じ範囲に確保したメモリを、ゲストから読み書きできることがありました。
  • 深刻度: アドバイザリでは CVSS 3.1 が 7.5(CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H)。AWS の速報には CVSS の記載はありません。
  • 悪用状況: 速報とアドバイザリに悪用の記載はありません。
  • 報告者: Coinspect の Juan Pablo Martinez Kuhn さん(速報とアドバイザリによる)。

時系列を並べると、修正版は公表より 2 か月ほど前に出ています。

日付 出来事 出典
2026-09-01 修正の PR #6174 がマージ(1.16 系へのバックポートの #6178 は 09-02) PR #6174
2026-09-10 v1.16.2 と v1.17.0 のリリース v1.16.2 のリリースノート
2026-11-04 アドバイザリと AWS の速報を公開 上記の速報とアドバイザリ

v1.16.2 のリリースノートには、#6174 で直した問題として、スナップショットのメモリファイルから復元した microVM で、virtio-mem が unplug したメモリを VMM から書き込める状態のまま残し、マップされないままにすることもあった、という趣旨の記載があります。リリースノートが挙げているのは virtio-mem だけですが、アドバイザリは balloon を持つ VM も対象にしています。

攻撃の流れ

  1. 前提条件: 上の 3 条件に当てはまる VM で、攻撃者がゲストの中の root を持っていることが前提です(CVSS の PR:H)。さらに、後で説明するメモリの置き換えが失敗するホストのメモリ状況も必要です(AC:H)。
  2. 悪用: ゲストが balloon や virtio-mem の unplug でメモリを返すと、Firecracker はその範囲を解放するため、空のマッピングで置き換えます(以下、置き換え)。この置き換えに失敗すると、範囲が穴になったまま Firecracker が動き続けます。
  3. 影響: Firecracker が後で確保したメモリがその穴に入ると、ゲストから読み書きできます。アドバイザリは、ゲストの root が Firecracker プロセスのメモリを読み書きできると説明しています。CVSS でもスコープの変更(S:C)として評価されています。

この記事の図は、すべて一次情報を基に自作した概念図で、公式の構成図ではありません。図 1 は、登場するものの関係と、上の 2 と 3 の流れを示したものです。

CVE-2026-122192 の仕組みを示す自作の概念図。上段のゲストには root を持つ攻撃者がいる前提で、ゲストの物理メモリのうち一部を、返した範囲として示している。中段の KVM は、メモリスロットでゲストの物理アドレスを Firecracker プロセスのアドレスに対応づける。下段の Firecracker プロセスのアドレス空間には、スナップショットのメモリファイルを MAP_PRIVATE でマップしたゲストメモリの領域がある。番号1でゲストの balloon または virtio-mem のドライバがメモリを返し、番号2で Firecracker 側のデバイスが、返された範囲を置き換える。置き換えに失敗するとその範囲は穴のまま残り、後で確保したメモリが入ると、番号3の経路でゲストから KVM を通じて読み書きできる。

図 1 の①と②が上の 2、③が上の 3 にあたります。③の経路で読み書きできるのは、ゲストのメモリの実体が Firecracker プロセスのメモリだからです。Firecracker は KVM を使って microVM を動かす VMM です(Firecracker の README)。KVM の API ドキュメントにある KVM_SET_USER_MEMORY_REGION の説明では、ゲストのメモリ領域には、VMM が指定したプロセス内のアドレス(userspace_addr)からのメモリが使われます。この領域のメモリが入れ替わるとゲストにも自動で反映され、例として "an mmap() that affects the region will be made visible immediately"(領域に影響する mmap() は、すぐにゲストから見えるようになる)と書かれています。

なお、どのデータを狙えるか、どうやって失敗を起こすかといった具体的な内容は、一次情報には書かれていません。

なぜ成立するのか

PR #6174 の説明とコミットメッセージによると、この問題は、スナップショットから復元した VM での置き換えの仕組みに、次の 3 つの要因が重なって起きていました。

前提:復元したゲストメモリは、匿名マッピングを被せて置き換える

スナップショットのドキュメントでは、File はページフォールトの処理をカーネルに任せる方式、Uffd は専用のユーザー空間のプロセスがページフォールトを処理する方式と説明されています。PR によると、File バックエンドで復元すると、ゲストメモリはメモリファイルを MAP_PRIVATE でマップした領域(file-backed)になります。

ゲストがメモリを返したとき、Firecracker はその範囲のメモリをホストに戻します。メモリホットプラグのドキュメントによると、匿名メモリ(ファイルに対応づいていないメモリ)なら madvise(MADV_DONTNEED) を使います。一方、ファイルをマップした領域では、ゼロページの匿名マッピングを mmap(MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE) で既存のマップの上に強制的に被せていた、と PR は説明しています。v1.16.1 のソースのコメントによると、ファイルをマップした領域では MADV_DONTNEED を使っても、次に読んだときにファイルに保存されている中身が見えてしまうためです。

要因 1:置き換えのマッピングに MAP_NORESERVE が付いていなかった

元のマッピングは MAP_NORESERVE 付きで作られていましたが、置き換えのマッピングには付いていませんでした。PR によると、MAP_NORESERVE がないと、置き換えのマッピングはオーバーコミットの計上の対象になり、ENOMEM で失敗することがありました。

オーバーコミットの計上は、カーネルがメモリの確保量を数え、vm.overcommit_memory の設定に応じて確保を断る仕組みです。カーネルのドキュメントによると、設定を変えていない場合のモード 0 では、明らかに大きすぎる確保だけを断ります。モード 2(strict)では確保量の合計を上限と比べ、MAP_NORESERVE は無視されます。

要因 2:6.12 より前のカーネルでは、MAP_FIXED が先に古いマッピングを外す

PR とコミットメッセージは、ホストの Linux カーネルが 6.9 未満の場合、MAP_FIXED は既存のマッピングを外してから計上を確認すると説明しています。そのため、置き換えが失敗すると、範囲はマップされていない状態で残ります。

ただし、カーネルのソース(mm/mmap.c の mmap_region。6.12 では内部の __mmap_region)を確かめると、6.11 までは既存のマッピングを外してから計上を確認する順序でした。この順序が変わったのは 6.12 です。6.12 では、外す対象を集めるだけで確定させないまま計上を確認し、失敗したら元のマッピングに戻します。この変更は、6.12 で入ったパッチシリーズ「Avoid MAP_FIXED gap exposure」によるものです。シリーズの 1 つのコミット 224c1c702c08 のメッセージにも、集める段階では何も確定させないので失敗したら元に戻せる、とあります。AWS の速報が緩和策の条件に挙げる「Linux 6.12 のオーバーコミット処理の変更」も、この変更と合います。PR の「6.9 未満」とは食い違うので、6.9〜6.11 のホストも、穴が残る側に入ると考えておくのが安全です。

6.12 以降のホストでも、置き換えがほかの理由で失敗する可能性までなくなるわけではありません。アドバイザリも対象条件にカーネルのバージョンを含めていないので、対象かどうかは「先に結論」の 3 条件で判断します。

要因 3:Firecracker は失敗しても止まらなかった

Firecracker はこの失敗を致命的なエラーとして扱っていませんでした。コミットメッセージによると、呼び出し元はエラーをログに残すだけで処理を続け、デバイスはその範囲を有効なゲストメモリとして扱い続けていました。アドバイザリでも、CWE として CWE-390(エラー状態を検出しても対処しない)と CWE-825(期限切れポインタの参照)が挙がっています。

図 2 は、前提と 3 つの要因が重なったときに、返された範囲がどう変わるかを示したものです。

アドレス空間の変化を示す自作の概念図。段階1、スナップショットから復元した直後は、ゲストメモリの領域全体がメモリファイルを MAP_PRIVATE でマップした領域で、MAP_NORESERVE 付きのため strict モード以外ではオーバーコミットの計上の対象外。段階2、ゲストがメモリを返すと、MAP_NORESERVE なしの置き換えの mmap(MAP_FIXED) が呼ばれ、6.12 より前のカーネルでは計上を確認する前に元のマッピングが外れる。段階3、計上の確認で ENOMEM になると置き換えは失敗し、範囲はマップされていない穴のまま残るが、Firecracker はエラーをログに残して動き続ける。段階4、Firecracker が後で確保したメモリが穴の位置に入ることがあり、ゲストの root からゲストメモリとして読み書きできる。

修正:MAP_NORESERVE を付け、失敗したらプロセスを止める

PR #6174 では、今回の脆弱性に対して次の 2 つの変更が入りました。

  • 置き換えの mmap に MAP_NORESERVE を付ける(x86_64 と aarch64 の seccomp フィルターの mmap のルールも、新しいフラグに対応させて更新)
  • 置き換えの mmap が失敗したら、範囲が既に外れていて元に戻せないので、プロセスを止める。コミットメッセージによると、引数は事前に検証されているので、正しい要求でこの失敗が起きることはない

同じ PR では、virtio-mem で unplug したメモリの保護が外れる問題も直っています。Firecracker は、すべて unplug したスロットを PROT_NONE にしてアクセスできないようにしています。しかし、置き換えのマッピングが PROT_READ | PROT_WRITE で作られていたため、この保護が外れていました。その結果、プロセスが動いている間ずっと、unplug したメモリを VMM 側から読み書きできる状態が残っていました。修正後は、すべて unplug したスロットは PROT_NONE、plug 済みのブロックを含むスロットは PROT_READ | PROT_WRITE と、スロットごとに最終的な保護で直接マップし直します。コミットメッセージによると、いったん読み書きできる状態にしてから保護をかけ直すと、その間だけ書き込める時間ができるためです。

ほかにも、plug 済みのブロックがない範囲への unplug の要求では解放しない変更(UNPLUG_ALL のたびに領域全体を解放していた無駄をなくすもの)と、結合テストが入っています。結合テストは、virtio-mem の plug と unplug を通して、ファイルをマップした領域でも unplug 後のスロットが PROT_NONE のままかを確かめます。コミットメッセージによると、virtio-mem の既存のユニットテストは匿名メモリを使っていたので、この分岐を通っていませんでした。

図 3 は、修正前後の違いをまとめたものです。上の 2 行が今回の脆弱性、3 行目が virtio-mem(v1.14.0 で追加)の別の修正です。

修正前後を比べた自作の図。修正前の v0.23.0〜v1.16.1 では、置き換えの mmap に MAP_NORESERVE がなく、オーバーコミットの計上で ENOMEM になりうる。失敗したときはログを残して動き続け、範囲は穴のまま残る。修正後の v1.16.2 と v1.17.0 では、MAP_NORESERVE 付きで元と同じフラグになり、計上が原因では失敗しない。失敗したときはプロセスを止め、Firecracker と VM が終了する。3 行目は virtio-mem の別の修正で、修正前はすべて unplug したスロットが PROT_READ と PROT_WRITE に戻って VMM から読み書きできる状態が残り、修正後は PROT_NONE のままになる。

自分の環境が影響を受けるか確かめる

アドバイザリの条件に沿って、次の順に確かめます。

  1. Firecracker のバージョン: firecracker --version で、v0.23.0〜v1.16.1 の範囲に入っているか
  2. 復元方法: オーケストレーターの設定やコードで、PUT /snapshot/load の mem_backend.backend_type に File を指定しているか。非推奨の mem_file_path フィールドで指定している古いクライアントも、API の実装では File バックエンドとして扱われます(v1.16.1 のソース)。Uffd なら対象外です
  3. デバイス: 復元した VM に balloon か virtio-mem があるか。GET /vm/config の応答の balloon と memory-hotplug で確かめられます(API 定義)。CHANGELOG によると、v1.0.0 以降はスナップショットから復元した VM でも元の VM の構成が返ります。balloon だけなら GET /balloon でも確かめられ、balloon がない場合は 400(Balloon device not configured.)が返ると API 定義に書かれています

守り方

v1.16.2 以降へ更新し、スナップショットを作り直す

v1.16.2 か v1.17.0 以降に更新します。フォークや派生コードを使っている場合も、同じ修正を取り込むよう AWS の速報とアドバイザリが求めています。

更新するときは、スナップショットの互換性に注意が要ります。v1.16.2 のリリースノートによると、このリリースではスナップショット形式のバージョンが上がっています(#6100 による変更で、今回の修正とは別です)。そのため、v1.16.2 で作ったスナップショットは v1.16.0 / v1.16.1 では読み込めず、v1.16.0 / v1.16.1 で作ったものも v1.16.2 では読み込めません。

v1.17.0 のリリースノートには、スナップショット形式の記載がありません。ただ、ソースの SNAPSHOT_VERSION は v1.16.1 が 10.0.0、v1.16.2 が 11.0.0、v1.17.0 が 12.0.0 で、MAJOR がすべて違います。v1.17.0 のソースでは、読み込むスナップショットの MAJOR が一致しないと InvalidFormatVersion のエラーで拒否します(snapshot/mod.rs)。そのため、v1.17.0 に上げる場合も、v1.16.x までに作ったスナップショットは作り直しが要ります。保存済みのスナップショットから VM を起動する運用では、更新の順番と作り直しを計画しておく必要があります。

すぐに更新できない場合

アドバイザリと AWS の速報は次の緩和策を挙げています。

  • Uffd バックエンドで復元します。その場合、解放されたページを扱うページフォールトハンドラーの要件を満たす必要があります。ページフォールトの処理のドキュメントでは、balloon で返された範囲のページはファイルから読まず、ゼロで埋める必要があると説明しています。virtio-mem を使う場合は、メモリホットプラグのドキュメントにあるとおり、hot-remove されたブロックの UFFD_EVENT_REMOVE イベントも処理する必要があります。
  • ヒューリスティックなオーバーコミット(vm.overcommit_memory=0)を使い、メモリの大きさに上限を設けます。アドバイザリでは、VM ごとのゲストメモリ(virtio-mem の領域を含む)を、ホストの RAM とスワップの合計以下に抑えるとしています。AWS の速報は、上限の対象を discard の最大サイズとし、この緩和策を Linux 6.12 のオーバーコミット処理の変更が入っていないカーネル向けとしています(6.12 の変更は「なぜ成立するのか」の要因 2 を参照)。
  • Firecracker のメモリ cgroup の OOM killer を有効のままにします。なぜこれが緩和策になるのかは、アドバイザリにも速報にも書かれていません。

2 つ目と 3 つ目の緩和策は失敗の起きやすさを下げるだけで、すべての失敗を防ぐものではない、とアドバイザリと速報の両方が書いています。

検知の手がかり

修正前のバージョンでも、置き換えの失敗はエラーとしてログに残っていました(コミットメッセージ)。v1.16.1 のソースでは、ファイルをマップした領域の置き換えに失敗すると discard_range: mmap failed というエラーが出ます(memory.rs)。呼び出し元のデバイスも、virtio-mem は virtio-mem: Failed to discard memory range、balloon は Error removing memory range というエラーを出します。balloon の free page hinting か free page reporting を有効にしている場合は、balloon hinting: failed to remove range と balloon: failed to remove range も出ます。Firecracker のログにこれらがあれば、置き換えが失敗した手がかりになります。文言はバージョンによって変わりうるので、使っているバージョンのソースで確かめる必要があります。また、ログからは、その範囲にメモリが入ってゲストから読み書きされたかまでは分かりません。

修正後のバージョンでは、置き換えに失敗するとプロセスが止まります。v1.16.2 のソースでは、止まるときのメッセージにも同じ discard_range: mmap failed が入ります。Firecracker プロセスの異常終了も手がかりの 1 つです(異常終了の原因はほかにもありえます)。

まとめ

  • CVE-2026-122192 の対象は、Firecracker v0.23.0〜v1.16.1 の VM のうち、File バックエンドでスナップショットから復元し、balloon か virtio-mem を持つものだけです。AWS によると、AWS のサービスは影響を受けません。
  • ゲストのメモリの実体は Firecracker プロセスのメモリなので、置き換えに失敗して開いた穴に Firecracker のメモリが入ると、ゲストから見えます。穴が開いて残った原因は、置き換えの mmap に MAP_NORESERVE がなかったこと、6.12 より前のカーネルでは MAP_FIXED が先に古いマッピングを外していたこと、そして失敗しても処理を止めなかったことの重なりです。
  • 修正版の v1.16.2 と v1.17.0 は 2026 年 9 月に出ていました。これらのバージョンに更新し、既存のスナップショットは新しいバージョンで作り直す必要があります。すぐに更新できない場合は、Uffd バックエンドでの復元が緩和策になります。

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事