Skip to content

Commit abc85fb

Browse files
committed
Alternative way to detect AMD bug
1 parent e992e45 commit abc85fb

1 file changed

Lines changed: 34 additions & 13 deletions

File tree

src/rdrand.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> {
2323
for _ in 0..RETRY_LIMIT {
2424
let mut el = mem::uninitialized();
2525
if _rdrand64_step(&mut el) == 1 {
26-
// AMD CPUs from families 14h to 16h (pre Ryzen) will sometimes give
27-
// bogus random data. Discard these values and warn the user.
28-
// See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
29-
if cfg!(not(target_env = "sgx")) && (el == 0 || el == !0) {
30-
error!("RDRAND returned suspicious value {}, CPU RNG is broken", el);
31-
return Err(Error::UNKNOWN)
32-
}
3326
return Ok(el.to_ne_bytes());
3427
}
3528
}
@@ -43,25 +36,53 @@ compile_error!(
4336
"SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd."
4437
);
4538

46-
#[cfg(target_feature = "rdrand")]
39+
#[cfg(target_env = "sgx")]
4740
fn is_rdrand_supported() -> bool {
4841
true
4942
}
5043

5144
// TODO use is_x86_feature_detected!("rdrand") when that works in core. See:
5245
// https://github.com/rust-lang-nursery/stdsimd/issues/464
53-
#[cfg(not(target_feature = "rdrand"))]
46+
#[cfg(not(target_env = "sgx"))]
5447
fn is_rdrand_supported() -> bool {
55-
use core::arch::x86_64::__cpuid;
5648
use lazy_static::lazy_static;
57-
// SAFETY: All x86_64 CPUs support CPUID leaf 1
58-
const FLAG: u32 = 1 << 30;
5949
lazy_static! {
60-
static ref HAS_RDRAND: bool = unsafe { __cpuid(1).ecx & FLAG != 0 };
50+
static ref HAS_RDRAND: bool = has_rdrand();
6151
}
6252
*HAS_RDRAND
6353
}
6454

55+
#[cfg(not(target_env = "sgx"))]
56+
pub fn has_rdrand() -> bool {
57+
use core::arch::x86_64::__cpuid;
58+
// SAFETY: All x86_64 CPUs support CPUID leaf 0 and 1
59+
let leaf_0 = unsafe { __cpuid(0) };
60+
let vendor_id: [u8; 12] = unsafe { mem::transmute([leaf_0.ebx, leaf_0.edx, leaf_0.ecx]) };
61+
let is_amd = &vendor_id == b"AuthenticAMD";
62+
63+
if cfg!(target_feature = "rdrand") && !is_amd {
64+
return true;
65+
}
66+
let leaf_1 = unsafe { __cpuid(1) };
67+
68+
// Early AMD CPUs have a broken RDRAND. They return nonrandom data when
69+
// resuming from sleep. We will ignore RDRAND support on such platfroms.
70+
// See: https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
71+
if is_amd {
72+
let mut family_id = (leaf_1.eax & 0x00000f00) >> 8;
73+
if family_id == 0xF {
74+
let extended_id = (leaf_1.eax & 0x0ff00000) >> 20;
75+
family_id += extended_id;
76+
}
77+
// This issue is only present before Zen (family 17h).
78+
if family_id < 0x17 {
79+
return false;
80+
}
81+
}
82+
const RDRAND_FLAG: u32 = 1 << 30;
83+
leaf_1.ecx & RDRAND_FLAG != 0
84+
}
85+
6586
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
6687
if !is_rdrand_supported() {
6788
return Err(Error::UNAVAILABLE);

0 commit comments

Comments
 (0)