AMD 的随机数生成器不能生成 0?
AMD CPU 似乎存在一个 bug:某些代际的硬件随机数指令(尤其是 Zen 2 `rdrand16`)几乎不会真正生成 0,或者把 0 输出标记成失败,这引发了人们对本应均匀随机的数值是否存在偏差的质疑。评论者指出,大多数系统并不会直接使用这些硬件随机数,而是把它们作为更强的软件 RNG 的种子;现代内核也会混合多个熵源,因此单一有缺陷的来源通常不至于灾难性。不过,历史表明,即使是很小的 RNG 偏差也可能演变成真正的安全漏洞。讨论还扩展到了是否应信任厂商实现的黑盒 RNG、内核如何防范不良或恶意的熵源,以及在用户态自行编写密码学 RNG 的风险。
AMD RNG bug 的性质
- 报告主要集中在 Zen 1/2 时代的 CPU 上,其中
RDRAND16似乎从不返回 0,而RDRAND32/64会。 - 更仔细的测试显示,
RDRAND16确实 可以在内部生成 0,但当它这样做时,进位标志(CF)会被置为 0,表示“失败 / 重试”。- 通常的封装会丢弃这些结果并循环,因此用户代码实际上从未看到过 16 位的 0。
- CF=0“失败”的速率与 1/65536 一致,且所有这类失败都返回 0,这表明这更像是一种系统性行为,而不只是单纯缺少 0。
- 更新一代(Zen 3/4)在测试中没有显示这个问题;Zen 5 有一个相关的 RDSEED 勘误,其中全零结果并且 CF=1 可能导致类似的规避做法,从而重新引入“从不为 0”的行为。
与过去硬件 RNG 问题的关系
- 早期 Zen 2 微码 bug 曾导致
RDRAND总是返回全 1,直到更新后才修复。 - 有人担心微码“修复”可能把一种错误模式(全 1)换成了另一种(没有 0)。
- 一些系统运维人员因为这类 AMD 故障而在 Linux 内核中禁用了对 CPU RNG 的信任。
安全影响与随机性质量
- 一种观点:实际影响很小,因为:
- 硬件 RNG 通常只用于为 CSPRNG(如内核池)提供种子。
- 少掉 2^16(或 2^32/2^64)个值中的 1 个,几乎不会缩小密钥空间。
- 另一种观点:任何系统性偏差都是危险信号:
- 密码学历史表明,小偏差在某些协议中可能会变得可利用。
- 即使这个特定偏差大概率无害,轻视这类缺陷也被认为是有风险的。
- 线程还回到基础概念:
- 期望的输出是 离散均匀 分布;“从不为 0”就是偏差。
- 可预测性不只是“不是总是同一个值”,而是“没有任何值或谓词被偏袒”。
混合熵与对硬件的信任
- 大体共识:不要依赖单一、黑盒的硬件 RNG;应通过强构造(CSPRNG、sponge 或 XOF)混合多个来源。
- 争论点在于:
- 一个“有问题”但非恶意的来源,是否可能让一个好的池子变得更差(多数人认为不会;也有人引用这样的攻击:一个能看到其他输入的恶意来源可以偏置输出)。
- 与其自己在用户态实现 RNG,不如始终使用内核接口(
/dev/urandom、getentropy/getrandom)是否更好。
- 有人强烈反对自定义 RNG(现实世界失败案例很多);也有人为成熟、经过长期验证的自定义 XOF 设计辩护,尤其是在跨平台或嵌入式场景中。
验证、测试,以及它为何会漏过去
- 几位评论者对这样一个简单的分布 bug 竟然通过了硬件验证感到惊讶;推测包括:
- 验证计划不完整(没有专门覆盖 0 的 bin)。
- 越界一位之类的错误。
- 实用说明:对全部 65,536 个 16 位输出做直方图统计,是一种很容易的黑盒测试,应该能抓住这类错误。