From abc85fbfad0c5d0ea9f24fd2bc383beba85ad5a8 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Jun 2019 18:46:29 -0700 Subject: [PATCH] Alternative way to detect AMD bug --- src/rdrand.rs | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/rdrand.rs b/src/rdrand.rs index addd9f0e..0e3c60b4 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -23,13 +23,6 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { for _ in 0..RETRY_LIMIT { let mut el = mem::uninitialized(); if _rdrand64_step(&mut el) == 1 { - // AMD CPUs from families 14h to 16h (pre Ryzen) will sometimes give - // bogus random data. Discard these values and warn the user. - // See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505 - if cfg!(not(target_env = "sgx")) && (el == 0 || el == !0) { - error!("RDRAND returned suspicious value {}, CPU RNG is broken", el); - return Err(Error::UNKNOWN) - } return Ok(el.to_ne_bytes()); } } @@ -43,25 +36,53 @@ compile_error!( "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd." ); -#[cfg(target_feature = "rdrand")] +#[cfg(target_env = "sgx")] fn is_rdrand_supported() -> bool { true } // TODO use is_x86_feature_detected!("rdrand") when that works in core. See: // https://github.com/rust-lang-nursery/stdsimd/issues/464 -#[cfg(not(target_feature = "rdrand"))] +#[cfg(not(target_env = "sgx"))] fn is_rdrand_supported() -> bool { - use core::arch::x86_64::__cpuid; use lazy_static::lazy_static; - // SAFETY: All x86_64 CPUs support CPUID leaf 1 - const FLAG: u32 = 1 << 30; lazy_static! { - static ref HAS_RDRAND: bool = unsafe { __cpuid(1).ecx & FLAG != 0 }; + static ref HAS_RDRAND: bool = has_rdrand(); } *HAS_RDRAND } +#[cfg(not(target_env = "sgx"))] +pub fn has_rdrand() -> bool { + use core::arch::x86_64::__cpuid; + // SAFETY: All x86_64 CPUs support CPUID leaf 0 and 1 + let leaf_0 = unsafe { __cpuid(0) }; + let vendor_id: [u8; 12] = unsafe { mem::transmute([leaf_0.ebx, leaf_0.edx, leaf_0.ecx]) }; + let is_amd = &vendor_id == b"AuthenticAMD"; + + if cfg!(target_feature = "rdrand") && !is_amd { + return true; + } + let leaf_1 = unsafe { __cpuid(1) }; + + // Early AMD CPUs have a broken RDRAND. They return nonrandom data when + // resuming from sleep. We will ignore RDRAND support on such platfroms. + // See: https://github.com/systemd/systemd/issues/11810#issuecomment-489727505 + if is_amd { + let mut family_id = (leaf_1.eax & 0x00000f00) >> 8; + if family_id == 0xF { + let extended_id = (leaf_1.eax & 0x0ff00000) >> 20; + family_id += extended_id; + } + // This issue is only present before Zen (family 17h). + if family_id < 0x17 { + return false; + } + } + const RDRAND_FLAG: u32 = 1 << 30; + leaf_1.ecx & RDRAND_FLAG != 0 +} + pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { if !is_rdrand_supported() { return Err(Error::UNAVAILABLE);