The failure in one picture
A long prompt is rejected by the engine, but the request still goes through the prefill to decode handoff. Decode reserves pages for the whole prompt. Prefill writes one.
Decode reads all eight. Seven still hold earlier requests' data. The client gets HTTP 200 and a fluent answer.
Illustration of the Cloudflare incident, using the eight pages in Cloudflare's own diagram. Real prompts were far longer.
One rule covers most of this. Every exit path releases what it acquired, exactly once, and only after all writers have finished.
Five shapes
Reading pages this request never wrote
The receiver reserves for the whole request, the sender fills part of it, the transfer is marked complete.
Cloudflare postmortem, 26 October 2026. 155 responses, 27 August to 9 September. No evidence of deliberate exploitation. Confidentiality.
Late writes into reissued memory
An aborted request has remote writes in flight that cannot be recalled. The memory is reissued first, and the writes land in another request's cache. The victim sees the same symptom as shape 1, so the two cannot be told apart from the response.
SGLang team post, 26 October 2026, reproduced live on two GPUs: the victim's output turned into the aborted request's prompt text. What leaks is the aborted request's own context. Affected from the v0.4.x line (early 2025) until v0.5.21, or v0.5.22 for one backend; not backported to earlier branches. No CVE assigned at time of writing. Confidentiality and integrity.
Slot state not reset on reuse
A token allowlist from one request stays in a batch slot and constrains a later, unrestricted request. The original owner may still be running: this is a stale copy, not data in flight.
vLLM GHSA-6cxc-2vcg-w5qc, 2026-10-06, Low (3.7). Its version field lists 0.31.0 as patched; its text says the fix was merged but not yet in a tagged release. Integrity.
Components disagree after a rejection
One layer records an item as cached before a later check rejects the request. The paired layer never gets it.
vLLM CVE-2026-105753, 2026-09-28, moderate. Patched in 0.28.0 per the advisory metadata, whose body lists versions differently. It trips an assertion, so it is visible. The same shape appears in SGLang tensor-parallel ranks after a failed cache restore. Availability.
Reported, below the advisory bar
A mixed batch skips a checkpoint write but still donates the slot to the prefix cache; the reporter saw about a tenth of eval answers change while the server stayed healthy. Also an addressing bug (range example only) and two related scheduler-stall reports.
SGLang issues 39342 (open, fix also open) and 39271 (open); 41841 closed as completed 2026-10-12; PR 41144 merged. States checked 2026-10-27. Illustration only. Integrity and availability.
Check your stack
Four tests
Each test has a positive control: a condition it must fail under. A test that cannot fail proves nothing. These are procedures. Hedgerow has not run them.
Pool against limit
- Do
- Read pool capacity and context limit from the running engine on every node.
- Pass
- The pool holds the full context limit everywhere.
- Must fail when
- A staging node has a smaller pool.
Over-capacity rejection
- Do
- Run requests with unique canary markers, then send one larger than capacity.
- Pass
- The client gets an error and no foreign canary appears in any output.
- Must fail when
- A fault injection skips the final page write. If it does not leak, the test is blind.
Mixed-outcome concurrency
- Do
- Interleave valid, rejected and cancelled requests, each with its own canary.
- Pass
- Every response contains only its own canary. Check content, not only status.
- Must fail when
- The same load runs on a build with a published stale-state advisory.
Slot reuse
- Do
- Interleave a request with a token allowlist and unrestricted ones while the batch compacts.
- Pass
- Unrestricted outputs are unconstrained.
- Must fail when
- Run against vLLM below 0.31.0.
Mapping to AISVS, ATLAS and CWE
| Framework | Mapping |
|---|---|
| Affected component | Scheduler and batch state; KV page pool; prefill to decode handoff; paired cache layers in front of the engine |
| OWASP AISVS 1.01 | Closest: 5.3.1 (shared serving must not let one tenant influence or observe another) and 4.2.4 (accelerator memory sanitized between jobs, level 3). Neither covers per-request state reset inside one engine's pooled memory. A keyword search of 1.01 and 1.02-dev found no requirement that does. |
| MITRE ATLAS 5.6.0 | Partial: AML.T0057 LLM Data Leakage. It describes adversary-crafted prompts. The shape 1 incident involved no adversary, so no technique fits exactly. |
| CWE | Shape 1: CWE-226 Sensitive Information in Resource Not Removed Before Reuse, and CWE-459 Incomplete Cleanup (Hedgerow proposals). Shape 3: CWE-440 and CWE-668, as assigned in the advisory. Shape 4: CWE-617 Reachable Assertion, as assigned in the advisory. |
All mitigations
| Control | Shapes |
|---|---|
| Release state on every exit path exactly once, and not while any accessor can still touch it, local or remote, reader or writer. For one-sided remote writes, release on a positive drain acknowledgement. If only a timeout exists, derive it from a stated upper bound on how long writers can stay alive (transport timeout plus a transfer batch cap), treat expiry as a backstop, and never put the released content into a reusable cache. With no such bound, keep the slots out of use, count them against capacity, alarm, and fence by restarting the receiving worker. Every degradation should lengthen the hold. | 1, 2, 3, 4 |
| Fail closed when the two sides of a handoff disagree. Two different count checks are in the record. Comparing tokens transferred with the receiver's own reservation is independent of the sender: Cloudflare proposed it, and its pull request shows closed and unmerged. Comparing with the length the sender publishes is not independent: the SGLang maintainers declined it because that length comes from the same accounting that signals success. Compare against something the sender did not produce. | 1 |
| Check cache pool size against the context limit from the running engine, before serving and after every relevant change. Do not rely on a proxy to enforce the limit. | 1 |
| Use an end-to-end integrity check on transferred data. A non-cryptographic checksum catches corruption and truncation, not deliberate writes, and only writes that land before the check. It is opt-in and does not yet cover every model architecture: confirm in the logs that requests are verified. We know of no production engine that verifies page ownership at read time; Cloudflare built a page-tag prototype that cost under 1% in its tests and disabled it for reliability, and is working on a production version. Zeroing pages on release (a draft proposal) would blunt shape 1, since unwritten pages would read as zeros, but it does not stop a late write. | 1, 2 |
| Keep the load balancer from aborting a request mid-transfer. Pair aborts in the router so both halves learn of them. Run matching prefill and decode versions: if one side does not acknowledge, the other waits out its timeout. Held capacity is about abort rate times hold time times pages per request, so size for it and rate-limit aborts per tenant. | 2 |
| Validate response content under concurrent, mixed-outcome load as part of release testing, using canary markers. | All |
What this page does not claim
- The Cloudflare incident was an exposure. Cloudflare found no evidence of deliberate exploitation.
- One incident is not a prevalence figure for any engine.
- The tests have not been run by Hedgerow.
- Shape 5 is bug reports, listed to show range, not as evidence of exposure.
- The mapping reflects the standard versions named above and may change as they do.
Sources
- Cloudflare, "Cross-request KV cache exposure in the GLM-5.3 Workers AI model, and our remediation with SGLang", 26 October 2026. blog.cloudflare.com
- SGLang team, "SGLang security update: improving request isolation", 26 October 2026. lmsys.org
- GHSA-6cxc-2vcg-w5qc. github.com/advisories
- CVE-2026-105753, GHSA-ph3r-5jfg-f84f. github.com/advisories
- SGLang issues 39342, 39271, 41841, and pull request 41144.
- NVD CVE-2023-28858 and CVE-2023-28859 (redis-py), the same class outside serving engines. nvd.nist.gov
- GRIEF, arXiv:2605.11202. arxiv.org
- OWASP AISVS 1.01. github.com/OWASP/AISVS
- MITRE ATLAS 5.6.0. atlas.mitre.org
Changelog
- 2026-10-27. First published. Definition widened to state that escapes its owner, after outside review and the SGLang team's post.
- 2026-10-27. Second outside review: count-check paragraph reframed as two compatible checks, bounded-timeout release wording, checksum coverage warning, page-tag prototype note, release versions and failed-send fixes, preconditions on the adversary line, two directions with owner-alive note, issue states with access date, no-CVE note for shape 2.
- 2026-10-27. After one outside review: shape 2 relabelled advisory-backed, adversary and release-control wording corrected, count-check claim attributed to both posts, shape 5 wording fixed, redis-py record added.