1313//! but significant number of users to experience panics caused by a failure of
1414//! this function. See [#94098].
1515//!
16- //! The current version changes this to use the `BCRYPT_RNG_ALG_HANDLE`
17- //! [Pseudo-handle], which gets the default RNG algorithm without querying the
18- //! system preference thus hopefully avoiding the previous issue.
19- //! This is only supported on Windows 10+ so a fallback is used for older versions.
16+ //! The current version falls back to using `BCryptOpenAlgorithmProvider` if
17+ //! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
2018//!
2119//! [#94098]: https://github.com/rust-lang/rust/issues/94098
2220//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
2321//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
24- //! [Pseudo-handle]: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-pseudo-handles
2522use crate :: mem;
2623use crate :: ptr;
2724use crate :: sys:: c;
@@ -33,37 +30,35 @@ use crate::sys::c;
3330/// [`HashMap`]: crate::collections::HashMap
3431/// [`RandomState`]: crate::collections::hash_map::RandomState
3532pub fn hashmap_random_keys ( ) -> ( u64 , u64 ) {
36- Rng :: open ( ) . and_then ( |rng| rng . gen_random_keys ( ) ) . unwrap_or_else ( fallback_rng)
33+ Rng :: SYSTEM . gen_random_keys ( ) . unwrap_or_else ( fallback_rng)
3734}
3835
39- struct Rng ( c:: BCRYPT_ALG_HANDLE ) ;
36+ struct Rng {
37+ algorithm : c:: BCRYPT_ALG_HANDLE ,
38+ flags : u32 ,
39+ }
4040impl Rng {
41- #[ cfg( miri) ]
42- fn open ( ) -> Result < Self , c:: NTSTATUS > {
43- const BCRYPT_RNG_ALG_HANDLE : c:: BCRYPT_ALG_HANDLE = ptr:: invalid_mut ( 0x81 ) ;
44- let _ = (
45- c:: BCryptOpenAlgorithmProvider ,
46- c:: BCryptCloseAlgorithmProvider ,
47- c:: BCRYPT_RNG_ALGORITHM ,
48- c:: STATUS_NOT_SUPPORTED ,
49- ) ;
50- Ok ( Self ( BCRYPT_RNG_ALG_HANDLE ) )
41+ const SYSTEM : Self = unsafe { Self :: new ( ptr:: null_mut ( ) , c:: BCRYPT_USE_SYSTEM_PREFERRED_RNG ) } ;
42+
43+ /// Create the RNG from an existing algorithm handle.
44+ ///
45+ /// # Safety
46+ ///
47+ /// The handle must either be null or a valid algorithm handle.
48+ const unsafe fn new ( algorithm : c:: BCRYPT_ALG_HANDLE , flags : u32 ) -> Self {
49+ Self { algorithm, flags }
5150 }
52- # [ cfg ( not ( miri ) ) ]
53- // Open a handle to the RNG algorithm.
51+
52+ /// Open a handle to the RNG algorithm.
5453 fn open ( ) -> Result < Self , c:: NTSTATUS > {
5554 use crate :: sync:: atomic:: AtomicPtr ;
5655 use crate :: sync:: atomic:: Ordering :: { Acquire , Release } ;
57- const ERROR_VALUE : c:: LPVOID = ptr:: invalid_mut ( usize:: MAX ) ;
5856
5957 // An atomic is used so we don't need to reopen the handle every time.
6058 static HANDLE : AtomicPtr < crate :: ffi:: c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
6159
6260 let mut handle = HANDLE . load ( Acquire ) ;
63- // We use a sentinel value to designate an error occurred last time.
64- if handle == ERROR_VALUE {
65- Err ( c:: STATUS_NOT_SUPPORTED )
66- } else if handle. is_null ( ) {
61+ if handle. is_null ( ) {
6762 let status = unsafe {
6863 c:: BCryptOpenAlgorithmProvider (
6964 & mut handle,
@@ -80,47 +75,32 @@ impl Rng {
8075 unsafe { c:: BCryptCloseAlgorithmProvider ( handle, 0 ) } ;
8176 handle = previous_handle;
8277 }
83- Ok ( Self ( handle) )
78+ Ok ( unsafe { Self :: new ( handle, 0 ) } )
8479 } else {
85- HANDLE . store ( ERROR_VALUE , Release ) ;
8680 Err ( status)
8781 }
8882 } else {
89- Ok ( Self ( handle) )
83+ Ok ( unsafe { Self :: new ( handle, 0 ) } )
9084 }
9185 }
9286
9387 fn gen_random_keys ( self ) -> Result < ( u64 , u64 ) , c:: NTSTATUS > {
9488 let mut v = ( 0 , 0 ) ;
9589 let status = unsafe {
9690 let size = mem:: size_of_val ( & v) . try_into ( ) . unwrap ( ) ;
97- c:: BCryptGenRandom ( self . 0 , ptr:: addr_of_mut!( v) . cast ( ) , size, 0 )
91+ c:: BCryptGenRandom ( self . algorithm , ptr:: addr_of_mut!( v) . cast ( ) , size, self . flags )
9892 } ;
9993 if c:: nt_success ( status) { Ok ( v) } else { Err ( status) }
10094 }
10195}
10296
103- /// Generate random numbers using the fallback RNG function (RtlGenRandom)
104- #[ cfg( not( target_vendor = "uwp" ) ) ]
97+ /// Generate random numbers using the fallback RNG function
10598#[ inline( never) ]
10699fn fallback_rng ( rng_status : c:: NTSTATUS ) -> ( u64 , u64 ) {
107- let mut v = ( 0 , 0 ) ;
108- let ret =
109- unsafe { c:: RtlGenRandom ( & mut v as * mut _ as * mut u8 , mem:: size_of_val ( & v) as c:: ULONG ) } ;
110-
111- if ret != 0 {
112- v
113- } else {
114- panic ! (
115- "RNG broken: {rng_status:#x}, fallback RNG broken: {}" ,
116- crate :: io:: Error :: last_os_error( )
117- )
100+ match Rng :: open ( ) . and_then ( |rng| rng. gen_random_keys ( ) ) {
101+ Ok ( keys) => keys,
102+ Err ( status) => {
103+ panic ! ( "RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}" )
104+ }
118105 }
119106}
120-
121- /// We can't use RtlGenRandom with UWP, so there is no fallback
122- #[ cfg( target_vendor = "uwp" ) ]
123- #[ inline( never) ]
124- fn fallback_rng ( rng_status : c:: NTSTATUS ) -> ( u64 , u64 ) {
125- panic ! ( "RNG broken: {rng_status:#x} fallback RNG broken: RtlGenRandom() not supported on UWP" ) ;
126- }
0 commit comments