LLM Serving

GLM-5.3 KV cache exposure leaked 155 Workers AI responses via stale cache pages

A prefill KV pool smaller than the configured context, plus an SGLang rejection-path bug, let decode read other users' cache pages. Cloudflare recommends SGLang operators upgrade to v0.5.22 or later.

Published
October 26, 2026
Read
6 min
Published by
Samir Sengupta
Diagram of SGLang prefill and decode GPUs where decode reads stale KV cache pages from other users' GLM-5.3 requests
Note 109 / 115Daily note · Written from 1 source

How this was madeDrafted with Claude, Anthropic’s model, from 1 cited source, then checked against it in a second editing pass. Editorial policy

the short version

  • Between August 27 and September 9, 2026, 155 responses from Cloudflare's GLM-5.3 Workers AI model contained data from other customers' requests, all to prompts longer than 681,280 tokens.
  • The leak needed two faults at once: a prefill KV pool of 681,280 tokens behind a 1,048,576-token context window, and an SGLang disaggregation bug that sent rejected requests through prefill/decode handoff as if they were live.
  • Because KV pages are not zeroed between requests, correct page ownership tracking in the inference engine is the only thing that keeps one tenant's cache out of another's response.
  • Cloudflare recommends that all SGLang operators update to v0.5.22 or later. Cloudflare itself now checks KV pool size against context length on the running engine before any disaggregated deployment change ships.

The GLM-5.3 KV cache exposure on Cloudflare Workers AI caused 155 responses to include data from other customers' requests made to the same model shortly before. All 155 were responses to prompts longer than 681,280 tokens. The model's prefill KV pool held 681,280 tokens while its context window was 1,048,576. An SGLang bug in disaggregated prefill/decode mode then turned over-length requests into reads of stale cache pages instead of errors. The fix is a larger pool plus an engine patch, and Cloudflare recommends that all SGLang operators run v0.5.22 or later.

The exposure ran from the model's launch on August 27, 2026 until September 9. Gadi Evron of Knostic reported it through Cloudflare's HackerOne program on September 8 at 23:11 UTC. Cloudflare declared an incident at 13:22 UTC on September 9, set the model to private at 14:00, and restored it at 19:40 with a corrected configuration and a patched engine. Cloudflare says no other Workers AI model was affected. After excluding its own and the researcher's testing, it says the affected requests are consistent with ordinary long-context use, and it found no evidence of deliberate exploitation.

How the GLM-5.3 KV cache exposure happened

Workers AI serves GLM-5.3 (@cf/zai-org/glm-5.3) with SGLang in a disaggregated setup. Prefill GPUs build the KV cache for a prompt and hand it over RDMA, via Mooncake, to decode GPUs that generate the response. SGLang sizes the KV pool at startup from whatever GPU memory remains after loading weights, while the context length is a separate configuration value. Nothing forces the two to agree. On the GLM-5.3 prefill nodes they did not: 681,280 tokens of pool against 1,048,576 tokens of context.

One commenter on Hacker News asked whether Cloudflare advertised a 1,048,576-token window it could not hold. The report says the model was deployed with a 1,048,576-token context window and that Cloudflare knew about the mismatch before launch. The team expected the engine to return an error for prompts it could not process. Pre-launch tests showed successful HTTP status codes and a streaming body with no error frames. Those tests checked status codes and performance at long contexts, not model output and coherence, and Cloudflare now checks pool size against context length from the running engine before any disaggregated deployment change ships.

Why SGLang sent rejected requests to decode

When a prompt exceeded the pool, SGLang's scheduler rejected it through Req.set_finish_with_abort. That call recorded the error and swapped the prompt for a one-token placeholder so the request could drain through normal batching. On a single node this produced an error for the client. In disaggregated mode, both admission queues checked whether a request was finished, not whether it was rejected. The rejection lived in to_finish rather than finished_reason, so finished() returned False and the request looked live.

  1. The router told decode the full prompt length, and decode reserved KV pages for all of it (8 pages in Cloudflare's example).
  2. Prefill admitted the rejected request because finished() was False.
  3. Prefill ran a forward pass on the one-token placeholder.
  4. Prefill sent one page of KV to decode and signaled transfer complete.
  5. Decode's admission queue made the same finished() check and accepted the handoff.
  6. Decode attention read all 8 reserved pages; only P0 had been written by this request, and P1 to P7 still held earlier users' prompts and partial generations.
  7. The client received HTTP 200 instead of HTTP 400, with prompt_tokens matching the long prompt and a coherent reply containing content from other users' KV pages.

Cloudflare says every request over the threshold behaved this way, so the handoff happened every time rather than by a timing accident. One visible signal was that the reply arrived faster than an 8-page prefill should take. Another user had reported the bug to SGLang as issue #30233 on July 6, 2026, which Cloudflare found while investigating.

Unzeroed KV pages make ownership tracking the boundary

The KV cache is a shared pool of fixed-size pages. Pages are assigned at admission, released at completion, and not zeroed between requests; the next owner simply overwrites them. Cloudflare describes this as common among inference engines and necessary for the best performance. That makes the engine's page ownership and lifetime tracking the isolation mechanism. The tracking has to handle tasks on the same node with different lifetimes, such as KV cache offload and token generation, and a remote prefill node that holds a simultaneous mutable reference to the page.

One commenter on Hacker News concluded that enterprises with highly sensitive data should use non-shared hosts. The report does not address dedicated hosting or offer a page-zeroing option. Other commenters suggested that unrelated responses they had received from other providers might have the same cause. The sources say nothing about any provider besides Cloudflare, so treat that as speculation.

What Cloudflare changed in configuration and engine

  • Configuration: GLM-5.3 was aligned with the GLM-5.2 configuration. This freed enough GPU memory for a prefill KV pool of about 2.04 million tokens, roughly twice the 1,048,576-token context, so a valid prompt can no longer reach the rejection path.
  • Engine: a request with a pending rejection is now retired at the disaggregation admission queue, with its original error, before bootstrap, allocation or KV transfer. Cloudflare proposed this upstream as #38704, which also has decode verify that tokens transferred match tokens reserved.
  • Upstream: the fix was already on SGLang main as #36988, merged September 6 for a related failure path but not yet released. Maintainers also merged #38935, which rejects these requests earlier, following Cloudflare's approach.
  • Validation: both changes were tested against the researcher's reproduction script and Cloudflare's canary-marker tests before the model was restored.

Cloudflare calls pool size alone a fragile guarantee. Changes to quantization, tensor parallelism, CUDA graph sizes or a future model version all change the pool size in ways that are easy to miss. Other situations may also exist in which the prefill scheduler rejects a request and triggers the same bug. Every other disaggregated Workers AI deployment already had pools larger than its context length on both prefill and decode. The engine fix and later hardening now apply to every model Cloudflare serves with SGLang, and its deployment checks require them.

SGLang v0.5.22 is the version to run

Cloudflare says the patch was released in SGLang v0.5.20, with follow-ups in v0.5.21 (October 2, 2026) and v0.5.22 (October 19, 2026). It also says the bug was addressed in v0.5.21. Between September 16 and October 12, upstream merged the admission fix, per-transfer checksums and abort-ACK preservation, and turned deferred KV release on by default. Cloudflare met the SGLang maintainers, including RadixArk's CTO and core engineers, at 23:30 UTC on September 10. Teams serving shared-cache inference with SGLang in disaggregated mode should upgrade to v0.5.22 or later and compare the KV pool size against the configured context length; SGLang prints the pool size in its startup logs and exposes it as a metric.

KV cache memory-safety bugs are a wider class

Cloudflare groups this incident with KV cache memory-safety bugs it has been fixing for months. In each, a page is freed, reused or read while something else still claims it. One cause is lifetime within a node, where generation, host offload, the transfer receiver and abort paths share pages. Earlier fixes include #24226 and #30899 for decode offload and #30937 for double release on grammar errors. The other cause is a cross-node race: an abort arrives while prefill is still writing over RDMA, and late writes land in a page already given to another request. Cloudflare proposed #31357 and #38961 and contributed a reproduction script in #32564.

In all of these the result is corrupted output or one user's data in another user's response, with no error raised. Tests that check only status codes will pass. Long-context validation therefore has to check output content, as Cloudflare's canary-marker tests did before the model was restored.

Questions this raises

What caused the GLM-5.3 KV cache exposure on Workers AI?

The prefill KV pool held 681,280 tokens while the context window was 1,048,576, so longer prompts hit SGLang's rejection path. In disaggregated prefill/decode mode, rejected requests still looked live, and decode read reserved KV pages that still held earlier users' prompts and partial generations.

Which SGLang version should operators run to avoid this bug?

Cloudflare recommends that all SGLang operators run v0.5.22 or later. Between September 16 and October 12, upstream merged the admission fix, per-transfer checksums and abort-ACK preservation, and turned deferred KV release on by default.

Was the GLM-5.3 cache leak deliberately exploited?

Cloudflare says it found no evidence of deliberate exploitation. After it excluded its own testing and the researcher's, the affected requests were consistent with ordinary long-context use. It also says no other Workers AI model was affected.

Spotted an error? Email samir.s.sengupta@gmail.com and I will correct it and date the change at the top of the note. How these notes are researched, drafted and checked is in the editorial policy.

More notes

Building something on this?

I ship production LLM, RAG and agentic systems for a living - the infrastructure behind the things these notes are about. Open to roles, contract work and research collaboration.