何が起きたのか:設定不一致と拒否ハンドオフ透過によるKVキャッシュ露出の成立条件

Cloudflareは2026年10月26日、推論サービス「Workers AI」で、他テナントの推論データが後続の応答に混入する不具合が発生したと公表しました[6, 28]。影響が確認されたモデルは「@cf/zai-org/glm-5.3」で、Cloudflareの調査範囲では、ほかのWorkers AIモデルへの影響は確認されていません[1, 15, 20, 40, 54]。この事象は2026年8月27日のモデル公開時から発生していました。外部から報告を受けた後、9月9日14

UTCに対象モデルを非公開化し、同日19
UTCに修正版の提供を再開しました。2026年10月26日には、SGLang開発チームとの協調開示が行われました[6, 28]。影響が確認された応答は155件で、いずれもprefill KVキャッシュプール容量の681,280トークンを超える長文プロンプトに対するものでした[5, 15, 20, 38, 54]。

直接の契機は、モデルの設定値と物理メモリプール容量の乖離です。Workers AIでGLM-5.3を展開した際、設定上のコンテキスト長は1,048,576トークンでしたが、入力処理を担うprefill GPU側のKVキャッシュプール容量は681,280トークンでした[2, 13, 21, 27, 36, 54]。そのため、モデルの設定上限内に収まる長文プロンプトでも、prefill側のメモリプール容量を超えて拒否経路に入る場合がありました[13, 27, 36, 54]。

補足:PrefillとDecodeの分離構成(PD分離)とは

大規模言語モデルの推論において、入力プロンプトを一括処理する計算負荷の高い「Prefill」段階と、トークンを1つずつ逐次生成するメモリ負荷の高い「Decode」段階を、別々のGPUサーバ群に分けて実行するアーキテクチャです。

不具合が成立した経路は、単一ノード構成と分離型推論(PD分離)構成との挙動の違いにあります。prefill KVプールに収まらないプロンプトが投入されると、エンジン内部で要求が拒否状態としてマークされ、1トークンのプレースホルダー(stub)プロンプトに置き換えられました[4, 14, 22, 31, 37, 55]。単一ノード構成では、このプレースホルダーを処理した後、クライアントに通常のエラーを返して終了していました[31, 37]。一方、prefillノードとdecodeノードを分離し、Mooncake経由のRDMAでKVキャッシュを転送する構成では、ディスアグリゲーション側の受け入れキュー(admission queue)が要求の拒否状態ではなく finished() の状態を確認していました。その結果、要求を稼働中(live)と判定し、通過させていました[2, 4, 14, 22, 31]。

この判定透過により、ブートストラップ処理、GPUページの予約、1トークンのプレースホルダーに対するKVキャッシュ生成が実行され、decodeノードへ渡されました[4, 31]。Workers AIの推論基盤では、要求の終了時にGPUメモリページをゼロクリアせず、次の要求が新しいデータで上書きする設計を採用しています[3, 12, 22, 32, 55]。そのため、decodeノードは1トークン分だけ上書きされた予約ページを受け取り、今回の要求が書き込んでいない過去の他要求のデータが残るページを参照しながら応答を生成しました[4, 14, 22, 31, 37, 55]。

以上のように、プール容量を超えた要求が1トークンに置き換えられた後、PDハンドオフを通過し、デコード側で未上書きの過去ページを参照する経路が確認されています[2, 4, 14, 22, 31, 37]。

Cloudflareによると、本件の発生機構は、中断された要求の非同期RDMA書き込みが遅れてKVスロットに到着する競合状態(race)とは異なります。拒否された要求がハンドオフキューを毎回通過する制御経路の不具合でした[9, 24]。また、Cloudflareはモデル公開前からプール容量とコンテキスト長の不一致を把握していました。しかし、長文テストではHTTPステータスが成功し、ストリーミング中にエラーが出ないことだけを確認し、生成内容の整合性は検証していませんでした。そのため、拒否処理は安全に完了すると判断していました[30]。

後輩容量オーバーで拒否されたリクエストが、なぜそのまま生成処理まで進んでしまったんですか?

先輩単一ノードならエラーで終わっていた処理が、PrefillとDecodeを分けたキュー制御で稼働中と誤認されたんだ。エラーを抱えたまま、ハンドオフの関門を通り抜けてしまったんだよ。

後輩メモリを初期化せずに上書き再利用する前提があったから、1トークン分しか書かれていないメモリの残りをそのまま読んでしまったんですね。

なぜ今起きたのか:分離型推論の高速化・メモリ再利用設計とテナント分離の境界

大規模言語モデルの推論では、KVキャッシュに各アテンション層が過去のトークンについて計算したKeyとValueのテンソルを保持します。トークン生成のたびに新しいトークン分だけを追加で計算するため、過去分の再計算を省けます[10]。推論処理は、入力全体を一括処理するため計算負荷が高い「Prefill」段階と、トークンを1つずつ生成するためメモリ帯域の負荷が高い「Decode」段階に分かれます[33]。Cloudflareは同一エンジン内での処理干渉を避け、GPUの利用効率を高めるため、両段階を別々のサーバ群に分離していました。prefillノードが生成したKVキャッシュは、RDMAでdecodeノードへ転送されます[2, 12, 33]。

GPU上のKVキャッシュ領域は、固定サイズのページを共有するプールとして管理され、要求の受け入れ時に割り当てられ、推論完了時に解放されます[12, 32]。解放したページを都度ゼロクリアせず、次の要求への割り当て時に上書きする設計は、GPU推論の処理効率と性能を維持する一般的な手法です[3, 12, 32, 55]。

補足:RadixAttentionとcache_saltとは

SGLangが採用するKVキャッシュ管理手法です。過去の推論で計算したプロンプトや生成結果のトークン列を木構造(Radix Tree)で保持し、前方一致する要求間でキャッシュを再利用します。cache_saltは、このツリー照合時にテナントやユーザごとに独立した名前空間を割り当てるための識別子です。

推論キャッシュには、論理的な照合による再利用と、物理メモリページの再割り当てという、異なる2つの境界があります。SGLangのRadixAttentionは、完了した要求のプロンプトと生成結果のKVキャッシュを基数木(radix tree)に保持し、共通のプロンプトプレフィックスを持つ後続要求で再利用する論理キャッシュ機構です。キャッシュはLRU方式で退避されます[11]。in-processのRadixKeyでは cache_salt を指定すると、異なるsalt間でプレフィックスが一致しないようにする名前空間が提供されます[16, 23]。マルチテナント環境では、認証済みテナントごとに安定した cache_salt を設定することで、論理的な照合を分離できます。cache_saltはRadixKeyの照合名前空間を分けます。物理ページの所有権管理や拒否要求のハンドオフ制御への作用は、公開資料からは確認できません[17, 23, 34, 60]。今回の露出は、プレフィックスキャッシュのヒットによって他者のデータが参照されたものではありません。要求終了後に解放され、次の要求に再割り当てされた物理GPUページに残っていたデータが、制御不備によって露出しました[12, 23, 55]。

分離型推論の制御に関する脆弱性は、GLM-5.3の公開前の2026年7月6日にも、SGLangのIssue #30233で報告されていました[29]。そこでは、モデルのコンテキスト上限に達していない場合でも、エンジンの入力上限を超えた要求がアボート後にキューへ入り、不完全なKVキャッシュのままデコードを開始する障害経路が指摘されていました[29]。

後輩キャッシュのキーやテナント識別子を設定していれば、こうしたデータの混入は防げるものだと思っていました。

先輩そこが運用上の重要な境界線だね。識別子が分けるのはプレフィックス照合という論理的な名前空間であって、GPUの物理メモリ割り当てやプロセス間の受け渡し制御そのものではないんだ。

後輩論理的なキャッシュ分離が機能していても、物理ページの寿命管理とキューの制御に穴があると防げないわけですね。

何が変わるのか:早期リタイア修正・プール拡張と上流検証の到達点と未検証範囲

事案を受け、Cloudflareは直ちに是正措置を実施しました。GLM-5.3の設定をGLM-5.2相当に戻してGPUメモリの余力を確保し、prefill KVプール容量を約204万(約2.04 million)トークンに拡大しました[7, 18, 25, 35, 43, 44]。これにより、有効なコンテキスト内のプロンプトがプール超過によって拒否経路に入る事態を解消しました[44]。さらに、拒否保留となった要求について、元のエラーを保持したまま、ブートストラップ、メモリ割り当て、KV転送より前にエラー終了させる緊急エンジンパッチを適用しました[7, 18, 25, 35, 43, 45]。Cloudflare Product Securityは、修正前に独立した2つのアカウントを使って漏えいを再現していました[47]。復旧時には、研究者の再現スクリプトと自社のカナリーマーカーテスト(canary-marker tests)を使って、修正の効果を確認しました[7, 18, 25, 35, 43, 47]。

上流のSGLangプロジェクトでも、複数段階のパッチが統合されました。まずPR #36988では、最後のインフライトprefill結果を安全に処理できる時点で中断要求をリタイアさせ、チャンク送信と再キューイングを停止し、メタデータバッファ、HiCache、KVブロック、Mamba状態を解放する処理が導入されました(SGLang v0.5.20)[8, 19, 46, 66]。続くPR #38935では、prefillとdecode双方の受け入れ段階に、intake拒否要求をハンドオフ前に停止するガードが追加されました(SGLang v0.5.21)[8, 19, 26, 45, 66]。ただし、PR #38935の記録では、#36988適用後のこの修正は正しさの修正ではなく、無駄な処理や遅延を減らすものと位置付けられています[26, 66]。Cloudflareは推論基盤の運用者に、SGLang v0.5.22以降への更新を推奨しています[8, 19, 26, 65]。

修正前の構成と復旧後の設定値、上流SGLangでの検証実績および未検証範囲を以下にまとめます[2, 7, 46, 48, 49, 51, 67, 68]。

項目修正前の状態・検証実績修正後・上流PRでの対応残存制約・未検証の範囲
Prefill KVプール容量681,280トークン(設定コンテキスト長1,048,576トークンと乖離)[2]約2,040,000トークン(約2.04Mトークンへ拡大)[7]メモリ設定と実容量の運用監視が引き続き必要[50]
中断要求のリタイア制御(PR #36988)中断された要求が残りリソース解放や再キューイングで競合prefill完了時に要求を安全にリタイアし各種バッファを解放(v0.5.20)[46]H200選択テスト39件通過確認にとどまる[46]
受け入れ早期拒否ガード(PR #38935)finished()判定の透過により拒否要求がハンドオフを通過prefill/decode双方の受け入れ関門で拒否要求を早期停止(v0.5.21)[45]非対称拒否時にdecode側で300秒の待機タイムアウトが発生[49, 67]
PR #38935の回帰検証規模拒否要求がハンドオフされ過去ページを参照拒否要求のハンドオフコミット0件、登録ユニットテスト1,011件通過[48, 49]フルモデルでの推論精度・性能ベンチマークは未実施[49, 67]
整合性チェックサム検証(PR #39500)転送データの完全性照合機構なしprefill生成のAdler-32チェックサムをdecodeバッチ投入前に検証[51]フルモデル精度・速度評価未実施、デフォルトは無効[51, 68]

上流修正の回帰検証は、2基のH200 GPUとMooncakeを使い、スケジューラの最大入力長を1,018トークン、コンテキスト長を4,096トークンに設定した環境で、1,500トークンのプロンプトを投入して実施されました[48]。この検証では、拒否要求のハンドオフコミットが0件となり、登録ユニットテスト1,011件(4件スキップ)の通過が確認されました[49, 67]。

運用上、未検証の範囲や制約がいくつか残っています。PR #38935の検証では、prefillノード側だけが拒否する非対称な条件で、decodeノード側が300秒の転送タイムアウトまで待機し続ける制約が記録されています[49, 67]。また、PR #38935とPR #39500のいずれでも、フルモデルでの推論精度試験や処理速度のベンチマークは実施されていません[49, 51, 67, 68]。公開資料からは、PR #38704の転送長照合が上流に統合されたことを確認できません[52]。Cloudflareが実施したカナリーマーカーテストについても、マーカーの形式や反復回数、再開後の定量的な監視データは公表されていません。公開情報だけでは、第三者が同等の条件・精度で再現できるか判断できません[53]。

後輩パッチが当たってユニットテストも通過しているなら、修正としては完了したと見てよいのでしょうか?

先輩拒否要求が透過する致命的な経路は塞がれたけれど、検証の前提には注意が必要だね。上流の検証ではユニットテストは通っているものの、モデル全体の推論精度や速度性能の測定までは行われていないんだ。

後輩異常系で300秒待たされるタイムアウトの制約も残っているとなると、実運用での挙動監視が欠かせないわけですね。

誰に影響するのか:155件の影響応答の評価と主要AI各社の分離設計・実務対応

Cloudflareが報告した影響応答は155件です。発生時期と件数は通常の長文利用傾向と整合しており、意図的な悪用の証拠は見つかっていません[38, 41]。仕組み上、上書きされなかった物理ページに残る他利用者のプロンプトや部分生成テキストが、後続の応答に現れる可能性があります。ただし、完全なプロンプトや会話全体が漏えいした事実は、公開資料では確認されていません[39]。Cloudflareは影響を受けた顧客を「少数」と説明していますが、155件の応答数からユニーク顧客数や顧客ごとの件数、漏えいしたテキストの内容・規模は特定できません。公表情報だけでは、個別アカウントへの影響度合いを判断できません[42]。

推論基盤におけるキャッシュやリソースの分離境界は、主要AI事業者ごとに異なります。各サービスのキャッシュ分離単位と運用上の留意点を以下に示します[56, 57, 58, 59, 61, 62, 63]。

サービス・提供元キャッシュ分離境界・リソース割り当て単位保持仕様・課金条件隔離・運用の留意点
Cloudflare Workers AI同一モデルインスタンス単位(セッションアフィニティ推奨)[56]前方一致テンソルの再利用によるTTFT短縮とTPS向上[56]物理ページの再割り当てとハンドオフキューの制御が分離の前提[12, 55]
Anthropic Claudeワークスペース単位(API、AWS、Foundry)/組織単位(Bedrock、GCP)[57]標準TTL 5分(書込み1.25倍)/延長TTL 1時間(書込み2倍)、読取り0.1倍[61]接続先クラウドによって分離境界が変化、長TTLは書込みコストが増加[57, 61]
Google Cloud (Context Cache)プロジェクト番号単位で分離(プロジェクト間共有なし)[58]プロジェクト内のコンテキストキャッシュとして保持[58]マルチテナントプロキシやエージェントではエンドユーザー権限に合わせた設計が必要[58]
OpenAI (Prompt Caching)組織(organization)単位およびリージョン処理境界単位[59]自動キャッシュ、prompt_cache_key によるルーティング誘導[59]キー指定はルーティング用であり特定マシンへの固定やヒットは非保証[59]
Microsoft FoundryStandard(顧客間共有)/Provisioned Throughput(固定処理容量)[62]PTU単位の時間課金(リクエスト有無を問わず発生)[62]スループット確保の仕様であり、専用GPUや物理KVキャッシュの分離を保証するものではない[62, 63]

各社のプロンプトキャッシュ機能は、組織やワークスペースなどの論理境界に基づいて制御されています[57, 58, 59]。専用リソースの仕様を確認する際は、その範囲に注意が必要です。たとえば、Microsoft FoundryのProvisioned Throughputは顧客専用の処理容量を確保しますが、これはスループット(処理能力)の固定割り当てを意味します。この仕様だけから、専用GPUや専用ホスト、物理KVキャッシュの完全な分離まで保証されるとは判断できません[62, 63]。厳格なテナント分離を求める利用者は、処理能力枠に加えて、物理的な実行境界やキャッシュのライフサイクル仕様も個別に確認する必要があります[63]。

以上を踏まえ、Workers AIの利用者とSGLang運用者には、以下の対応が適切です。

  1. 利用者の点検と保全対応:
    • 影響期間中(2026年8月27日〜9月9日)にWorkers AIの対象モデル(@cf/zai-org/glm-5.3)を呼び出した履歴があるか、アクセスログを照合します[6, 40, 64]。
    • プール閾値の681,280トークンを超えるプロンプトを投入していた場合は、HTTPステータスが200でも応答本文を点検します[5, 38, 64]。
    • 他者由来と疑われる不自然なテキストが見つかった場合は、リクエストとレスポンスの記録を保全したうえで、Cloudflareにアカウント単位の調査を依頼します[64]。プロンプトに外部へ露出してはならない認証情報などが含まれていた場合は、一般的なインシデント対応手順に従って資格情報をローテーションします[64]。
  2. 推論基盤運用者の防御対応:
    • SGLangを分離型(PD)構成で運用している場合は、各ノードの実KVプール容量と設定コンテキスト長を実行中のエンジンから直接比較し、プール容量が下回る場合はコンテキスト設定を引き下げます[50, 65]。
    • エンジンを早期拒否パッチを含むv0.5.22以降へ速やかに更新します[8, 19, 26, 65]。
    • KV解放の遅延処理(deferred KV release)や転送バッチ上限を設定し、Adler-32チェックサムやカナリー検査が対象モデルのアーキテクチャで有効に機能しているか、ログで確認します[50, 65]。

後輩クラウド事業者のプロビジョンド枠や専用枠を契約していれば、こうした混入のリスクは避けられると考えてよいのでしょうか?

先輩確保されているのが処理能力の枠(スループット)なのか、物理的なGPUやメモリの完全占有なのかを見極める必要があるね。容量保証の仕様から、直ちにメモリの物理隔離まで読み取ることはできないんだ。

後輩処理枠の確保とデータの物理境界を分けて考える必要があるわけですね。まずはログの確認と設定の照合から着手します。