cpu 支持 rdrand 需通过 cpuid 检查 eax=1 时 ecx[30] 位,不可直接调用;内联汇编或内置函数均须检查 cf 或返回值判断成功与否,失败时结果未定义。

怎么确认 CPU 支持 RDRAND
不查直接用会触发 #UD(无效指令异常),程序崩得悄无声息。必须先用 cpuid 检测,且不能只看 EAX=1 的返回值——RDRAND 标志在 ECX 的第 30 位(ECX.RDRAND[bit 30]),不是靠编译器自动加的。
- 调用
cpuid前必须设EAX = 1,否则 ECX 不含该位信息 - 别信
__builtin_ia32_rdrand32_step的文档说“自动检测”,它不检查,只管执行 - Linux 下可用
cat /proc/cpuinfo | grep rdrand快速验证,但生产代码里不能依赖这个
用内联汇编安全读取 RDRAND 值
直接写 rdrand %eax 很危险:失败时寄存器内容未定义,且 CF 标志才表示成功,不是返回值。必须检查标志位,再决定是否使用结果。
- 32 位用
rdrand %eax,64 位推荐rdrand %rax(避免高位残留) - 必须用
jc(jump if carry)判断 CF,不能用test %eax,%eax——失败时 %eax 可能非零 - GCC 内联汇编要显式声明 clobber:
"=a"(val), "=&c"(cf)+"cc",否则优化可能破坏 CF
int rdrand32(uint32_t *out) {
unsigned char ok;
__asm__ volatile("rdrand %0; setc %1"
: "=a"(*out), "=c"(ok)
: : "cc");
return ok;
}
用 Intel 内置函数更简洁但有陷阱
__builtin_ia32_rdrand32_step 看起来省事,但它返回 int(非零=成功),且不保证生成的值在 [0, UINT32_MAX] 范围内——实际就是原样返回寄存器值,失败时内容任意。
- 必须检查返回值,不能只取结果变量
- Clang 和 GCC 行为一致,但老版本 GCC(undefined reference
- 启用需加编译选项
-mrdrnd,否则即使 CPU 支持也会链接失败 - 不要和
-march=native混用——它可能把 RDRAND 当成默认指令,导致在不支持的机器上崩溃
为什么不能无条件 fallback 到 rand() 或 /dev/urandom
RDRAND 是硬件熵源,设计目标是高吞吐、低延迟、抗侧信道;而软件 PRNG(如 rand())或系统熵池(如 /dev/urandom)有完全不同的安全模型和性能曲线。混用反而引入不确定性。
立即学习“C++免费学习笔记(深入)”;
- 若 RDRAND 不可用,应明确报错或走预设降级路径,而不是静默切到
rand() - Linux 上
getrandom(2)是更好 fallback,但需检查ENOSYS和EAGAIN - 反复调用 RDRAND 失败(比如连续 10 次)大概率说明硬件故障或被禁用(BIOS 关闭、
clearcpuid=rdseed,rdrand内核参数),该停就停










