AMD 的随机数生成器,永远吐不出一个 0
目录
- 一台 AMD,两年没见过 0
- 0 到底有多稀有:先算一笔概率账
- 现场验证:我在 Intel 上替 AMD 把这场实验跑了
- 从「全是 1」到「没有 0」:AMD RNG 的 Bug 家族史
- 最讽刺的一周:Zen 5 的 RDSEED 正在疯狂吐 0
- 所以,一个「永远不输出 0」的 RNG 意味着什么
一台 AMD,两年没见过 0
2026 年 5 月,巴西的汇编语言程序员 Jessé 在 flat assembler 论坛上发了一帖,标题平平无奇:「AMD 的随机数生成器无法生成 0 吗?」
他写了一个小程序,用 rdrand 和 rdseed 指令疯狂抽取随机数,然后统计 0、1、-1 出现的次数。结果让他愣了很久:在 AMD 上,16 位宽的随机数里,0 一次都没出现过。同样的代码换到 Intel 上,0 的出现完全正常。
他在帖子里说:从发帖前 9 天开始,到他发帖那一刻,两台 AMD 处理器,没有吐出一个 0。
这个帖子的标题在 9 月 22 日登上了 Hacker News 首页,230 分,171 条讨论。但真正让我感兴趣的不是「AMD 出了 bug」这个结论——毕竟芯片厂商出 bug 不稀奇。我感兴趣的是:一个「永远不输出 0」的随机数生成器,到底是怎么通过验证的?
0 到底有多稀有:先算一笔概率账
先别急着嘲讽 AMD。0 在随机数里出现的概率,比你想象的低得多。
- 16 位随机数:范围 0~65535,出现 0 的概率是 1/65536 ≈ 0.0015%
- 32 位随机数:概率是 1/2³² ≈ 0.00000002%
- 64 位随机数:概率是 1/2⁶⁴,小到约等于 0
也就是说,如果你想验证「32 位 RNG 能不能输出 0」,你需要平均抽 42.9 亿次才能遇到一次。这已经超出了任何常规测试的耐心范围。
Jessé 的帖子之所以能测出问题,是因为他用了 16 位窗口——1/65536 的概率在几千万次抽样里是完全可测的。换成 32 位,他跑几百年也未必能等到一个 0。
这解释了为什么这个 bug 能藏这么久:你测的那个「窗口」越大,0 就越稀有,bug 就越难暴露。 硬件验证团队跑完测试套件,全绿,收工——因为他们的测试根本没跑够能撞见 0 的次数。
现场验证:我在 Intel 上替 AMD 把这场实验跑了
我的服务器是 Intel Xeon Platinum 8255C,支持 rdrand 和 rdseed。我把 Jessé 的测试逻辑用 C 重写了一遍,用 Intel 内联函数(不是汇编)跑了完整的 6 组实验:
- 16 位
rdrand:2000 万次抽取,285 个 0,比率 1.425e-5,和理论的 1/65536 = 1.526e-5 几乎完全吻合 - 16 位
rdseed:760 万次成功抽取,119 个 0,比率 1.564e-5,同样吻合 - 32 位:4000 万次,0 个 0 —— 但这正常,因为期望值本来就只有 0.009 次
- 64 位:4000 万次,0 个 0 —— 同样正常,期望值 2e-10 次
我的实验证明了三件事:
- Intel 的 RNG 在 16 位窗口上完全符合均匀分布,0 出现的频率和理论值分毫不差
- Jessé 的实验方法是对的:16 位窗口确实能把「0 是否出现」变成一个可测的问题
- 32/64 位看不到 0 是正常的,不是 bug——真正的问题只在 16 位这个「放大镜」下才现形
我还顺手跑了他提供的那个 random-fryer 可执行文件,2 秒内就看到了 16 位的 0 出现,程序打印出「CPU has passed 'zero generate' test」。
从「全是 1」到「没有 0」:AMD RNG 的 Bug 家族史
这不是 AMD RNG 第一次在「0」上翻车。
2020 年,AMD Zen 2 架构(Ryzen 3000 系列)的处理器被发现一个更离谱的问题:rdrand 指令会持续返回 -1(也就是 0xFFFF,全是 1),导致一些应用程序一启动就崩溃。这个 bug 最后靠微码更新修复。
Hacker News 上一位用户 jstanley 的评论一针见血:
「这是 Zen 2 上第一个 RNG bug。我记得刚买回来的时候,某个应用一启动就退出,因为 rdrand 总是返回 -1,全是 1。后来用微码更新修好了。所以我们现在学到的是:他们把『总是生成全是 1』修成了『永远不生成全是 0』??」
这句吐槽扎心的地方在于:从「全是 1」到「没有 0」,可能只是同一个修复动作的另一面。 硬件工程师修 bug 时,最省事的办法往往是在输出端加一个「如果等于某个值就重试」的过滤器。如果那个值恰好是 0xFFFF,你修好了「全是 1」,却可能顺手把「0」也一起过滤掉了。
评论里还有一条更意味深长的:「或许 AMD 认为,加密代码跳过 0 更安全。」 —— 数学上 0 和其他数出现的概率完全一样,但人类对 0 有非理性的恐惧。一个「从不输出 0」的 RNG 在密码学上毫无问题(0 和其他值一样不可能被预测),但在心理上,它看起来就像「坏了」。
最讽刺的一周:Zen 5 的 RDSEED 正在疯狂吐 0
就在 Jessé 的帖子在 Hacker News 上发酵的同一周——2026 年 9 月 21 日——AMD 发布了一份安全公告 AMD-SB-7055:
Zen 5 处理器的
RDSEED指令可能以「与随机性不一致的速率」返回 0,同时错误地报告成功(CF=1),这可能把失败误判为成功。16 位和 32 位的 RDSEED 受影响,64 位不受影响。
等等,你没看错:AMD 一边有处理器永远吐不出 0,一边有处理器疯狂吐 0。
Zen 5 的 RDSEED bug 是另一个方向的问题——它不只是「0 出现得太少」,而是「0 出现得太多,多到不像随机」。AMD 的缓解方案很直白:
- 用 64 位形式的 RDSEED
- 或者干脆从 CPUID 里把 RDSEED 藏起来(
clearcpuid=rdseed) - 或者把「返回 0 且 CF=1」当成失败处理,重试直到拿到非零值
最讽刺的是:Zen 5 的缓解建议,恰好就是「重试直到非零」——而这正是 Zen 2 时代「永远不输出 0」的 bug 可能采用的修复方式。 AMD 的 RNG 家族史,像极了在「过滤 0」和「过滤非 0」之间反复横跳。
所以,一个「永远不输出 0」的 RNG 意味着什么
先说结论:它在密码学上几乎毫无影响。
现代系统的随机数生成是分层结构:rdrand/rdseed 只是熵源,真正给应用程序用的是内核的 CSPRNG(如 Linux 的 /dev/urandom)。CSPRNG 会从硬件熵源取种子,然后自己用密码学算法扩展。就算硬件 RNG 永远不输出 0,CSPRNG 输出的随机流依然是均匀的——因为 0 缺失的信息,被算法重新均匀化了。
正如 Hacker News 上 CodesInChaos 的评论所说:「很尴尬,但实际影响可能很小,因为这些硬件随机数通常不会直接使用,而是作为 CSPRNG 的种子。」
但这件事依然值得在意,原因有两个:
第一,它暴露了硬件验证的盲区。 一个「永远不输出 0」的 RNG 通过了所有标准测试——因为 Dieharder、TestU01 这些测试套件跑的是统计分布,而「0 从未出现」在 32 位窗口下要跑几十亿次才能被察觉。「0 缺失」这种结构性缺陷,统计测试抓不到,只有直方图能抓到。 Hacker News 上 hnacobsxph 的评论印证了这一点:「我在 KDF 里追过类似的 bug,只有把 16 位抽取画成直方图才抓到,统计套件从来没报警。」
第二,它提醒我们:随机性不是「看起来随机」,而是「可验证的随机」。 一个永远吐不出 0 的 RNG,和 NS 的预知能力无关,和阴谋论无关——它就是一个结构性的、可复现的、被直方图暴露的缺陷。而它之所以能存在这么久,恰恰因为验证随机性的工具,本身就不够随机地检查随机性。
Jessé 的帖子已经过去 4 个月,AMD 的回应是「内部升级了案例,等待更新」。而这一周,AMD 又发布了另一个方向的 RDSEED bug 公告。
一台永远吐不出 0 的芯片,和一台疯狂吐 0 的芯片,可能出自同一个设计团队之手。它们之间的共同点不是「AMD 很菜」,而是:随机数生成器是整个芯片上最容易被「看起来没问题」骗过的地方。
评论(0)
暂无评论,来写第一条吧~