Cloudflare 披露 GLM-5.3 Workers AI 跨请求 KV cache 泄露事件及修复细节
Cloudflare 于 2026 年 10 月 26 日发布协同披露报告,说明 Workers AI 上的 GLM-5.3 模型在 8 月 27 日至 9 月 9 日期间发生跨请求 KV cache 泄露,共 155 条响应受影响,均为超过 681,280 tokens 的超长提示。
同一新闻,精选展示《Cloudflare 披露 GLM-5.3 Workers AI 跨请求 KV cache 泄露事件》
Hacker News:AI 热帖· wondersky·2026-10-27 06:01· 4 小时前AI 评分74
GLM-5.3 Workers AI 模型中的跨请求 KV 缓存暴露问题(155 条回复)
AI 导读
Cloudflare 于 2026 年 10 月 26 日发布协同披露报告,说明 Workers AI 上的 GLM-5.3 模型因 prefill KV cache 池(681,280 tokens)小于上下文窗口(1,048,576 tokens)叠加 SGLang 分离式推理的拒绝路径缺陷,导致超长请求读到了之前其他用户请求的 KV cache 数据。
正文 · 原文
2026 年 9 月 8 日,来自 Knostic 的安全研究员 Gadi Evron 向我们发出警报,指出自 2026 年 8 月 27 日起,GLM-5.3 Workers AI 模型(@cf/zai-org/glm-5.3)存在租户隔离问题。我们已修复该问题,并于 9 月 9 日恢复了该模型。我们已确认 Workers AI 上托管的其他模型均未受影响。
8 月 27 日至 9 月 9 日期间,少数使用 GLM 5.3 Workers AI 模型的客户收到的回复中,包含了其他客户在此前不久向同一模型发出的请求数据。共有 155 条回复受到影响,全部来自长度超过 681,280 tokens 的提示词。
在排除我们自身和该研究员的测试后,我们识别出的请求在时间和数量上与普通的长上下文使用情况一致,而非有人反复利用该漏洞。我们未发现任何故意利用的证据。
今天,我们与 SGLang(我们所使用的开源推理引擎)的一篇博文同步发布本报告,作为协同披露。 本文介绍了导致该问题的缺陷、我们的响应时间线,以及我们随后对生产环境 SGLang 部署所做的加固工作。我们还详细说明了我们与 SGLang 上游维护者持续合作、以解决此问题及类似缺陷的努力。
发生了什么
GLM 是来自 Z.ai 的文本生成模型,专为智能体编程工作流而构建;它是 Workers AI 提供的众多模型 之一。Gadi Evron 于 9 月 8 日通过我们的 HackerOne 项目提交报告,称发送给 Workers AI 上 @cf/zai-org/glm-5.3 的超长提示词有时会返回属于其他用户会话的文本。Cloudflare 于次日上午复现了该问题,并在宣布事件后 40 分钟内将该模型下线。随后我们部署了修正后的配置和打过补丁的推理引擎,并于当天恢复了服务。
这份关于跨请求 KV 缓存暴露的具体报告是两项重叠问题共同导致的结果。第一项是 Cloudflare 所使用的开源推理引擎 SGLang 的配置错误。我们部署 GLM 5.3 时设置了 1,048,576 tokens 的上下文窗口,但我们 prefill GPU 上的 KV 缓存池只能容纳 681,280 tokens。我们在上线前就已知道这一不匹配。我们原本预期引擎会对任何无法处理的提示词返回错误,而我们的长上下文测试返回了成功响应,因此我们并不认为这是一个问题。
第二个问题是 SGLang 在分离式 prefill/decode 模式下处理由此产生的超长请求时存在缺陷。一个无法放入池中的 prompt 在内部被标记为已拒绝,并被替换为一个单 token 的占位符,但 prefill 并没有中止该请求并返回客户端错误,而是为这个占位符 prompt 生成了 KV cache,并将其传输到 decode 节点。decode 节点收到“传输完成”消息后,以为 prefill 已用该请求的完整 prompt 覆盖了先前请求的 KV cache,而实际上它保留了先前状态的大部分内容。decode 节点随后在仍持有先前请求数据的过期缓存页上执行注意力计算并生成响应。结果,对超过 681,280 个 token 的 prompt 的响应可能包含其他用户 GLM 5.3 会话的数据。
立即修复
在禁用该模型后,我们采取了以下措施来修复此漏洞,然后才恢复对其的公开访问:
为修复我们的配置,我们让池容纳完整上下文。 我们将 GLM 5.3 的配置调整为与 GLM 5.2 配置一致,从而释放了足够的 GPU 内存,使 prefill KV 池现在可容纳约 204 万个 token,约为 1,048,576 token 上下文长度的两倍。现在,适合上下文窗口的 prompt 总能放入池中,因此有效请求永远不会走到拒绝路径。仅此一项即可防止该缺陷被触发。
为修复引擎,我们还让拒绝路径变得安全。 仅依赖池大小是脆弱的:量化、张量并行、CUDA graph 大小或未来模型版本的任何变化都会以容易被忽视的方式改变池大小。此外,可能还有其他情况会使 prefill 调度器拒绝请求并触发同一缺陷。为防止这种情况,我们还修补了引擎,使携带待处理拒绝的请求在分离式准入队列处以其原始错误被淘汰,且发生在任何 bootstrap、分配或 KV 传输之前。我们将此作为 #38704 向上游提出,该补丁还让 decode 节点验证传输的 token 数量与其预留的数量匹配。该修复已存在于 SGLang main 的 #36988 中,该补丁于 9 月 6 日针对相关故障路径合并,但尚未发布。维护者还合并了 #38935,该补丁遵循我们的方法,更早地拒绝这些请求。
在恢复模型之前,我们使用研究人员的复现脚本和我们自己的金丝雀标记测试验证了这两项更改。
背景:我们如何服务 GLM 5.3
Workers AI 以分离式配置通过 SGLang 运行 GLM 5.3:一组 prefill GPU 处理 prompt 并为其生成模型的 KV cache,然后通过 Mooncake 以远程直接内存访问(RDMA)方式将该 cache 交给一组 decode GPU 来生成响应。我们此前在关于高性能 LLM和更小、更快、更安全的模型的文章中介绍过这种架构。
GPU 内存中的 KV 缓存是一个由固定大小页面组成的共享池。每个请求在被接纳时会被分配页面,并在完成时释放这些页面。目前,请求之间不会将页面清零;下一个获得该页面的请求会直接覆盖它。这种做法在推理引擎中很常见,也是实现最佳性能所必需的,但这意味着推理引擎负责跟踪这些内存页面的所有权。KV 缓存页面的所有权和生命周期跟踪不仅要处理节点内的共享所有权(KV 缓存卸载和 token 生成都以不同的生命周期访问该内存),还要处理技术上与远程 prefill 节点共享的所有权(该节点同时持有该页面的可变引用)。
此外,这个池的大小并不是一个配置值。SGLang 在启动时根据加载模型权重后剩余的 GPU 内存来确定其大小。模型宣称的上下文长度是一个单独的配置值。没有任何机制强制两者一致,而检查它们是否一致是我们的职责。
问题一:配置错误
在我们的配置下,GLM 5.3 prefill 节点上的池大小为 681,280 个 token,而模型的上下文长度为 1,048,576 个 token。所选的 KV 池大小会在启动时由 SGLang 打印在日志中,并作为指标暴露出来。我们意识到了这种大小不匹配;然而,在发布前测试期间,我们在基准测试中观察到了成功的 HTTP 状态码和没有错误帧的流式响应体。我们以为引擎能够通过流式传输或其他机制妥善处理 KV 缓存池与请求上下文窗口之间的差异。
这是我们测试中的一个关键缺口:我们只验证了长上下文下的 HTTP 响应码和引擎性能,而没有验证模型输出和连贯性。现在,在任何分离式部署变更上线之前,我们会直接从运行中的引擎检查 KV 池大小与上下文长度是否匹配,而不是从响应码推断。
问题二:SGLang 缺陷
在 v0.5.21 中,SGLang 团队现已修复了其处理长于 KV 池大小的提示词方式中的这个缺陷。以下是之前发生的情况。
当到达的提示词长度超过 KV 池容量时,SGLang 的调度器会拒绝它。拒绝路径(Req.set_finish_with_abort)记录要返回给客户端的错误,并将提示词替换为一个单 token 占位符,以便请求可以在不分配真实内存的情况下通过正常批处理机制排空。在单节点部署中,这可以正常工作:占位符运行,批次完成,客户端收到错误。

在我们运行的分离式模式下,prefill 和 decode 上的准入队列检查的是请求是否已完成,而不是是否被拒绝。被拒绝的请求会报告自己为存活状态。因此:
- 引导。路由器告知 decode 节点提示词长度(图中为 8 页)。Decode 为该请求预留了 8 个 KV 页。
- Prefill 准入。Prefill 准入队列接纳任何
req.finished()为 False 的请求。被拒绝的请求将其拒绝状态存储在to_finish中,而不是finished_reason中,因此finished()仍返回 False。被拒绝的请求被接纳了。 - Prefill 前向传播。拒绝操作已将提示词替换为一个单 token 占位符。Prefill 对该单个 token 运行了一次简单的前向传播。
- KV 传输。 Prefill 向 decode 发送了一页 KV,并发出传输完成的信号。
- Decode 准入。 Decode 的准入队列有同样的
finished()检查,因此它接受了这次交接。 - Decode 注意力。 Decode 开始生成,注意力读取了全部 8 个预留页。只有页 P0 是由该请求写入的。页 P1 到 P7 仍保留着先前请求留下的内容:其他用户的提示词和部分生成结果。
- 响应。 客户端收到的是 HTTP 200 而不是 HTTP 400。
prompt_tokens匹配了长提示词,回复也是连贯的。它到达的速度比 8 页 prefill 应有的速度更快,并且包含了其他用户 KV 页的内容。
每个超过阈值的请求都表现如此。该漏洞已由另一位用户于 2026 年 7 月 6 日作为 issue #30233 报告给 SGLang,Cloudflare 在调查此事件时注意到了它。自 v0.5.10 起,我们一直在向 SGLang 标记此类问题,而此次安全事件加快了 Cloudflare 已与 SGLang 团队合作启动的工作。该问题的补丁已在 SGLang v0.5.20 中发布,后续在 v0.5.21 和 v0.5.22 中跟进,所有依赖 SGLang 的其他运营方均可使用。我们强烈建议所有运营方将 SGLang 依赖更新至 v0.5.22 或更高版本。有关具体 PR 的更多细节,请继续阅读下文。
GLM 5.3 是唯一受此漏洞影响的模型。Workers AI 上其他所有分离式部署的 KV 池在 prefill 和 decode 两端都大于其上下文长度,因此没有请求能到达拒绝路径。我们已对使用 SGLang 提供服务的每个模型应用了引擎修复和下文所述的加固措施,并且我们的部署检查要求每个新部署都必须具备这些措施。
时间线
所有时间均为 UTC。
时间 | 事件 |
2026 年 8 月 27 日 | GLM 5.3 在 Workers AI 上线,其 prefill KV 池小于上下文窗口。该暴露风险自上线起即存在。 |
2026 年 9 月 8 日 - 23:11 | 通过 HackerOne 收到报告。 |
2026 年 9 月 9 日 - 08:00 | Cloudflare 开始核实 HackerOne 报告。 |
2026 年 9 月 9 日 - 13:22 | 宣布安全事件。产品安全团队在 25 分钟内使用两个独立账户复现了泄漏。 |
2026 年 9 月 9 日 - 14:00 | 在确认问题范围后,GLM 5.3 被设为私有。请求返回 HTTP 400。状态页已更新。 |
2026 年 9 月 9 日 - 14:35 | 根因确定为 prefill 池小于上下文长度。使用更大池的 Canary prefill 节点确认不受影响。 |
2026 年 9 月 9 日 - 14:52 | 所有 prefill 节点已滚动更新至修正后的配置。 |
2026 年 9 月 9 日 - 19:04 | 使用修正后的配置以及 Cloudflare 编写的引擎补丁重建了完整部署,该补丁可对被 prefill 调度器拒绝的请求正确返回错误。已针对研究人员的复现脚本进行验证。 |
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 协调发布。 |
持续加固工作
这个 bug 属于一类 KV 缓存内存安全 bug,Cloudflare 已经为此努力了数月。在每种情况下,一个页面在被释放、重用或读取时,仍有其他东西对它持有所有权。我们看到了两个常见原因。
第一个是单个节点内的页面生命周期。解码节点上的多个任务可能同时持有同一个页面:token 生成、卸载到主机内存、KV 传输接收方,以及中止和错误路径。如果其中一个任务在另一个任务仍在使用该页面时将其释放,那么一个新请求可能会在该页面仍在被读取或写入时被分配到这个页面。我们之前针对解码卸载(#24226、#30899)以及语法错误时的双重释放(#30937)的修复就属于这一类。
第二个是跨节点的竞态。当一个请求在预填充仍通过 RDMA 向解码写入其 KV 缓存时被中止,已经发送的写入无法撤回。如果解码立即释放这些页面,这些页面可能会被分配给另一个请求,而迟到的写入就会落入那个请求的 KV 缓存中。Cloudflare 通过提议的修复(#31357、#38961)和一个复现脚本(#32564)为修复这一竞态做出了贡献。在这两种情况下,结果都是输出损坏,或者一个用户的数据出现在另一个用户的响应中,且不会引发任何错误。本次事件中的 bug 有所不同:一个被拒绝的请求每次都会经过交接,而不是由于时序上的偶然。但结果相同:解码读取了该请求从未写入的页面。这些都没有涵盖本次事件中的故障,即预填充拒绝了请求,而解码从未得知此事。
自事件发生以来,我们:
- 扩展了延迟释放,使其也保留预填充的源页面,并覆盖所有失败类型,而不仅仅是中止。我们将其作为 #38961 向上游提出,该提案随后被 #40645、#41402 和 #41404 取代。
- 部署了有界的 Mooncake 传输批次(#32758,来自 SGLang 社区),因此任何单次传输调用都不会超过释放超时时间。
- 更改了我们的负载均衡器,使客户端断开连接不再在传输过程中中止请求。它会保持预填充连接直到预填充完成,并保持解码连接直到第一个 token,然后关闭两者。
- 为每次分离式部署变更添加了检查:根据上下文长度测量 KV 池大小、两个角色上的延迟释放、释放超时内的传输批次边界,以及所有必需的补丁均已应用。
所有这些变更都已发布到我们的分离式生产部署中。
与 SGLang 团队合作
9 月 10 日,即事件发生后的第二天,我们在 RadixArk 与 SGLang 维护者会面。我们一致同意,RadixArk 将调查我们报告的竞态条件,将完整性检查的想法向上游提出,并为这类 bug 构建复现测试。
以下内容已在 SGLang main 上落地,以解决本次事件及相关的 KV 缓存竞态条件,除一个外均已包含在 v0.5.22 中。运行分离式 SGLang 的运维人员应更新到 v0.5.22,或根据需要向后移植这些补丁。
类别 | 拉取请求 | 版本 | 作用 |
事件 | v0.5.20 | 在已中止的预填充请求的结果到达解码之前将其退役。这阻止了本次事件中的暴露。 | |
事件 | v0.5.21 | 在准入阶段、任何 bootstrap、分配或 KV 传输之前就拒绝这些请求。遵循 Cloudflare 草案补丁中的做法,#38704。 | |
竞态 | v0.5.21、v0.5.22 |
| |
竞态 | v0.5.20、v0.5.21 | 仅在支持延迟 KV 释放的后端上启用该功能,随后将其设为默认开启。 | |
竞态 | v0.5.21 | 修复 drain 确认丢失的情况,使释放在收到确认时发生,而不是等到超时。 | |
竞态 | v0.5.21 | 在每次传输失败时都保留页面,而不仅是 decode 发起的中止。 | |
竞态 | v0.5.21 | 修复中止通知路径中的一处回归。 | |
竞态 | v0.5.22 | Mooncake 发送失败后不再将其写入报告为已 drain。 | |
竞态 | v0.5.22 | 按请求为中止消息打标签,使复用的 ID 无法消费过期的确认。新增 MORI 支持。 | |
竞态 | v0.5.20 | 在 Mooncake、NIXL 和 MORI 之间共享 prefill 到 decode 的失败通知。 | |
竞态 | v0.5.20 | 可选限制 Mooncake 传输批次大小,使一次超时调用不会留下无界的在途写入。 | |
生命周期 | v0.5.21 | Decode 在撤回请求并释放其页面之前,等待进行中的主机内存卸载完成。由 Cloudflare 编写。 | |
生命周期 | v0.5.22 | 在主机内存缓存恢复失败时保持张量并行各 rank 的一致性。 | |
可观测性 | v0.5.21 | 新增 | |
可观测性 | v0.5.20、v0.5.21 | 修复 SGLang 的 KV canary,该功能用于检测并记录潜在的损坏。 | |
可观测性 | v0.5.22 | 延迟释放的指标:计数、持续时间,以及每次保持是以 drain 确认还是超时结束。 |
以下后续工作仍在进行中:
- #39729 针对
--disaggregation-enable-kv-checksum的额外清理。已合并,等待发布。 - 释放时将 KV 页面清零,草案提案:#46435
- 将 KV 校验和验证从 prefill 到 decode 的交接扩展到每个 decode 步骤(见下文“构造即完整”)
- 更广泛的 KV Canary 与 KV Checksum 模型和功能支持
我们给其他运维者的建议
如果你以分离式 prefill/decode 模式运行 SGLang,我们建议如下:
- 测量你的 KV 池并将其与上下文长度对比,在每个 prefill 和 decode 节点上,从引擎启动日志中读取,并在每次更改权重、量化、并行度或内存比例之后重新测量。如果池较小,就降低上下文长度以适配。不要依赖代理来强制执行限制,也不要假设引擎会拒绝放不下的内容。这正是我们未能强制执行的检查。
- 升级到包含 #36988 和 #38935 的构建(v0.5.22 或更高版本),这样无论池大小如何,超长请求都会在交接前被拒绝。这是引擎侧的修复,也是当第一步检查出错时的兜底。
- 在两个角色上都启用延迟 KV 释放 (在 #41023 之后为默认),并限制 Mooncake 传输批次,使一次超时调用不会留下无界的写入尾部。
- 在你的模型支持的情况下开启损坏检测。
--disaggregation-enable-kv-checksum和--kv-canary尚未覆盖所有模型架构。检查日志以确认请求正在被验证。我们正与 SGLang 团队合作,将两项检查扩展到更多架构,包括 GLM 5.3、GLM 5.3 Flash 和 DeepSeek V4。 - 确保你的代理不会产生不必要的中止。配置你的负载均衡器,允许请求进行到第一个 token,这样你就不会触发需要跨节点协调的中止。
更长期
通过构造保证完整性
配置检查和生命周期修复减少了页面被误用的方式数量。它们并不能使误用变得不可能,而这次事件提醒我们,下一个 bug 将具有我们尚未见过的形态。持久的解决方案是让引擎在读取时检测到某个请求即将在一个它并不拥有的页面上进行注意力计算,并让那一个请求失败,而不是返回别人的数据。
我们在夏天构建了它的原型,并在八月进行了描述。每个物理 KV 页面都带有一个标签,当页面被重新分配时该标签会改变,每个请求记录它期望的标签,并且在 decode 读取缓存之前会检查映射。在我们的测试中,它带来的吞吐量和尾部延迟损失不到 1%。我们在八月中旬于 GLM 5.2 上进行了测试,但随后不久因可靠性问题将其禁用。
该原型还与特定的注意力后端和模型架构绑定,因此无法成为通用修复方案。我们正在与 SGLang 团队合作,在引擎中构建一个适用于所有模型的生产版本,而不是维护 Cloudflare 专用的补丁。#39500 中的每次传输校验和是第一步:它验证从 prefill 到 decode 的交接。页面级标签则更进一步,因为它们检查每一次读取,包括传输很久之后的读取。
通过数学上的不可能性保证完整性
RDMA 将数据从一个节点的内存直接复制到另一个节点的内存。两台机器必须在数据去向、数据大小以及何时完成上达成一致,这一点至关重要;任何不匹配都可能导致接收方读取错误的数据。这个 bug 是分布式系统中一致性故障的一个例子,其中两台机器对该系统的状态有着不一致的视图。
在 Cloudflare 的其他地方,我们投入精力使用形式化验证方法来证明分布式系统的安全性。Topaz 为我们的权威 DNS 服务器实现了无冲突的 IP 选择。Meerkat 的 QuePaxa 实现模型已经展示出随后被修复的一致性 bug。
我们现在正将我们在形式化方法方面的经验引导到 KV 缓存传输引擎上。这将从对基于 RDMA 的 KV 缓存传输协议进行建模并证明其安全性开始。目标是使用 Mooncake 对我们生产环境的 SGLang 分布式 KV 缓存系统进行建模,然后跟踪并修复任何偏离我们希望保证的安全属性的情况。目的是尽可能证明这样的 bug 不会再次发生。现实地说,我们预计证明器能告诉我们的内容会存在空白,但证明系统的其余部分使我们能够更仔细地审查和监控这些空白。
协调披露
我们作为协调披露的一部分发布这份报告。我们要感谢研究员 Gadi Evron,他通过我们的 HackerOne 项目报告了此问题,提供了清晰、可复现的脚本,并给了我们时间来修复它,并在发布前与 SGLang 维护者协调。我们还要感谢 RadixArk 的 SGLang 团队,他们将此视为他们自己的问题以及我们的问题,并紧急提交了上游修复。
阅读更多内容,请访问 SGLang 团队的这篇博文。
如果你在 Workers AI 中发现安全问题,请在 hackerone.com/cloudflare 报告。
来源:Hacker News:AI 热帖 · blog.cloudflare.com
来源:RadarAI · aihot.news