内容提要
Valkey官方DEB包因沿用Debian补丁,误用zfree释放普通malloc内存,导致SAVE失败时内存统计回绕或崩溃,影响五条版本线。上游源码无此问题,但打包流水线未运行上游测试。作者已提交修复并删除多余补丁,强调打包是最后质量防线,Agent应多做验证而非造新代码。
延伸解读
打包环节如何引入上游不存在的 Bug
Valkey 官方 DEB 包中的 Bug 并非来自上游源码,而是打包时沿用了 Debian 的补丁。该补丁将栈缓冲区改为堆分配,却错误使用 zfree() 释放普通 malloc 内存,导致内存统计错误或崩溃。这说明用户实际运行的软件是上游源码、下游补丁、编译参数和依赖库共同作用的产物,打包是对软件的最后一次改写,可能制造出上游根本没有的问题。
不同构建方式导致截然不同的故障表现
同一份错误补丁,在链接系统 jemalloc 的官方 DEB 包中,表现为内存统计静默回绕,used_memory 可能变成天文数字并触发 OOM;而在使用内置私有 jemalloc 的构建中,第一次失败的同步 SAVE 就可能直接导致段错误崩溃。这提醒读者,编译参数、依赖库和分配器选择会显著改变程序行为,不能仅以源码版本判断实际风险。
测试接缝:上游与打包流水线各测各的
上游 CI 测试的是没有该补丁的源码,打包流水线只检查安装、启动等包装盒完整性,却注释掉了上游功能回归测试。于是 Bug 正好活在两套质量体系之间:上游 CI 绿,打包 CI 也绿,官方包照常发布。这揭示了打包测试的盲区——包级安装测试通过,不等于软件功能正确,必须运行上游回归测试才能覆盖冷门错误路径。
修复思路:删除补丁比修正补丁更彻底
技术上的最小修复是把 zfree(cwdp) 换成 zlibc_free(cwdp),但维护者进一步质疑该补丁是否必要。作者随后在 Pigsty 的 Valkey 和 Redis 包中直接删除整份 Debian 补丁,重新构建并运行测试,用户可见行为没有变化,问题也随补丁消失。这提示:最安全的补丁往往是根本不需要维护的补丁,每份 patch 都应被当作生产代码审查,并明确其存在理由和删除时机。
Q&A
Valkey 官方 DEB 包为什么会出现上游源码没有的 Bug?
因为官方打包流水线从 Debian 引入了打包补丁,其中包含一个错误的 zfree 调用,用于释放普通 malloc 分配的内存,导致内存统计错误或崩溃。
这个 Bug 会导致什么具体现象?
同步 SAVE 失败时,used_memory 会减少,多次失败后回绕到接近 2^64 的天文数字,触发 OOM;若使用内置 jemalloc,则第一次失败就可能段错误崩溃。
哪些 Valkey 版本和平台受到影响?
影响 Valkey 7.2、8.0、8.1、9.0、9.1 五条官方 DEB 产品线,在 Debian 12、Debian 13、Ubuntu 22.04、Ubuntu 24.04 及两种 CPU 架构上均可复现。RPM 和上游源码不受影响。
为什么上游测试和打包测试都没有发现这个 Bug?
上游源码没有这份补丁,所以上游 CI 测不到;打包流水线虽然做了安装、启动等测试,但跳过了上游功能回归测试,因此 Bug 活在两套质量体系之间。
这个 Bug 最终是如何被发现和修复的?
作者在 Pigsty 打包时运行上游 runtest,unit/shutdown 测试失败并崩溃,定位到 debian/patches 中的 zfree(cwdp)。修复方式是将 zfree 替换为 zlibc_free,并最终删除整份 Debian 补丁。
作者对 Agent 在软件测试中的角色有什么看法?
作者认为 Agent 最该做的是反复验证、交叉构建、复现问题,而不是生产更多未经验证的代码。Agent 不嫌烦,能把过去性价比低的质量工作做到底。