Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions src/backends/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,19 @@ static RDRAND_GOOD: lazy::LazyBool = lazy::LazyBool::new();
const RETRY_LIMIT: usize = 10;

#[target_feature(enable = "rdrand")]
#[cfg_attr(target_os = "uefi", allow(unused_unsafe))] // HACK: Rust lint gives false positive on uefi
unsafe fn rdrand() -> Option<Word> {
fn rdrand() -> Option<Word> {
for _ in 0..RETRY_LIMIT {
let mut val = 0;
// SAFETY: this function is safe to call from a `[target_feature(enable
// = "rdrand")]` context (it itself is annotated with
// `target_feature(enable = "rdrand")`) but was marked unsafe until
// https://github.com/rust-lang/stdarch/commit/59864cd which was pulled
// in via https://github.com/rust-lang/rust/commit/f2eb88b which is
// expected to be included in 1.93.0. Since our MSRV is 1.85, we need to
// use unsafe here and suppress the lint.
//
// TODO(MSRV 1.93): remove allow(unused_unsafe) and the unsafe block.
#[allow(unused_unsafe)]
if unsafe { rdrand_step(&mut val) } == 1 {
return Some(val);
}
Expand All @@ -49,12 +58,12 @@ compile_error!(
// Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
// Fails with probability < 2^(-90) on 32-bit systems
#[target_feature(enable = "rdrand")]
unsafe fn self_test() -> bool {
fn self_test() -> bool {
// On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision.
let mut prev = Word::MAX;
let mut fails = 0;
for _ in 0..8 {
match unsafe { rdrand() } {
match rdrand() {
Some(val) if val == prev => fails += 1,
Some(val) => prev = val,
None => return false,
Expand Down Expand Up @@ -102,49 +111,48 @@ fn is_rdrand_good() -> bool {
unsafe { self_test() }
}

// TODO: make this function safe when we have feature(target_feature_11)
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> {
fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> {
// We use chunks_exact_mut instead of chunks_mut as it allows almost all
// calls to memcpy to be elided by the compiler.
let mut chunks = dest.chunks_exact_mut(size_of::<Word>());
for chunk in chunks.by_ref() {
let src = unsafe { rdrand() }?.to_ne_bytes();
let src = rdrand()?.to_ne_bytes();
chunk.copy_from_slice(slice_as_uninit(&src));
}

let tail = chunks.into_remainder();
let n = tail.len();
if n > 0 {
let src = unsafe { rdrand() }?.to_ne_bytes();
let src = rdrand()?.to_ne_bytes();
tail.copy_from_slice(slice_as_uninit(&src[..n]));
}
Some(())
}

#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_u32() -> Option<u32> {
unsafe { rdrand() }.map(crate::util::truncate)
fn rdrand_u32() -> Option<u32> {
rdrand().map(crate::util::truncate)
}

#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_u64() -> Option<u64> {
unsafe { rdrand() }
fn rdrand_u64() -> Option<u64> {
rdrand()
}

#[cfg(target_arch = "x86")]
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_u32() -> Option<u32> {
unsafe { rdrand() }
fn rdrand_u32() -> Option<u32> {
rdrand()
}

#[cfg(target_arch = "x86")]
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_u64() -> Option<u64> {
let a = unsafe { rdrand() }?;
let b = unsafe { rdrand() }?;
fn rdrand_u64() -> Option<u64> {
let a = rdrand()?;
let b = rdrand()?;
Some((u64::from(a) << 32) | u64::from(b))
}

Expand Down