KV 缓存边界失守如何引发跨租户泄露
据文中事故复盘,Workers AI 的 GLM-5.3 标称上下文容量超过实际 KV 缓存容量,超长请求被标记拒绝后仍进入解码流程。解码节点读取残留缓存,导致 155 条响应混入其他客户的请求片段。
AI 深度解读
Cloudflare 的一次 AI 数据泄露,起点是两个对不上的数字:GLM-5.3 宣称支持约 100 万 token 上下文,实际 GPU 缓存却只能容纳 681,280 token。超长请求被标记为拒绝后,系统仍继续生成,最终把其他客户的请求片段带进了响应。
原文提到,8 月 27 日至 9 月 9 日期间,共有 155 条响应受到影响。没有人入侵系统。这件事值得程序员关注,因为它暴露的不只是容量配置问题,还有更常见的工程漏洞:报错状态写下去了,执行流程却没有停。
上下文装得下,不代表 GPU 缓存装得下
模型对外声明的上下文窗口,和部署环境实际能承载的长度,是两回事。
GLM-5.3 在 Workers AI 上宣称支持约 1M token。负责推理的 SGLang 会在启动时,根据 GPU 剩余显存计算 KV cache 池的容量。KV cache 可以理解为模型处理请求时保存的中间计算结果,后续生成会继续使用它。
这次部署中,缓存池实际只能容纳 681,280 token,但系统没有检查这个容量是否匹配对外声明的长度。
模型规格里的上限,不能直接当成服务部署后的容量保证。
原文说,全部 155 条异常响应,都对应长度超过 681,280 token 的提示词。不过,它没有提供这段时间超长请求的总量,因此不能据此计算泄露概率,也不能推断所有超长请求都会泄露。
为什么标记了拒绝,还会吐出别人的数据
容量不匹配只是第一步。真正让数据跨客户流出的,是拒绝请求之后的执行错误。
SGLang 当时使用了预填充与解码分离的路径,也就是 disaggregated prefill/decode。前一个阶段处理输入,后一个阶段逐步生成输出,两者由不同节点承担。
超长提示词到达后,系统把请求标记为拒绝,却没有终止处理。解码节点随后继续生成,使用的还是残留着先前请求数据的旧缓存页,于是其他客户的请求片段进入了响应。
这里有两个需要分别修复的问题:
- 入口应当识别实际无法承载的请求,避免容量配置与服务承诺脱节。
- 请求一旦被拒绝,后续节点就必须停止,不能继续读取缓存并生成结果。
「请求状态是失败」和「请求已经停止执行」不是同一件事。
原文没有展开缓存页如何分配、回收,以及修复具体增加了哪些检查。因此,不能把事故简单归结为「每次用完清空显存就行」,也不能替修复方案补上原文没有描述的实现。
我的判断
这次事故最有参考价值的地方,是两个缺陷如何串在一起:容量校验缺失让异常请求进入处理路径,终止逻辑失效又让异常变成了数据泄露。只盯着其中一处,很容易漏掉完整的故障链。
Cloudflare 的响应时间也值得记录。研究员 Gadi Evron 于 9 月 8 日通过 HackerOne 报告问题;Cloudflare 在 9 月 9 日宣布进入事件响应后,25 分钟内复现,40 分钟内下线模型。随后向全部节点部署修复,当晚恢复服务,并在 10 月 26 日发布事故报告。
这些时间能说明处置动作,但不能单凭它们判断隔离机制已经充分可靠。
版本建议还要仔细读:原文提到与 SGLang 维护者合作的上游修复涉及 v0.5.21+,同时建议使用分离式部署的自托管用户升级到 高于 v0.5.22 的版本。两处表述并不相同,原文也没有解释各版本修复范围,不能直接认定 v0.5.21 就包含全部所需修复。
对于普通 API 调用方,这件事也说明:在 Node 网关里限制输入长度有用,但无法代替推理服务自身的容量校验和客户数据隔离。客户端保护只能守住自己这一侧。
能拿来做什么
- 如果你在 8 月 27 日至 9 月 9 日期间向 Workers AI 的 GLM-5.3 发送过 681K+ token 的提示词,按原文建议联系 Cloudflare 支持,确认账户是否受影响;681K+ 是排查提示,事故中给出的精确门槛是超过 681,280 token。
- 如果你自托管 SGLang,并采用 prefill/decode 分离式部署,按原文建议升级到高于 v0.5.22 的版本,同时核对当前 GPU 配置算出的缓存容量与对外上下文上限。
- 如果你维护 Node AI 网关或多阶段生成任务,检查拒绝、超时、取消之后是否还有下游任务继续执行。验证重点应包括「没有继续生成」,不能只看接口是否返回错误。