From 01739ddae9015d9aa484f7c30a97b85d30fa8f98 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 26 Nov 2025 13:24:12 -0500 Subject: [PATCH] rdrand: mark rdrand safe In edition 2024 functions annotated with `target_feature(enable = "..")` are unsafe to call from contexts not so annotated, and otherwise safe. This was used in https://github.com/rust-lang/stdarch/commit/59864cd to mark RDRAND safe, and this change is expected to be in 1.93.0 and is already in nightly. Since uefi targets are only tested on nightly, we only saw this lint on uefi, resulting in a misattribution of the behavior and an incorrect comment. Thus acknowledge that the intrinsics are safe and mark `rdrand` itself safe (when called from an annotated context) and remove all the newly unused unsafe blocks. Link: https://doc.rust-lang.org/reference/attributes/codegen.html#r-attributes.codegen.target_feature.safety-restrictions. --- src/backends/rdrand.rs | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/backends/rdrand.rs b/src/backends/rdrand.rs index e1e8934c..ece9a434 100644 --- a/src/backends/rdrand.rs +++ b/src/backends/rdrand.rs @@ -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 { +fn rdrand() -> Option { 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); } @@ -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, @@ -102,21 +111,20 @@ 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]) -> Option<()> { +fn rdrand_exact(dest: &mut [MaybeUninit]) -> 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::()); 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(()) @@ -124,27 +132,27 @@ unsafe fn rdrand_exact(dest: &mut [MaybeUninit]) -> Option<()> { #[cfg(target_arch = "x86_64")] #[target_feature(enable = "rdrand")] -unsafe fn rdrand_u32() -> Option { - unsafe { rdrand() }.map(crate::util::truncate) +fn rdrand_u32() -> Option { + rdrand().map(crate::util::truncate) } #[cfg(target_arch = "x86_64")] #[target_feature(enable = "rdrand")] -unsafe fn rdrand_u64() -> Option { - unsafe { rdrand() } +fn rdrand_u64() -> Option { + rdrand() } #[cfg(target_arch = "x86")] #[target_feature(enable = "rdrand")] -unsafe fn rdrand_u32() -> Option { - unsafe { rdrand() } +fn rdrand_u32() -> Option { + rdrand() } #[cfg(target_arch = "x86")] #[target_feature(enable = "rdrand")] -unsafe fn rdrand_u64() -> Option { - let a = unsafe { rdrand() }?; - let b = unsafe { rdrand() }?; +fn rdrand_u64() -> Option { + let a = rdrand()?; + let b = rdrand()?; Some((u64::from(a) << 32) | u64::from(b)) }