Skip to content

Commit ef6de70

Browse files
authored
Rollup merge of #108060 - ChrisDenton:rtlgenrandom, r=thomcc
Revert to using `RtlGenRandom` as a fallback This is required due to `BCryptGenRandom` failing to load a dll it depends on. Fixes #108059
2 parents 504225c + dfd0afb commit ef6de70

File tree

2 files changed

+31
-103
lines changed

2 files changed

+31
-103
lines changed

library/std/src/sys/windows/c.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,6 @@ pub fn nt_success(status: NTSTATUS) -> bool {
295295
status >= 0
296296
}
297297

298-
// "RNG\0"
299-
pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
300298
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
301299

302300
#[repr(C)]
@@ -834,6 +832,10 @@ if #[cfg(not(target_vendor = "uwp"))] {
834832

835833
#[link(name = "advapi32")]
836834
extern "system" {
835+
// Forbidden when targeting UWP
836+
#[link_name = "SystemFunction036"]
837+
pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
838+
837839
// Allowed but unused by UWP
838840
pub fn OpenProcessToken(
839841
ProcessHandle: HANDLE,
@@ -1258,13 +1260,6 @@ extern "system" {
12581260
cbBuffer: ULONG,
12591261
dwFlags: ULONG,
12601262
) -> NTSTATUS;
1261-
pub fn BCryptOpenAlgorithmProvider(
1262-
phalgorithm: *mut BCRYPT_ALG_HANDLE,
1263-
pszAlgId: LPCWSTR,
1264-
pszimplementation: LPCWSTR,
1265-
dwflags: ULONG,
1266-
) -> NTSTATUS;
1267-
pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS;
12681263
}
12691264

12701265
// Functions that aren't available on every version of Windows that we support,

library/std/src/sys/windows/rand.rs

+27-94
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,39 @@
1-
//! # Random key generation
2-
//!
3-
//! This module wraps the RNG provided by the OS. There are a few different
4-
//! ways to interface with the OS RNG so it's worth exploring each of the options.
5-
//! Note that at the time of writing these all go through the (undocumented)
6-
//! `bcryptPrimitives.dll` but they use different route to get there.
7-
//!
8-
//! Originally we were using [`RtlGenRandom`], however that function is
9-
//! deprecated and warns it "may be altered or unavailable in subsequent versions".
10-
//!
11-
//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG`
12-
//! flag to query and find the system configured RNG. However, this change caused a small
13-
//! but significant number of users to experience panics caused by a failure of
14-
//! this function. See [#94098].
15-
//!
16-
//! The current version falls back to using `BCryptOpenAlgorithmProvider` if
17-
//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
18-
//!
19-
//! [#94098]: https://github.com/rust-lang/rust/issues/94098
20-
//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
21-
//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
1+
use crate::io;
222
use crate::mem;
233
use crate::ptr;
244
use crate::sys::c;
255

26-
/// Generates high quality secure random keys for use by [`HashMap`].
27-
///
28-
/// This is used to seed the default [`RandomState`].
29-
///
30-
/// [`HashMap`]: crate::collections::HashMap
31-
/// [`RandomState`]: crate::collections::hash_map::RandomState
326
pub fn hashmap_random_keys() -> (u64, u64) {
33-
Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng)
7+
let mut v = (0, 0);
8+
let ret = unsafe {
9+
c::BCryptGenRandom(
10+
ptr::null_mut(),
11+
&mut v as *mut _ as *mut u8,
12+
mem::size_of_val(&v) as c::ULONG,
13+
c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
14+
)
15+
};
16+
if c::nt_success(ret) { v } else { fallback_rng() }
3417
}
3518

36-
struct Rng {
37-
algorithm: c::BCRYPT_ALG_HANDLE,
38-
flags: u32,
39-
}
40-
impl Rng {
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 }
50-
}
51-
52-
/// Open a handle to the RNG algorithm.
53-
fn open() -> Result<Self, c::NTSTATUS> {
54-
use crate::sync::atomic::AtomicPtr;
55-
use crate::sync::atomic::Ordering::{Acquire, Release};
56-
57-
// An atomic is used so we don't need to reopen the handle every time.
58-
static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut());
59-
60-
let mut handle = HANDLE.load(Acquire);
61-
if handle.is_null() {
62-
let status = unsafe {
63-
c::BCryptOpenAlgorithmProvider(
64-
&mut handle,
65-
c::BCRYPT_RNG_ALGORITHM.as_ptr(),
66-
ptr::null(),
67-
0,
68-
)
69-
};
70-
if c::nt_success(status) {
71-
// If another thread opens a handle first then use that handle instead.
72-
let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire);
73-
if let Err(previous_handle) = result {
74-
// Close our handle and return the previous one.
75-
unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) };
76-
handle = previous_handle;
77-
}
78-
Ok(unsafe { Self::new(handle, 0) })
79-
} else {
80-
Err(status)
81-
}
82-
} else {
83-
Ok(unsafe { Self::new(handle, 0) })
84-
}
85-
}
19+
/// Generate random numbers using the fallback RNG function (RtlGenRandom)
20+
///
21+
/// This is necessary because of a failure to load the SysWOW64 variant of the
22+
/// bcryptprimitives.dll library from code that lives in bcrypt.dll
23+
/// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1788004#c9>
24+
#[cfg(not(target_vendor = "uwp"))]
25+
#[inline(never)]
26+
fn fallback_rng() -> (u64, u64) {
27+
let mut v = (0, 0);
28+
let ret =
29+
unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
8630

87-
fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> {
88-
let mut v = (0, 0);
89-
let status = unsafe {
90-
let size = mem::size_of_val(&v).try_into().unwrap();
91-
c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags)
92-
};
93-
if c::nt_success(status) { Ok(v) } else { Err(status) }
94-
}
31+
if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
9532
}
9633

97-
/// Generate random numbers using the fallback RNG function
34+
/// We can't use RtlGenRandom with UWP, so there is no fallback
35+
#[cfg(target_vendor = "uwp")]
9836
#[inline(never)]
99-
fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) {
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-
}
105-
}
37+
fn fallback_rng() -> (u64, u64) {
38+
panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
10639
}

0 commit comments

Comments
 (0)