Redis『4GB 边界』:集群总线 pub/sub 堆溢出该怎么查
一句话结论:这次两家同时修的漏洞,触发条件写得非常具体——集群模式、集群总线、PUBLISH/SPUBLISH、传播过程、消息大于 4GB,五个条件缺一不可;真正要动手的不是「有没有用 pub/sub」,而是五个条件在你的拓扑里能不能同时成立。
项 | 内容 |
摘要 | 10 月 26 日 Redis 与 Valkey 同日发布安全版,修的都是「集群模式下经集群总线传播超过 4GB 的 PUBLISH 消息导致堆溢出」。本文逐条拆官方公告的触发条件,指出两家用语上的差异,用零依赖脚本把 4GB 边界、集群总线扇出量与配置自查项算成可直接执行的清单。 |
前置知识 | Redis Cluster 的基本形态(节点间通过集群总线通信);知道 PUBLISH/SPUBLISH 与集群总线的关系;读过一次 |
阅读时间 | 约 16 分钟 |
代码环境 | Python 3.9+,零第三方依赖,不联网,输出确定(无随机源);脚本见 |
读完你能做什么 | 能在 10 分钟内判断自己集群是否落在触发条件里;能解释为什么 4GB 不是「一个建议值」而是 32 位长度字段的天花板;能给出升级之外的三条缓解路径;能在变更单里写清跨七条 Redis 产品线的一致性修复范围 |
一手来源有三处,本文引用的每一句话都能在其中点到:
- Redis Open Source 8.10 发布说明(
00-RELEASENOTES,8.10.3,Released Mon 26 Oct 2026)
- Valkey 9.1.3 / 9.0.7 / 8.0.12 发布公告(GHSA-f4q9-j22m-xqq9,Released Mon 26 October 2026)
- endoflife.date 的 Redis 版本与日期表(2026-10-27 更新)
一、这两天到底发布了什么
Redis 8.10.3 的发布说明很短,头部三段可以直接逐字引:
Redis 8.10.3 Released Mon 26 Oct 2026 10:00:00 IST ================================================================================ Update urgency: `SECURITY`: There are security fixes in the release. ### Security fixes - Heap buffer overflow when propagating a `PUBLISH`/`SPUBLISH` message larger than 4GB over the cluster bus in cluster mode一句话里有六个限定词:堆缓冲区溢出(heap buffer overflow)、传播过程(propagating)、PUBLISH/SPUBLISH消息、大于 4GB、经集群总线(over the cluster bus)、集群模式(in cluster mode)。安全修复只有这一条,没有第二条。
Valkey 侧的公告是同一天,编号GHSA-f4q9-j22m-xqq9,措辞如下:
Fix a heap buffer overflow in cluster Pub/Sub message construction that could allow an authenticated client to potentially execute arbitrary code by publishing an oversized message
修复同时进了三条产品线:Valkey 9.1.3、9.0.7、8.0.12,发布日期同为 2026 年 10 月 26 日;对应 PR 为#5030。
再看 Redis 的版本表,会发现一件更有信息量的事——同一天新增补丁的不止一条产品线:
产品线 | 当日新增版本 | 日期 | 安全支持状态 |
Redis 8.10 | 8.10.3 | 2026-10-26 | 是(当前稳定) |
Redis 8.8 | 8.8.4 | 2026-10-26 | 是(维护中) |
Redis 8.6 | 8.6.8 | 2026-10-26 | 是(维护中) |
Redis 8.4 | 8.4.8 | 2026-10-26 | 是(维护中) |
Redis 8.2 | 8.2.11 | 2026-10-26 | 是(安全支持) |
Redis 7.4 | 7.4.12 | 2026-10-26 | 是(安全支持) |
Redis 7.2 | 7.2.17 | 2026-10-26 | 是(安全支持) |
七条线同一天打补丁,说明有问题的这段代码在多条线里共享,存在的时间也不短。这也是为什么「我们用的是 7.2,应该没事」这类判断不成立。
二、触发条件逐条拆:五个条件缺一不可
官方给的触发条件,可以拆成一个五项的与门:
几点容易被读错的细节:
「集群总线」不是「数据复制链路」。集群总线是节点之间用于 gossip、故障检测、迁移协商、以及 pub/sub 扇出的那条独立通道,端口通常是客户端端口 + 10000。它和主从复制的数据链路不是一回事。这条链路在规范配置里不该暴露到公网或业务网。
PUBLISH与SPUBLISH是两种 pub/sub 语义。前者是集群范围广播,后者是分片(shard)范围内的广播。两者都在公告描述里,说明问题出在「构造要送出去的总线消息」这个共同环节,而不是某一种订阅形式。
两家的前置条件描述不一致。Valkey 的公告写了authenticated client(经过认证的客户端);Redis 的发布说明只写了漏洞本体,没有描述前置认证状态。这两个口径不能互相替代——看到 Valkey 写了「需要认证」,就推断 Redis 侧也一样从而放松内网策略,是不严谨的做法。正确做法是按各自公告的最小值执行:[未被官方说明的部分,一律按更保守的前提处理]。
三、4GB 这个数字为什么眼熟
4GB 不是随手写的「一个很大的值」。它不是 GiB 的整数典故,而是 32 位无符号整数的天花板:
2^32 = 4,294,967,296字节 = 4 GiB
- 一个
uint32长度字段能表示的范围是0 … 2^32 - 1
- 一旦真实长度越过
2^32,写进 32 位字段的值会被截断成一个远小于真实值的数
推论部分需要明确标注:Redis 与 Valkey 的公告都没有给出根因分析,也没有说明是哪一个长度字段、哪一段内存分配出了问题。下面这句是工程推断,不是官方结论——「按截断后的长度分配缓冲区,再按真实长度写入,就会写越界」。把这个推断写在纸上是为了让排查方向清楚,不能拿它当成事实引用。真正准确的公开事实只有一条:在官方描述的五个条件下,会发生堆缓冲区溢出。
四、动手验证:把能算的部分算出来
漏洞本体不需要也不是我们能复现的。但有三件事是可以直接算出来的,它们决定了你的排查顺序。脚本_脚本/redis_cluster_bus_4gb.py零依赖,本机实跑(Windows,Python 3.13.12)输出如下(节选):
第 1 段:2^32 边界
真实消息长度 | 真实长度 | 按 uint32 截断后 | 分配缺口 ---------------------------------------------------------------------------------- 2^32 - 2 B | 4.00 GiB | 4.00 GiB | 0.00 B 2^32 - 1 B | 4.00 GiB | 4.00 GiB | 0.00 B 2^32(恰好) | 4.00 GiB | 0.00 B | 4.00 GiB <- 越过 4 GiB 边界 2^32 + 1 B | 4.00 GiB | 1.00 B | 4.00 GiB <- 越过 4 GiB 边界 2^32 + 4 KiB | 4.00 GiB | 4.00 KiB | 4.00 GiB <- 越过 4 GiB 边界 2^32 + 1 GiB | 5.00 GiB | 1.00 GiB | 4.00 GiB <- 越过 4 GiB 边界 2^33(两倍) | 8.00 GiB | 0.00 B | 8.00 GiB <- 越过 4 GiB 边界跨越边界之后,截断值直接掉到一个极小的数量级。「4GB 边界」在这类内存安全问题里通常意味着:长度口径虽然是 64 位,但中间某个环节把它塞进了 32 位。
第 2 段:集群总线扇出
一条 4 GiB 的PUBLISH在全互联的总线拓扑里要被扇出到其余每一个节点,搬运总量是(节点数 - 1) × 消息大小:
集群节点数 | 总线搬运总量 | 1 GbE | 10 GbE | 25 GbE | 40 GbE |
3 | 8.00 GiB | 76.4 s | 7.6 s | 3.1 s | 1.9 s |
6 | 20.00 GiB | 3.2 min | 19.1 s | 7.6 s | 4.8 s |
9 | 32.00 GiB | 5.1 min | 30.5 s | 12.2 s | 7.6 s |
12 | 44.00 GiB | 7.0 min | 42.0 s | 16.8 s | 10.5 s |
30 | 116.00 GiB | 18.5 min | 1.8 min | 44.3 s | 27.7 s |
100 | 396.00 GiB | 63.0 min | 6.3 min | 2.5 min | 1.6 min |
(脚本假定链路有效吞吐为线速的 90%,只用于量级估算。)
这张表的用法不在于「攻击要多久」,而在于爆炸半径:节点越多,一条超大消息占满总线的时间越长,这段时间里 gossip、心跳、故障检测、槽迁移消息全部在排队。对一个 30 节点以上的集群来说,即便不考虑内存安全本身,把单条 pub/sub 消息做到 GiB 量级也已经是自伤行为。
第 3 段:配置自查
proto-max-bulk-len 取值 | 单个 bulk 上限 | 能否越过 4 GiB 边界 ------------------------------------------------------------------------------ 512.00 MiB | 512.00 MiB | 不能 1.00 GiB | 1.00 GiB | 不能 2.00 GiB | 2.00 GiB | 不能 4.00 GiB | 4.00 GiB | 不能 8.00 GiB | 8.00 GiB | 能proto-max-bulk-len限制的是客户端协议里单个 bulk string 的大小,Redis 默认值是 536870912 字节(512 MiB)。它是「多大的消息能被塞进来」的第一道闸门。
需要说清楚的一句:官方没有说明超限消息是通过哪条路径构造出来的——是单个超大 bulk,还是内部构造总线消息时的累积结果。因此这一段只用于回答「我的配置有没有为超大消息开门」,不能反过来推出「只要 proto-max-bulk-len 是 512MB 就绝对无风险」。
五、常见误区
误区一:我们没用 pub/sub,所以不用升级。补丁是发布级的,发布里除安全修复外还有一批常规修复;更重要的是,修复后能不能构造出超限消息这件事,对新行为是全局约束。按产品线版本直接升级,不做「选择性升级」。
误区二:这是 Redis 的问题,我们用的是 Valkey。两个项目这次修的是同一类代码问题(Valkey 三条产品线同日发布补丁)。用 Valkey 的团队要升级到 9.1.3 / 9.0.7 / 8.0.12 之一。
误区三:只看 Valkey 公告里的「认证过的客户端」。前文说过,两家的前置条件描述不一致。内网 + 弱口令 + PUBLISH 权限的组合,在很多部署里是存在的。
误区四:把单实例的maxmemory当成防线。maxmemory管的是数据集,不管单条协议消息的构造过程,也不管集群总线的缓冲区。
误区五:只升级主节点。集群总线是全互联的,任何一个未升级节点都保留着有问题的那一段代码。
六、排查与缓解清单(可直接进变更单)
# | 动作 | 判定 / 目的地 |
1 | 列出全部集群实例的版本 | 目标:Redis ≥ 8.10.3 / 8.8.4 / 8.6.8 / 8.4.8 / 8.2.11 / 7.4.12 / 7.2.17;Valkey ≥ 9.1.3 / 9.0.7 / 8.0.12 |
2 | 检查 | 若被调到 4 GiB 以上,回滚到业务需求的最小值并记录原因 |
3 | 检查集群总线端口(客户端端口 + 10000)网络可达性 | 不该对公网、也不该对整个业务网开放 |
4 | 审计有 | 收缩到必要的用户与必要的 channel 前缀 |
5 | 确认 pub/sub 消息体的业务上限 | 在应用层加一道大小校验,别让总线成为唯一的闸口 |
6 | 升级后回归 | 至少覆盖集群广播、跨节点订阅、故障转移期间的订阅连续性 |
顺序建议:先 2、3、4(不改版本,成本低),再排窗口做 1,最后 5 进业务迭代。
七、适用边界与产业坐标
本文的结论建立在两家官方公告的原文之上,不包含任何自己构造的攻击样本,也没有复现过程。官方未给出 CVE 编号(Redis 侧发布说明未引用 CVE;Valkey 侧用 GHSA 编号[本文写成时未见对应 CVE 编号,以官方更新为准]),也未说明根因——凡是涉及「为什么坏」的部分,上文已明确标为推断。
把视角从补丁拉回工程:真正容易被忽略的不是这一条 CVE,而是企业级缓存部署里 pub/sub 通常没有被当成一等公民治理。它对业务方是「一个广播功能」,对运维方是「一条独立通信平面的持续负载」,权限往往跟 Redis 用户一起被整体授予,消息大小没有上限,网络暴露也没有单独审查过。一旦铺开到几十个节点的集群,边界在哪里没人说得清。
回到国内环境的约束:这类组件替换通常要在给定的信创软硬件生态里完成,替换对象往往是存量 Redis 集群,且要求业务代码不改调用语义。金蝶天燕 Apusic 这条线上,分布式缓存 AMDC 承接的是会话与热点数据的缓存语义,ADMQ 承接的是消息流本身;对运维来说要回答的问题不是「能不能广播」,而是广播发生在哪一层、上限设在哪里、谁有权调用——把这件事写进规范,比等下一个补丁更有用。具体产品的能力边界以各自的官方文档为准。
八、结论
- 2026 年 10 月 26 日,Redis 七条产品线同日发布安全补丁,Valkey 三条产品线同步修复
GHSA-f4q9-j22m-xqq9,两者描述的是同一类问题:集群模式下经集群总线传播大于 4GB 的PUBLISH/SPUBLISH消息导致堆缓冲区溢出。
- 触发条件是五项的与门:集群模式、集群总线、PUBLISH/SPUBLISH、传播过程、大于 4GB。逐条核一遍比整体判断「我们用不用」靠谱。
- 4 GiB = 2^32 字节,是 32 位长度字段的天花板;根因官方未说明,相关的推断部分已在文中标出。
- 六节点集群扇出一条 4 GiB 消息要搬 20 GiB,在 10 GbE 上约 19 秒——这段时间总线是堵的。
- 两家前置条件口径不一致(Valkey 明确写了 authenticated client,Redis 未说明),按更保守的前提处理。
问一句:你们集群的proto-max-bulk-len现在是多少,集群总线端口有没有做过单独的网络隔离?如果这两个问题的答案需要翻文档才知道,建议今天就把它们写进配置基线。