@@ -23,5 +23,121 @@ pub fn fill_bytes(bytes: &mut [u8]) {
23
23
}
24
24
}
25
25
26
+ // Fallback to rdrand if rng protocol missing.
27
+ //
28
+ // For real-world example, see [issue-13825](https://github.com/rust-lang/rust/issues/138252#issuecomment-2891270323)
29
+ #[ cfg( any( target_arch = "x86_64" , target_arch = "x86" ) ) ]
30
+ if rdrand:: fill_bytes ( bytes) {
31
+ return ;
32
+ }
33
+
26
34
panic ! ( "failed to generate random data" ) ;
27
35
}
36
+
37
+ /// Port from [getrandom](https://github.com/rust-random/getrandom/blob/master/src/backends/rdrand.rs)
38
+ #[ cfg( any( target_arch = "x86_64" , target_arch = "x86" ) ) ]
39
+ mod rdrand {
40
+ cfg_if:: cfg_if! {
41
+ if #[ cfg( target_arch = "x86_64" ) ] {
42
+ use crate :: arch:: x86_64 as arch;
43
+ use arch:: _rdrand64_step as rdrand_step;
44
+ type Word = u64 ;
45
+ } else if #[ cfg( target_arch = "x86" ) ] {
46
+ use crate :: arch:: x86 as arch;
47
+ use arch:: _rdrand32_step as rdrand_step;
48
+ type Word = u32 ;
49
+ }
50
+ }
51
+
52
+ static RDRAND_GOOD : crate :: sync:: LazyLock < bool > = crate :: sync:: LazyLock :: new ( is_rdrand_good) ;
53
+
54
+ // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software
55
+ // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
56
+ // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1.
57
+ const RETRY_LIMIT : usize = 10 ;
58
+
59
+ unsafe fn rdrand ( ) -> Option < Word > {
60
+ for _ in 0 ..RETRY_LIMIT {
61
+ let mut val = 0 ;
62
+ if unsafe { rdrand_step ( & mut val) } == 1 {
63
+ return Some ( val) ;
64
+ }
65
+ }
66
+ None
67
+ }
68
+
69
+ // Run a small self-test to make sure we aren't repeating values
70
+ // Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
71
+ // Fails with probability < 2^(-90) on 32-bit systems
72
+ unsafe fn self_test ( ) -> bool {
73
+ // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision.
74
+ let mut prev = Word :: MAX ;
75
+ let mut fails = 0 ;
76
+ for _ in 0 ..8 {
77
+ match unsafe { rdrand ( ) } {
78
+ Some ( val) if val == prev => fails += 1 ,
79
+ Some ( val) => prev = val,
80
+ None => return false ,
81
+ } ;
82
+ }
83
+ fails <= 2
84
+ }
85
+
86
+ fn is_rdrand_good ( ) -> bool {
87
+ #[ cfg( not( target_feature = "rdrand" ) ) ]
88
+ {
89
+ // SAFETY: All Rust x86 targets are new enough to have CPUID, and we
90
+ // check that leaf 1 is supported before using it.
91
+ let cpuid0 = unsafe { arch:: __cpuid ( 0 ) } ;
92
+ if cpuid0. eax < 1 {
93
+ return false ;
94
+ }
95
+ let cpuid1 = unsafe { arch:: __cpuid ( 1 ) } ;
96
+
97
+ let vendor_id =
98
+ [ cpuid0. ebx . to_le_bytes ( ) , cpuid0. edx . to_le_bytes ( ) , cpuid0. ecx . to_le_bytes ( ) ] ;
99
+ if vendor_id == [ * b"Auth" , * b"enti" , * b"cAMD" ] {
100
+ let mut family = ( cpuid1. eax >> 8 ) & 0xF ;
101
+ if family == 0xF {
102
+ family += ( cpuid1. eax >> 20 ) & 0xFF ;
103
+ }
104
+ // AMD CPUs families before 17h (Zen) sometimes fail to set CF when
105
+ // RDRAND fails after suspend. Don't use RDRAND on those families.
106
+ // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286
107
+ if family < 0x17 {
108
+ return false ;
109
+ }
110
+ }
111
+
112
+ const RDRAND_FLAG : u32 = 1 << 30 ;
113
+ if cpuid1. ecx & RDRAND_FLAG == 0 {
114
+ return false ;
115
+ }
116
+ }
117
+
118
+ // SAFETY: We have already checked that rdrand is available.
119
+ unsafe { self_test ( ) }
120
+ }
121
+
122
+ unsafe fn rdrand_exact ( dest : & mut [ u8 ] ) -> Option < ( ) > {
123
+ // We use chunks_exact_mut instead of chunks_mut as it allows almost all
124
+ // calls to memcpy to be elided by the compiler.
125
+ let mut chunks = dest. chunks_exact_mut ( size_of :: < Word > ( ) ) ;
126
+ for chunk in chunks. by_ref ( ) {
127
+ let src = unsafe { rdrand ( ) } ?. to_ne_bytes ( ) ;
128
+ chunk. copy_from_slice ( & src) ;
129
+ }
130
+
131
+ let tail = chunks. into_remainder ( ) ;
132
+ let n = tail. len ( ) ;
133
+ if n > 0 {
134
+ let src = unsafe { rdrand ( ) } ?. to_ne_bytes ( ) ;
135
+ tail. copy_from_slice ( & src[ ..n] ) ;
136
+ }
137
+ Some ( ( ) )
138
+ }
139
+
140
+ pub ( crate ) fn fill_bytes ( bytes : & mut [ u8 ] ) -> bool {
141
+ if * RDRAND_GOOD { unsafe { rdrand_exact ( bytes) . is_some ( ) } } else { false }
142
+ }
143
+ }
0 commit comments