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 | |