@@ -45,26 +45,66 @@ compile_error!(
45
45
"SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand."
46
46
) ;
47
47
48
- #[ cfg( target_feature = "rdrand" ) ]
49
- fn is_rdrand_supported ( ) -> bool {
50
- true
48
+ // Run a small self-test to make sure we aren't repeating values
49
+ // Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
50
+ // Fails with probability < 2^(-90) on 32-bit systems
51
+ #[ target_feature( enable = "rdrand" ) ]
52
+ unsafe fn self_test ( ) -> bool {
53
+ // On AMD, RDRAND returns usize::MAX on failure, count it as a collision.
54
+ let mut prev = usize:: MAX ;
55
+ let mut fails = 0 ;
56
+ for _ in 0 ..8 {
57
+ match rdrand ( ) {
58
+ Some ( val) if val == prev => fails += 1 ,
59
+ Some ( val) => prev = val,
60
+ None => return false ,
61
+ } ;
62
+ }
63
+ fails <= 2
51
64
}
52
65
53
- // TODO use is_x86_feature_detected!("rdrand") when that works in core. See:
54
- // https://github.com/rust-lang-nursery/stdsimd/issues/464
55
- #[ cfg( not( target_feature = "rdrand" ) ) ]
56
- fn is_rdrand_supported ( ) -> bool {
57
- use crate :: util:: LazyBool ;
66
+ fn is_rdrand_good ( ) -> bool {
67
+ #[ cfg( not( target_feature = "rdrand" ) ) ]
68
+ {
69
+ // SAFETY: All Rust x86 targets are new enough to have CPUID, and we
70
+ // check that leaf 1 is supported before using it.
71
+ let cpuid0 = unsafe { arch:: __cpuid ( 0 ) } ;
72
+ if cpuid0. eax < 1 {
73
+ return false ;
74
+ }
75
+ let cpuid1 = unsafe { arch:: __cpuid ( 1 ) } ;
76
+
77
+ let vendor_id = [
78
+ cpuid0. ebx . to_le_bytes ( ) ,
79
+ cpuid0. edx . to_le_bytes ( ) ,
80
+ cpuid0. ecx . to_le_bytes ( ) ,
81
+ ] ;
82
+ if vendor_id == [ * b"Auth" , * b"enti" , * b"cAMD" ] {
83
+ let mut family = ( cpuid1. eax >> 8 ) & 0xF ;
84
+ if family == 0xF {
85
+ family += ( cpuid1. eax >> 20 ) & 0xFF ;
86
+ }
87
+ // AMD CPUs families before 17h (Zen) sometimes fail to set CF when
88
+ // RDRAND fails after suspend. Don't use RDRAND on those families.
89
+ // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286
90
+ if family < 0x17 {
91
+ return false ;
92
+ }
93
+ }
94
+
95
+ const RDRAND_FLAG : u32 = 1 << 30 ;
96
+ if cpuid1. ecx & RDRAND_FLAG == 0 {
97
+ return false ;
98
+ }
99
+ }
58
100
59
- // SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID
60
- // is supported, CPUID leaf 1 is always supported.
61
- const FLAG : u32 = 1 << 30 ;
62
- static HAS_RDRAND : LazyBool = LazyBool :: new ( ) ;
63
- HAS_RDRAND . unsync_init ( || unsafe { ( arch:: __cpuid ( 1 ) . ecx & FLAG ) != 0 } )
101
+ // SAFETY: We have already checked that rdrand is available.
102
+ unsafe { self_test ( ) }
64
103
}
65
104
66
105
pub fn getrandom_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
67
- if !is_rdrand_supported ( ) {
106
+ static RDRAND_GOOD : LazyBool = LazyBool :: new ( ) ;
107
+ if !RDRAND_GOOD . unsync_init ( is_rdrand_good) {
68
108
return Err ( Error :: NO_RDRAND ) ;
69
109
}
70
110
rdrand_exact ( dest) . ok_or ( Error :: FAILED_RDRAND )
0 commit comments