Skip to content

Commit 3baf5f8

Browse files
authored
Rollup merge of #102044 - ChrisDenton:BCrypt-system-rand, r=thomcc
Remove `RtlGenRandom` (take two) First try to use the system preferred RNG but if that fails (e.g. due to a broken system configuration) then fallback to manually opening an algorithm handle.
2 parents 6fff4d9 + 8ca6a27 commit 3baf5f8

File tree

2 files changed

+29
-53
lines changed

2 files changed

+29
-53
lines changed

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
279279
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
280280
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
281281
pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
282-
pub const STATUS_NOT_SUPPORTED: NTSTATUS = 0xC00000BB_u32 as _;
283282

284283
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
285284
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
@@ -289,6 +288,7 @@ pub fn nt_success(status: NTSTATUS) -> bool {
289288

290289
// "RNG\0"
291290
pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
291+
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
292292

293293
#[repr(C)]
294294
pub struct UNICODE_STRING {
@@ -817,10 +817,6 @@ if #[cfg(not(target_vendor = "uwp"))] {
817817

818818
#[link(name = "advapi32")]
819819
extern "system" {
820-
// Forbidden when targeting UWP
821-
#[link_name = "SystemFunction036"]
822-
pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
823-
824820
// Allowed but unused by UWP
825821
pub fn OpenProcessToken(
826822
ProcessHandle: HANDLE,

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

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,12 @@
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
2522
use crate::mem;
2623
use crate::ptr;
2724
use crate::sys::c;
@@ -33,37 +30,35 @@ use crate::sys::c;
3330
/// [`HashMap`]: crate::collections::HashMap
3431
/// [`RandomState`]: crate::collections::hash_map::RandomState
3532
pub 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+
}
4040
impl 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)]
10699
fn 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

Comments
 (0)