Skip to content

Commit 9682b5d

Browse files
committed
Auto merge of rust-lang#101476 - ChrisDenton:BCryptRandom-fix, r=thomcc
Open a BCrypt algorithm handle Fixes rust-lang#101474, supplants rust-lang#101456. Replaces use of a pseduo handle with manually opening a algorithm handle. Most interesting thing here is the atomics. r? `@thomcc`
2 parents f91ca28 + 832c7af commit 9682b5d

File tree

2 files changed

+88
-20
lines changed

2 files changed

+88
-20
lines changed

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub type LPSYSTEM_INFO = *mut SYSTEM_INFO;
6666
pub type LPWSABUF = *mut WSABUF;
6767
pub type LPWSAOVERLAPPED = *mut c_void;
6868
pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void;
69+
pub type BCRYPT_ALG_HANDLE = LPVOID;
6970

7071
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
7172
pub type PLARGE_INTEGER = *mut c_longlong;
@@ -278,14 +279,16 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
278279
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
279280
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
280281
pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
282+
pub const STATUS_NOT_SUPPORTED: NTSTATUS = 0xC00000BB_u32 as _;
281283

282284
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
283285
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
284286
pub fn nt_success(status: NTSTATUS) -> bool {
285287
status >= 0
286288
}
287289

288-
pub const BCRYPT_RNG_ALG_HANDLE: usize = 0x81;
290+
// "RNG\0"
291+
pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
289292

290293
#[repr(C)]
291294
pub struct UNICODE_STRING {
@@ -1229,11 +1232,18 @@ extern "system" {
12291232
// >= Vista / Server 2008
12301233
// https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
12311234
pub fn BCryptGenRandom(
1232-
hAlgorithm: LPVOID,
1235+
hAlgorithm: BCRYPT_ALG_HANDLE,
12331236
pBuffer: *mut u8,
12341237
cbBuffer: ULONG,
12351238
dwFlags: ULONG,
12361239
) -> NTSTATUS;
1240+
pub fn BCryptOpenAlgorithmProvider(
1241+
phalgorithm: *mut BCRYPT_ALG_HANDLE,
1242+
pszAlgId: LPCWSTR,
1243+
pszimplementation: LPCWSTR,
1244+
dwflags: ULONG,
1245+
) -> NTSTATUS;
1246+
pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS;
12371247
}
12381248

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

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

+76-18
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
2323
//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
2424
//! [Pseudo-handle]: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-pseudo-handles
25-
use crate::io;
2625
use crate::mem;
2726
use crate::ptr;
2827
use crate::sys::c;
@@ -34,35 +33,94 @@ use crate::sys::c;
3433
/// [`HashMap`]: crate::collections::HashMap
3534
/// [`RandomState`]: crate::collections::hash_map::RandomState
3635
pub fn hashmap_random_keys() -> (u64, u64) {
37-
let mut v = (0, 0);
38-
let ret = unsafe {
39-
let size = mem::size_of_val(&v).try_into().unwrap();
40-
c::BCryptGenRandom(
41-
// BCRYPT_RNG_ALG_HANDLE is only supported in Windows 10+.
42-
// So for Windows 8.1 and Windows 7 we'll need a fallback when this fails.
43-
ptr::invalid_mut(c::BCRYPT_RNG_ALG_HANDLE),
44-
ptr::addr_of_mut!(v).cast(),
45-
size,
46-
0,
47-
)
48-
};
49-
if ret != 0 { fallback_rng() } else { v }
36+
Rng::open().and_then(|rng| rng.gen_random_keys()).unwrap_or_else(fallback_rng)
37+
}
38+
39+
struct Rng(c::BCRYPT_ALG_HANDLE);
40+
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))
51+
}
52+
#[cfg(not(miri))]
53+
// Open a handle to the RNG algorithm.
54+
fn open() -> Result<Self, c::NTSTATUS> {
55+
use crate::sync::atomic::AtomicPtr;
56+
use crate::sync::atomic::Ordering::{Acquire, Release};
57+
const ERROR_VALUE: c::LPVOID = ptr::invalid_mut(usize::MAX);
58+
59+
// An atomic is used so we don't need to reopen the handle every time.
60+
static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut());
61+
62+
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() {
67+
let status = unsafe {
68+
c::BCryptOpenAlgorithmProvider(
69+
&mut handle,
70+
c::BCRYPT_RNG_ALGORITHM.as_ptr(),
71+
ptr::null(),
72+
0,
73+
)
74+
};
75+
if c::nt_success(status) {
76+
// If another thread opens a handle first then use that handle instead.
77+
let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire);
78+
if let Err(previous_handle) = result {
79+
// Close our handle and return the previous one.
80+
unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) };
81+
handle = previous_handle;
82+
}
83+
Ok(Self(handle))
84+
} else {
85+
HANDLE.store(ERROR_VALUE, Release);
86+
Err(status)
87+
}
88+
} else {
89+
Ok(Self(handle))
90+
}
91+
}
92+
93+
fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> {
94+
let mut v = (0, 0);
95+
let status = unsafe {
96+
let size = mem::size_of_val(&v).try_into().unwrap();
97+
c::BCryptGenRandom(self.0, ptr::addr_of_mut!(v).cast(), size, 0)
98+
};
99+
if c::nt_success(status) { Ok(v) } else { Err(status) }
100+
}
50101
}
51102

52103
/// Generate random numbers using the fallback RNG function (RtlGenRandom)
53104
#[cfg(not(target_vendor = "uwp"))]
54105
#[inline(never)]
55-
fn fallback_rng() -> (u64, u64) {
106+
fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) {
56107
let mut v = (0, 0);
57108
let ret =
58109
unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
59110

60-
if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
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+
)
118+
}
61119
}
62120

63121
/// We can't use RtlGenRandom with UWP, so there is no fallback
64122
#[cfg(target_vendor = "uwp")]
65123
#[inline(never)]
66-
fn fallback_rng() -> (u64, u64) {
67-
panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
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");
68126
}

0 commit comments

Comments
 (0)