ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Redis『4GB 边界』:集群总线 pub/sub 堆溢出该怎么查

Redis『4GB 边界』:集群总线 pub/sub 堆溢出该怎么查

一句话结论:这次两家同时修的漏洞,触发条件写得非常具体——集群模式、集群总线、PUBLISH/SPUBLISH、传播过程、消息大于 4GB,五个条件缺一不可;真正要动手的不是「有没有用 pub/sub」,而是五个条件在你的拓扑里能不能同时成立。

项

内容

摘要

10 月 26 日 Redis 与 Valkey 同日发布安全版,修的都是「集群模式下经集群总线传播超过 4GB 的 PUBLISH 消息导致堆溢出」。本文逐条拆官方公告的触发条件,指出两家用语上的差异,用零依赖脚本把 4GB 边界、集群总线扇出量与配置自查项算成可直接执行的清单。

前置知识

Redis Cluster 的基本形态(节点间通过集群总线通信);知道 PUBLISH/SPUBLISH 与集群总线的关系;读过一次proto-max-bulk-len这类配置项

阅读时间

约 16 分钟

代码环境

Python 3.9+,零第三方依赖,不联网,输出确定(无随机源);脚本见_脚本/redis_cluster_bus_4gb.py

读完你能做什么

能在 10 分钟内判断自己集群是否落在触发条件里;能解释为什么 4GB 不是「一个建议值」而是 32 位长度字段的天花板;能给出升级之外的三条缓解路径;能在变更单里写清跨七条 Redis 产品线的一致性修复范围

一手来源有三处,本文引用的每一句话都能在其中点到:

  1. Redis Open Source 8.10 发布说明(00-RELEASENOTES,8.10.3,Released Mon 26 Oct 2026)
  1. Valkey 9.1.3 / 9.0.7 / 8.0.12 发布公告(GHSA-f4q9-j22m-xqq9,Released Mon 26 October 2026)
  1. 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

检查proto-max-bulk-len实际值

若被调到 4 GiB 以上,回滚到业务需求的最小值并记录原因

3

检查集群总线端口(客户端端口 + 10000)网络可达性

不该对公网、也不该对整个业务网开放

4

审计有PUBLISH/SPUBLISH权限的 ACL 用户

收缩到必要的用户与必要的 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现在是多少,集群总线端口有没有做过单独的网络隔离?如果这两个问题的答案需要翻文档才知道,建议今天就把它们写进配置基线。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进