Skip to content

Commit 0c92519

Browse files
committed
Make HashMap fall back to RtlGenRandom if BCryptGenRandom fails
Issue #84096 changed the hashmap RNG to use BCryptGenRandom instead of RtlGenRandom on Windows. Mozilla Firefox started experiencing random failures in env_logger::Builder::new() (Issue #94098) during initialization of their unsandboxed main process with an "Access Denied" error message from BCryptGenRandom(), which is used by the HashMap contained in env_logger::Builder The root cause appears to be a virus scanner or other software interfering with BCrypt DLLs loading. This change adds a fallback option if BCryptGenRandom is unusable for whatever reason. It will fallback to RtlGenRandom in this case. Fixes #94098
1 parent e209e85 commit 0c92519

File tree

2 files changed

+83
-4
lines changed

2 files changed

+83
-4
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,10 @@ if #[cfg(not(target_vendor = "uwp"))] {
788788

789789
#[link(name = "advapi32")]
790790
extern "system" {
791+
// Forbidden when targeting UWP
792+
#[link_name = "SystemFunction036"]
793+
pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
794+
791795
// Allowed but unused by UWP
792796
pub fn OpenProcessToken(
793797
ProcessHandle: HANDLE,

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

+79-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,69 @@
11
use crate::io;
22
use crate::mem;
3+
use crate::sync;
34
use crate::sys::c;
45

6+
// The kinds of HashMap RNG that may be available
7+
#[derive(Clone, Copy, Debug, PartialEq)]
8+
enum HashMapRng {
9+
Preferred,
10+
Fallback,
11+
}
12+
513
pub fn hashmap_random_keys() -> (u64, u64) {
14+
match get_hashmap_rng() {
15+
HashMapRng::Preferred => {
16+
preferred_rng().expect("couldn't generate random bytes with preferred RNG")
17+
}
18+
HashMapRng::Fallback => {
19+
fallback_rng().unwrap().expect("couldn't generate random bytes with fallback RNG")
20+
}
21+
}
22+
}
23+
24+
// Returns the HashMap RNG that should be used
25+
//
26+
// Panics if they are both broken
27+
fn get_hashmap_rng() -> HashMapRng {
28+
// Assume that if the preferred RNG is broken the first time we use it, it likely means
29+
// that: the DLL has failed to load, there is no point to calling it over-and-over again,
30+
// and we should cache the result
31+
static INIT: sync::Once = sync::Once::new();
32+
static mut HASHMAP_RNG: HashMapRng = HashMapRng::Preferred;
33+
34+
unsafe {
35+
INIT.call_once(|| HASHMAP_RNG = choose_hashmap_rng());
36+
HASHMAP_RNG
37+
}
38+
}
39+
40+
// Test whether we should use the preferred or fallback RNG
41+
//
42+
// If the preferred RNG is successful, we choose it. Otherwise, if the fallback RNG is successful,
43+
// we choose that
44+
//
45+
// Panics if both the preferred and the fallback RNG are both non-functional
46+
fn choose_hashmap_rng() -> HashMapRng {
47+
let preferred_error = match preferred_rng() {
48+
Ok(_) => return HashMapRng::Preferred,
49+
Err(e) => e,
50+
};
51+
52+
// On UWP, there is no fallback
53+
let fallback_result = fallback_rng()
54+
.unwrap_or_else(|| panic!("preferred RNG broken: `{}`, no fallback", preferred_error));
55+
56+
match fallback_result {
57+
Ok(_) => return HashMapRng::Fallback,
58+
Err(fallback_error) => panic!(
59+
"preferred RNG broken: `{}`, fallback RNG broken: `{}`",
60+
preferred_error, fallback_error
61+
),
62+
}
63+
}
64+
65+
// Generate random numbers using the preferred RNG function (BCryptGenRandom)
66+
fn preferred_rng() -> Result<(u64, u64), io::Error> {
667
use crate::ptr;
768

869
let mut v = (0, 0);
@@ -14,8 +75,22 @@ pub fn hashmap_random_keys() -> (u64, u64) {
1475
c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
1576
)
1677
};
17-
if ret != 0 {
18-
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
19-
}
20-
return v;
78+
79+
if ret == 0 { Ok(v) } else { Err(io::Error::last_os_error()) }
80+
}
81+
82+
// Generate random numbers using the fallback RNG function (RtlGenRandom)
83+
#[cfg(not(target_vendor = "uwp"))]
84+
fn fallback_rng() -> Option<Result<(u64, u64), io::Error>> {
85+
let mut v = (0, 0);
86+
let ret =
87+
unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
88+
89+
Some(if ret != 0 { Ok(v) } else { Err(io::Error::last_os_error()) })
90+
}
91+
92+
// We can't use RtlGenRandom with UWP, so there is no fallback
93+
#[cfg(target_vendor = "uwp")]
94+
fn fallback_rng() -> Option<Result<(u64, u64), io::Error>> {
95+
None
2196
}

0 commit comments

Comments
 (0)