Skip to content

Commit

Permalink
Alternative way to detect AMD bug
Browse files Browse the repository at this point in the history
  • Loading branch information
josephlr committed Jun 29, 2019
1 parent e992e45 commit abc85fb
Showing 1 changed file with 34 additions and 13 deletions.
47 changes: 34 additions & 13 deletions src/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Expand All @@ -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);
Expand Down

0 comments on commit abc85fb

Please sign in to comment.