| 1 | // Copyright 2018 Developers of the Rand project. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| 6 | // option. This file may not be copied, modified, or distributed |
| 7 | // except according to those terms. |
| 8 | use crate::{ |
| 9 | util::{slice_as_uninit, LazyBool}, |
| 10 | Error, |
| 11 | }; |
| 12 | use core::mem::{size_of, MaybeUninit}; |
| 13 | |
| 14 | cfg_if! { |
| 15 | if #[cfg(target_arch = "x86_64" )] { |
| 16 | use core::arch::x86_64 as arch; |
| 17 | use arch::_rdrand64_step as rdrand_step; |
| 18 | } else if #[cfg(target_arch = "x86" )] { |
| 19 | use core::arch::x86 as arch; |
| 20 | use arch::_rdrand32_step as rdrand_step; |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software |
| 25 | // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures |
| 26 | // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. |
| 27 | const RETRY_LIMIT: usize = 10; |
| 28 | |
| 29 | #[target_feature (enable = "rdrand" )] |
| 30 | unsafe fn rdrand() -> Option<usize> { |
| 31 | for _ in 0..RETRY_LIMIT { |
| 32 | let mut val = 0; |
| 33 | if rdrand_step(&mut val) == 1 { |
| 34 | return Some(val as usize); |
| 35 | } |
| 36 | } |
| 37 | None |
| 38 | } |
| 39 | |
| 40 | // "rdrand" target feature requires "+rdrand" flag, see https://github.com/rust-lang/rust/issues/49653. |
| 41 | #[cfg (all(target_env = "sgx" , not(target_feature = "rdrand" )))] |
| 42 | compile_error!( |
| 43 | "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand." |
| 44 | ); |
| 45 | |
| 46 | // Run a small self-test to make sure we aren't repeating values |
| 47 | // Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c |
| 48 | // Fails with probability < 2^(-90) on 32-bit systems |
| 49 | #[target_feature (enable = "rdrand" )] |
| 50 | unsafe fn self_test() -> bool { |
| 51 | // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision. |
| 52 | let mut prev = !0; // TODO(MSRV 1.43): Move to usize::MAX |
| 53 | let mut fails = 0; |
| 54 | for _ in 0..8 { |
| 55 | match rdrand() { |
| 56 | Some(val) if val == prev => fails += 1, |
| 57 | Some(val) => prev = val, |
| 58 | None => return false, |
| 59 | }; |
| 60 | } |
| 61 | fails <= 2 |
| 62 | } |
| 63 | |
| 64 | fn is_rdrand_good() -> bool { |
| 65 | #[cfg (not(target_feature = "rdrand" ))] |
| 66 | { |
| 67 | // SAFETY: All Rust x86 targets are new enough to have CPUID, and we |
| 68 | // check that leaf 1 is supported before using it. |
| 69 | let cpuid0 = unsafe { arch::__cpuid(0) }; |
| 70 | if cpuid0.eax < 1 { |
| 71 | return false; |
| 72 | } |
| 73 | let cpuid1 = unsafe { arch::__cpuid(1) }; |
| 74 | |
| 75 | let vendor_id = [ |
| 76 | cpuid0.ebx.to_le_bytes(), |
| 77 | cpuid0.edx.to_le_bytes(), |
| 78 | cpuid0.ecx.to_le_bytes(), |
| 79 | ]; |
| 80 | if vendor_id == [*b"Auth" , *b"enti" , *b"cAMD" ] { |
| 81 | let mut family = (cpuid1.eax >> 8) & 0xF; |
| 82 | if family == 0xF { |
| 83 | family += (cpuid1.eax >> 20) & 0xFF; |
| 84 | } |
| 85 | // AMD CPUs families before 17h (Zen) sometimes fail to set CF when |
| 86 | // RDRAND fails after suspend. Don't use RDRAND on those families. |
| 87 | // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286 |
| 88 | if family < 0x17 { |
| 89 | return false; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | const RDRAND_FLAG: u32 = 1 << 30; |
| 94 | if cpuid1.ecx & RDRAND_FLAG == 0 { |
| 95 | return false; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | // SAFETY: We have already checked that rdrand is available. |
| 100 | unsafe { self_test() } |
| 101 | } |
| 102 | |
| 103 | pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { |
| 104 | static RDRAND_GOOD: LazyBool = LazyBool::new(); |
| 105 | if !RDRAND_GOOD.unsync_init(is_rdrand_good) { |
| 106 | return Err(Error::NO_RDRAND); |
| 107 | } |
| 108 | // SAFETY: After this point, we know rdrand is supported. |
| 109 | unsafe { rdrand_exact(dest) }.ok_or(Error::FAILED_RDRAND) |
| 110 | } |
| 111 | |
| 112 | // TODO: make this function safe when we have feature(target_feature_11) |
| 113 | #[target_feature (enable = "rdrand" )] |
| 114 | unsafe fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> { |
| 115 | // We use chunks_exact_mut instead of chunks_mut as it allows almost all |
| 116 | // calls to memcpy to be elided by the compiler. |
| 117 | let mut chunks = dest.chunks_exact_mut(size_of::<usize>()); |
| 118 | for chunk in chunks.by_ref() { |
| 119 | let src = rdrand()?.to_ne_bytes(); |
| 120 | chunk.copy_from_slice(slice_as_uninit(&src)); |
| 121 | } |
| 122 | |
| 123 | let tail = chunks.into_remainder(); |
| 124 | let n = tail.len(); |
| 125 | if n > 0 { |
| 126 | let src = rdrand()?.to_ne_bytes(); |
| 127 | tail.copy_from_slice(slice_as_uninit(&src[..n])); |
| 128 | } |
| 129 | Some(()) |
| 130 | } |
| 131 | |