@@ -23,5 +23,121 @@ pub fn fill_bytes(bytes: &mut [u8]) {
2323 }
2424 }
2525
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+
2634 panic ! ( "failed to generate random data" ) ;
2735}
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