Cloudflare Workers AIにおけるクロスリクエストKVキャッシュ露出:GLM-5.3、SGLang、およびv0.5.22での修正
2026年8月27日から9月9日までの間、GLM-5.3 Workers AIモデル(@cf/zai-org/glm-5.3)を使用していた少数の顧客が、同じモデルへの他の顧客のリクエストからのデータを含む応答を受け取りました。原因は2つの重なる問題でした:モデルの宣伝された1,048,576トークンのコンテキストウィンドウと、681,280トークンしか保持できないプリフィルKVキャッシュプールとの間のCloudflareの設定不一致、および、過長なリクエストを拒否する代わりに非集約プリフィルからデコードへのハンドオフを通過させてしまうSGLangのバグです。合計で155件の応答が影響を受け、すべて681,280トークンより長いプロンプトに対するものでした。Cloudflareはこの問題を修正し、2026年9月9日にモデルを復元し、Workers AIでホストされている他のモデルには影響がないことを確認しました。
何が起こったか
Cloudflareは2026年9月8日、Knosticのセキュリティ研究者Gadi Evron氏から、Workers AI上の@cf/zai-org/glm-5.3に送信された非常に長いプロンプトが、他のユーザーのセッションに属するテキストを返すことがあるという報告をHackerOneプログラムを通じて受け取りました。Cloudflareは翌朝問題を再現し、セキュリティインシデントを宣言し、宣言から40分以内にモデルをオフラインにしました。その後、修正された設定とパッチ適用済みの推論エンジンを展開し、同日中にサービスを復元しました。
Cloudflareは、自社のテストと研究者のテストを除外した後、影響を受けたリクエストは、繰り返しの悪用ではなく、通常の長文コンテキスト使用とタイミングと量が一致しており、意図的な悪用の証拠は見つからなかったと述べています。
GLMは、エージェント型コーディングワークフロー向けにZ.aiが構築したテキスト生成モデルであり、Workers AIで利用可能なモデルの1つです。この露出は、2026年8月27日にWorkers AIでモデルがローンチされた時点から存在していました。
問題1:KVプールがコンテキストウィンドウより小さかった
CloudflareはGLM 5.3を1,048,576トークンのコンテキストウィンドウで展開しましたが、プリフィルGPU上のKVキャッシュプールには681,280トークン分のスペースしかありませんでした。Cloudflareはローンチ前にこの不一致を認識していました。
SGLangは、モデルウェイトのロード後に残るGPUメモリからKVキャッシュプールのサイズを起動時に決定します。プールサイズは設定値ではなく、モデルの宣伝されたコンテキスト長は別の設定値です。両者が一致することを強制するものはなく、Cloudflareの投稿では、その一致を確認するのはオペレーターの仕事であると述べられています。選択されたKVプールサイズはSGLangの起動ログに出力され、メトリックとして公開されます。
Cloudflareのローンチ前テストでは、ベンチマーク中に成功したHTTPステータスコードとエラーフレームのないストリーミングボディが観察されたため、同社はエンジンがKVキャッシュプールと要求されたコンテキストウィンドウの差を適切に処理していると想定しました。この投稿はこれを重要なテストギャップとして特定しています:CloudflareはHTTP応答コードとエンジンのパフォーマンスを長いコンテキストで検証しましたが、モデルの出力と一貫性を検証しませんでした。
問題2:非集約モードでのSGLang拒否パス
非集約プリフィル/デコードモードでは、KVプールが保持できるよりも長いプロンプトは内部的に拒否としてマークされ、1トークンのプレースホルダーに置き換えられましたが、リクエストは中止されませんでした。代わりに、プリフィルはプレースホルダープロンプトのKVキャッシュを生成し、それをデコードノードに転送しました。デコードノードは「転送完了」メッセージを受信し、プリフィルが以前のリクエストからのKVキャッシュをこのリクエストの完全なプロンプトで上書きしたと想定しましたが、実際には以前の状態の大部分がそのまま残っていました。デコードはその後、以前のリクエストからのデータを保持している古いキャッシュページにアテンションしながら応答を生成しました。
シングルノード展開では拒否パスは正しく機能しました:プレースホルダーが実行され、バッチが終了し、クライアントはエラーを受け取りました。非集約モードでは、プリフィルとデコードの両方のアドミッションキューは、リクエストが拒否されたかどうかではなく、終了したかどうかをチェックしました。拒否されたリクエストは、finished_reasonではなくto_finishに拒否を保存したため、finished()は依然としてFalseを返し、リクエストは自分自身をライブとして報告しました。
Cloudflareが説明するシーケンスは次のとおりです:
- ブートストラップ。 ルーターはデコードノードにプロンプト長(Cloudflareの図では8ページ)を伝え、デコードは8ページのKVを予約しました。
- プリフィルアドミッション。 プリフィルアドミッションキューは
req.finished()がFalseであるすべてのリクエストを許可したため、拒否されたリクエストが許可されました。 - プリフィルフォワードパス。 拒否によりプロンプトはすでに1トークンのプレースホルダーに置き換えられていたため、プリフィルはその1トークンに対して簡単なフォワードパスを実行しました。
- KV転送。 プリフィルは1ページのKVをデコードに送信し、転送が完了したことを通知しました。
- デコードアドミッション。 デコードのアドミッションキューは同じ
finished()チェックを使用し、ハンドオフを受け入れました。 - デコードアテンション。 デコードは生成を開始し、アテンションは予約された8ページすべてを読み取りました。ページP0だけがこのリクエストによって書き込まれました。ページP1からP7には、以前のリクエストが残したものがまだ保持されていました。
- 応答。 クライアントはHTTP 400の代わりにHTTP 200を受け取りました。
prompt_tokensは長いプロンプトと一致し、応答は一貫していましたが、8ページのプリフィルがかかるはずの時間よりも速く到着し、他のユーザーのKVページからのコンテンツが含まれていました。
しきい値を超えるすべてのリクエストはこのように動作しました。このバグは、2026年7月6日に別のユーザーによってSGLangにissue #30233として報告されており、Cloudflareはこのインシデントの調査中にそれに気付きました。Cloudflareは、v0.5.10以降、これらのタイプの問題をSGLangにフラグ付けしていたと述べています。
影響を受けたのはGLM 5.3のみでした。Workers AI上の他のすべての非集約展開では、プリフィルとデコードの両方でKVプールがコンテキスト長よりも大きいため、リクエストが拒否パスに到達することはありませんでした。
修正:設定の修正とエンジンの修正
Cloudflareは公開アクセスを復元する前に、2つの独立した修正を適用しました。
プールが完全なコンテキストを保持できるようにする。 CloudflareはGLM 5.3の設定をGLM 5.2の設定に合わせて調整し、プリフィルKVプールが約204万トークンを保持できるようにGPUメモリを解放しました。これは1,048,576トークンのコンテキスト長の約2倍です。コンテキストウィンドウに収まるプロンプトは常にプールにも収まるため、有効なリクエストに対して拒否パスに到達することはありません。
拒否パスを安全にする。 Cloudflareは、プールサイズだけに依存するのは脆弱であると主張しています。量子化、テンソル並列性、CUDAグラフサイズ、または将来のモデルバージョンの変更によりプールサイズが変わり、見逃しやすいためです。また、他の状況でプリフィルスケジューラがリクエストを拒否し、同じバグを引き起こす可能性があるためです。Cloudflareは、保留中の拒否を運ぶリクエストが、ブートストラップ、割り当て、またはKV転送が発生する前に、元のエラーとともに非集約アドミッションキューでリタイアされるようにエンジンをパッチしました。これを上流に#38704として提案し、デコードノードが転送されたトークン数が予約した数と一致することを検証するようにもしました。
Cloudflareは、研究者の再現スクリプトと自社のカナリアマーカーテストの両方に対して両方の変更を検証してから、モデルを復元しました。
インシデントのタイムライン
すべての時刻はUTCで、Cloudflareが公開したものです。
| いつ | 何が |
|---|---|
| 2026年8月27日 | GLM 5.3がWorkers AIでローンチされ、プリフィルKVプールがコンテキストウィンドウより小さかった。露出はローンチ時から存在。 |
| 2026年9月8日 – 23:11 | HackerOne経由で報告を受信。 |
| 2026年9月9日 – 08:00 | CloudflareがHackerOneレポートの検証を開始。 |
| 2026年9月9日 – 13:22 | セキュリティインシデントが宣言される。製品セキュリティが25分以内に2つの独立したアカウントを使用してリークを再現。 |
| 2026年9月9日 – 14:00 | GLM 5.3がプライベートに設定される。リクエストはHTTP 400を返す。ステータスページが更新される。 |
| 2026年9月9日 – 14:35 | 根本原因がプリフィルプールがコンテキスト長より小さいことと特定される。より大きなプールを持つカナリアプリフィルノードが影響を受けないことを確認。 |
| 2026年9月9日 – 14:52 | すべてのプリフィルノードが修正された設定にロールアウトされる。 |
| 2026年9月9日 – 19:04 | 修正された設定とCloudflare作成のエンジンパッチを使用して完全な展開が再構築される。研究者の再現スクリプトに対して検証される。 |
| 2026年9月9日 – 19:40 | モデルが公開に復元される。 |
| 2026年9月10日 – 23:30 | SGLangメンテナー(RadixArk CTOとコアエンジニア)との会議で上流修正に合意。 |
| 2026年9月11日~19日 | 追加のハードニングがすべての本番SGLang展開にロールアウトされる。 |
| 2026年9月16日~10月12日 | 上流SGLangがアドミッション修正、転送ごとのチェックサム、アボートACKの保持をマージし、遅延KV解放をデフォルトで有効にする。 |
| 2026年10月2日 | SGLang v0.5.21がリリースされる。 |
| 2026年10月19日 | SGLang v0.5.22がリリースされる。 |
| 2026年10月26日 | 研究者およびRadixArkとの協調公開。 |
上流SGLang修正
この問題のパッチはSGLang v0.5.20でリリースされ、v0.5.21とv0.5.22でフォローアップが行われました。Cloudflareは、すべてのオペレーターがSGLang依存関係をv0.5.22以降に更新することを推奨しています。以下の変更がSGLang mainに取り込まれました。すべてがv0.5.22に含まれているわけではありません。
| カテゴリ | プルリクエスト | リリース | 内容 |
|---|---|---|---|
| インシデント | #36988 | v0.5.20 | 中止されたプリフィルリクエストを、その結果がデコードに到達する前にリタイアさせる。これにより、このインシデントでの露出が止まる。 |
| インシデント | #38935 | v0.5.21 | ブートストラップ、割り当て、またはKV転送の前に、アドミッションでこれらのリクエストを拒否する。Cloudflareのドラフトパッチ#38704のアプローチに従う。 |
| レース | #39461, #41612 | v0.5.21, v0.5.22 | sgl-routerがクライアント切断またはプリフィル失敗時にリクエストの両半分をキャンセルする。 |
| レース | #37454, #41023 | v0.5.20, v0.5.21 | 遅延KV解放をサポートするバックエンドでのみ有効にし、その後デフォルトで有効にする。 |
| レース | #40645, #41402 | v0.5.21 | ドレイン確認が失われるケースを修正し、タイムアウトを待つのではなく確認時に解放が行われるようにする。 |
| レース | #41404 | v0.5.21 | デコード開始の中止だけでなく、すべての転送失敗でページを保持する。 |
| レース | #41618 | v0.5.21 | 中止通知パスのリグレッションを修正。 |
| レース | #43719 | v0.5.22 | Mooncake送信の失敗が、その書き込みをドレイン済みとして報告しないようにする。 |
| レース | #37077 | v0.5.22 | 中止メッセージをリクエストごとにタグ付けし、再利用されたIDが古い確認を消費できないようにする。MORIサポートを追加。 |
| レース | #36612 | v0.5.20 | プリフィルからデコードへの失敗通知をMooncake、NIXL、MORI間で共有する。 |
| レース | #32758 | v0.5.20 | Mooncake転送バッチサイズのオプションの上限。1回のタイムアウト呼び出しで無制限の数の書き込みが進行中になるのを防ぐ。 |
| ライフタイム | #30899 | v0.5.21 | デコードは、リクエストを撤回してページを解放する前に、進行中のホストメモリへのオフロードを待つ。Cloudflareによる作成。 |
| ライフタイム | #43562 | v0.5.22 | ホストメモリキャッシュの復元が失敗した場合に、テンソル並列ランクの一致を維持する。 |
| 可観測性 | #39500 | v0.5.21 | --disaggregation-enable-kv-checksumを追加:デコードは生成前に転送されたKVのチェックサムを検証する。クリーンアップは#39729でマージされ、リリース待ち。 |
| 可観測性 | #38596, #40642 | v0.5.20, v0.5.21 | SGLangのKVカナリアの修正。潜在的な破損を検出してログに記録する。 |
| 可観測性 | #41128 | v0.5.22 | 遅延解放のメトリック:数、期間、各保持がドレイン確認またはタイムアウトで終了したかどうか。 |
公開時点でまだ進行中:#39729(--disaggregation-enable-kv-checksumのクリーンアップ、マージ済みでリリース待ち)、解放時のKVページのゼロ化(ドラフト提案#46435)、プリフィルからデコードへのハンドオフからすべてのデコードステップへのKVチェックサム検証の拡張、およびより広範なKVカナリアとKVチェックサムのモデルおよび機能サポート。
Cloudflareが展開した追加のハードニング
Cloudflareはこのバグを、数か月間取り組んできたKVキャッシュメモリ安全性バグのファミリーの1つとして分類しています。各ケースで、ページが解放、再利用、または読み取りされる一方で、他の何かがまだそれに対するクレームを持っています。Cloudflareは2つの一般的な原因を特定しています。
1つ目は、単一ノード内のページライフタイムです。デコードノード上の複数のタスクが同じページを同時に保持できます:トークン生成、ホストメモリへのオフロード、KV転送レシーバー、および中止およびエラーパスです。1つがページを解放する一方で別のタスクがまだ使用している場合、新しいリクエストにそのページがまだ読み取りまたは書き込み中に与えられる可能性があります。以前のデコードオフロード(#24226、#30899)と文法エラー時の二重解放(#30937)の修正はこの種のものでした。
2つ目は、ノード間のレースです。プリフィルがRDMAを介してKVキャッシュをデコードに書き込んでいる間にリクエストが中止されると、すでに送信された書き込みを呼び戻すことはできません。デコードがそれらのページをすぐに解放すると、それらのページは別のリクエストに与えられる可能性があり、遅延書き込みがそのリクエストのKVキャッシュに着地します。Cloudflareはこのレースに対する修正案(#31357、#38961)と再現スクリプト(#32564)を提供しました。Cloudflareは、このインシデントのバグは異なっていたと述べています:拒否されたリクエストはタイミングの偶然ではなく毎回ハンドオフを通過しましたが、デコードがリクエストが書き込んだことのないページを読み取るという同じ結果をもたらしました。
インシデント以来、Cloudflareは以下を行ってきました:
- 遅延解放をプリフィルのソースページにも拡張し、中止だけでなくすべての失敗タイプをカバーするようにしました。上流に#38961として提案され、#40645、#41402、#41404に取って代わられました。
- 境界のあるMooncake転送バッチ(#32758、SGLangコミュニティから)を展開し、単一の転送呼び出しが解放タイムアウトを超えないようにしました。
- ロードバランサーを変更し、クライアント切断が転送中のリクエストを中止しないようにしました。プリフィル接続はプリフィルが終了するまで保持し、デコード接続は最初のトークンまで保持してから両方を閉じます。
- すべての非集約展開変更にチェックを追加しました:KVプールサイズをコンテキスト長と比較、両方のロールで遅延解放、解放タイムアウト内の転送バッチ境界、および必要なすべてのパッチが適用されていること。
Cloudflareが他のSGLangオペレーターに推奨すること
Cloudflareの非集約プリフィル/デコードモードでSGLangを実行しているすべての人へのガイダンスは次のとおりです:
- KVプールを測定し、コンテキスト長と比較してください。すべてのプリフィルおよびデコードノードで、エンジンの起動ログから、ウェイト、量子化、並列性、またはメモリフラクションの変更のたびに。プールが小さい場合は、コンテキスト長を下げて収まるようにしてください。プロキシに制限を強制させることに依存せず、エンジンが収まらないものを拒否すると想定しないでください。
- #36988と#38935(v0.5.22以降)を含むビルドにアップグレードしてください。これにより、プールサイズに関係なく、過長なリクエストがハンドオフ前に拒否されます。
- 両方のロールで遅延KV解放を有効にして実行してください(#41023以降デフォルト)、およびMooncake転送バッチを制限して、単一のタイムアウト呼び出しが無制限の書き込みのテールを残さないようにしてください。
- モデルがサポートする場所で破損検出を有効にしてください。
--disaggregation-enable-kv-checksumと--kv-canaryはまだすべてのモデルアーキテクチャをカバーしていません。ログを確認して、リクエストが検証されていることを確認してください。CloudflareはSGLangチームと協力して、GLM 5.3、GLM 5.3 Flash、DeepSeek V4を含むより多くのアーキテクチャに両方のチェックを拡張しています。 - プロキシが不要な中止を生成していないことを確認してください。 ロードバランサーを構成して、リクエストが最初のトークンまで進行できるようにし、クロスノード調整を必要とする中止をトリガーしないようにしてください。
長期的には:構築による整合性と形式検証
Cloudflareは、設定チェックとライフサイクル修正によりページの誤用の方法を減らすが、誤用を不可能にするわけではないと主張しています。永続的な答えは、エンジンが読み取り時にリクエストが所有していないページにアテンションしようとしていることを検出し、他の誰かのデータを返すのではなく、その1つのリクエストを失敗させることです。
Cloudflareは夏の間にこのプロトタイプを構築し、2026年8月に説明しました。すべての物理KVページには、ページが再割り当てされるときに変更されるタグが付いており、各リクエストは期待するタグを記録し、デコードがキャッシュを読み取る前にマッピングがチェックされます。Cloudflareのテストでは、スループットとテールレイテンシで1%未満のコストでした。Cloudflareは8月中旬にGLM 5.2でテストしましたが、信頼性の問題によりすぐに無効にしました。プロトタイプは特定のアテンションバックエンドとモデルアーキテクチャにも結び付けられていたため、一般的な修正にはなりませんでした。CloudflareはSGLangチームと協力して、すべてのモデルで機能する本番バージョンをエンジンに組み込んでいると述べています。#39500の転送ごとのチェックサムは、プリフィルからデコードへのハンドオフを検証するため、最初のステップです。ページレベルのタグは、転送後も長い間読み取りを含むすべての読み取りをチェックするため、さらに進んでいます。
Cloudflareはまた、このバグを分散システムの一貫性の失敗として位置付けています:RDMAはデータをあるノードのメモリから別のノードのメモリに直接コピーし、2つのマシンはデータがどこに行くか、そのサイズ、いつ終了するかについて同意する必要があります。Cloudflareは既存の形式検証作業(権威DNSでの競合のないIP選択のためのTopaz、および後に修正された一貫性バグを示したMeerkatのQuePaxa実装のモデル)を指摘し、現在その経験をKVキャッシュ転送エンジンに向けていると述べています。計画は、RDMAベースのKVキャッシュ転送プロトコルの安全性をモデル化して証明し、Mooncakeを使用して本番SGLang分散KVキャッシュシステムをモデル化し、保証したい安全性プロパティからの逸脱を追跡して修正することです。
Hacker Newsディスカッションが追加したもの
Hacker Newsスレッドで最も繰り返された批判は、タイトルがバグをモデルに過度に帰属させているというものでした。コメンターのjacquesm氏は次のように書いています:
これはGLM-5.3とはあまり関係ありません。これは、ユーザー間でモデルを共有し、簿記を適切に行わないときに起こることです。実際のモデルは関係ありません。これは、ユーザーセッション間に十分な分離がなく、メモリが重なる可能性があることを示しています。これはSGLANGとCloudflareのバグであり、Z.AIモデルのバグではありません。彼らに何ができたのかわかりません。Cloudflareは、タイトルで他の(私の意見では無実の)当事者を指名するのではなく、責任を負うべきです。
armcat氏は代替フレーミング「共有LLM推論におけるクロステナントKVキャッシュ露出:分析と修正」を提案し、GLM-5.3ユーザーとして最初は懸念の原因があると思ったが「そうではないことがわかった」と付け加えました。sauravadhk氏とadg001氏も、タイトルからモデル名を削除する同様の提案をしました。
数人のコメンターが、Cloudflare自身が開示したテストギャップに焦点を当てました。ComputerGuru氏は次のように書いています:
確かに「エラーを期待したが成功応答を得た」ことは、先に進むのではなく調査に値するのでは?
segmondy氏は、Cloudflareが681,280トークンしか保持できないのに1,048,576トークンのコンテキストウィンドウを宣伝したかどうかという関連する質問を提起しました。
他の人々は開示自体を賞賛しました。kami23氏は、根本原因分析を「ほとんどのものよりはるかに気に入った」と書き、所有権と、テストがバグを発見しなかった理由の明確な説明、およびCloudflareがモデルの提供を無効にできる速さを挙げました。
スレッドはまた、このインシデントを超えて問題を一般化しました。0xbadcafebee氏は、共有テナンシーはトークンを魔法のように分離できないことを意味し、「機密性の高いデータを持つ企業は、実際には非共有ホスト上にいるべきだ」と述べました。totetsu氏は、シングルテナント推論をサービスとして提供している人がいるかどうか尋ねました。nitishr氏は「より多くのサーバーレスプロバイダーが同様の問題を発見(または公開開示)していないことに驚いている」と書き、thehamkercat氏は他のプロバイダーは「おそらくこれが起こっていることすら知らない/気にしていない」と示唆しました。
2人のコメンターが、他の場所での同様のクロステナントリークと思われるものを説明しました。xmcqdpt2氏は、仕事でのClaude応答が「会社の他の誰かから来た可能性が非常に高い、非常に具体的で無関係な情報」を返したと述べ、Anthropicはそれは不可能であり、AIは時々間違いを犯すと彼らに言ったと述べました。reassess_blind氏は、ブラウザテストサービスのiOS VMがユーザー間でクリップボードデータを漏洩させたことを発見し、別のユーザーの資格情報を含み、ベンダーがバウンティを辞退したと述べました。これらはコメンターからの未検証の逸話であり、Cloudflareのレポートからの調査結果ではありません。
1人のコメンター、octoberfranklin氏は、このインシデントを「sglangがRustで書かれているにもかかわらず、初期化されていないメモリからの未定義動作によって焼かれた」ケースとして特徴付けました。Cloudflare自身のレポートは根本原因を異なる方法で説明しています:拒否されたリクエストは、アドミッションキューが拒否状態ではなくfinished()をチェックしたために許可され、デコードが上書きされていない古いKVページにアテンションしました。
協調開示
Cloudflareは、SGLangチームの投稿とともに、協調開示の一部としてレポートを公開しました。Cloudflareは、再現可能なスクリプトを使用してHackerOneプログラムを通じて問題を報告したGadi Evron氏、およびRadixArkのSGLangチームに感謝しました。Cloudflareは、SGLangチームがこのインシデントを自分たちの問題として扱い、緊急性を持って上流修正を提供したと述べています。Workers AIのセキュリティ問題は、hackerone.com/cloudflareで報告できます。