Skip to content

Commit c9babca

Browse files
committed
std: implement the random feature
Implements the ACP rust-lang/libs-team#393.
1 parent 0f6e1ae commit c9babca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+788
-468
lines changed

library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ pub mod panicking;
395395
#[unstable(feature = "core_pattern_types", issue = "123646")]
396396
pub mod pat;
397397
pub mod pin;
398+
#[unstable(feature = "random", issue = "none")]
399+
pub mod random;
398400
#[unstable(feature = "new_range_api", issue = "125687")]
399401
pub mod range;
400402
pub mod result;

library/core/src/random.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! Random value generation.
2+
//!
3+
//! The [`Random`] trait allows generating a random value for a type using a
4+
//! given [`RandomSource`].
5+
6+
/// A source of randomness.
7+
#[unstable(feature = "random", issue = "none")]
8+
pub trait RandomSource {
9+
/// Fills `bytes` with random bytes.
10+
fn fill_bytes(&mut self, bytes: &mut [u8]);
11+
}
12+
13+
/// A trait for getting a random value for a type.
14+
///
15+
/// **Warning:** Be careful when manipulating random values! The
16+
/// [`random`](Random::random) method on integers samples them with a uniform
17+
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
18+
/// modulo operations, some of the resulting values can become more likely than
19+
/// others. Use audited crates when in doubt.
20+
#[unstable(feature = "random", issue = "none")]
21+
pub trait Random: Sized {
22+
/// Generates a random value.
23+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
24+
}
25+
26+
impl Random for bool {
27+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
28+
u8::random(source) & 1 == 1
29+
}
30+
}
31+
32+
macro_rules! impl_primitive {
33+
($t:ty) => {
34+
impl Random for $t {
35+
/// Generates a random value.
36+
///
37+
/// **Warning:** Be careful when manipulating the resulting value! This
38+
/// method samples according to a uniform distribution, so a value of 1 is
39+
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
40+
/// values can become more likely than others. Use audited crates when in
41+
/// doubt.
42+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
43+
let mut bytes = (0 as Self).to_ne_bytes();
44+
source.fill_bytes(&mut bytes);
45+
Self::from_ne_bytes(bytes)
46+
}
47+
}
48+
};
49+
}
50+
51+
impl_primitive!(u8);
52+
impl_primitive!(i8);
53+
impl_primitive!(u16);
54+
impl_primitive!(i16);
55+
impl_primitive!(u32);
56+
impl_primitive!(i32);
57+
impl_primitive!(u64);
58+
impl_primitive!(i64);
59+
impl_primitive!(u128);
60+
impl_primitive!(i128);
61+
impl_primitive!(usize);
62+
impl_primitive!(isize);

library/std/src/hash/random.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
#[allow(deprecated)]
1111
use super::{BuildHasher, Hasher, SipHasher13};
1212
use crate::cell::Cell;
13-
use crate::{fmt, sys};
13+
use crate::fmt;
14+
use crate::sys::random::hashmap_random_keys;
1415

1516
/// `RandomState` is the default state for [`HashMap`] types.
1617
///
@@ -65,7 +66,7 @@ impl RandomState {
6566
// increment one of the seeds on every RandomState creation, giving
6667
// every corresponding HashMap a different iteration order.
6768
thread_local!(static KEYS: Cell<(u64, u64)> = {
68-
Cell::new(sys::hashmap_random_keys())
69+
Cell::new(hashmap_random_keys())
6970
});
7071

7172
KEYS.with(|keys| {

library/std/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@
317317
//
318318
// Library features (core):
319319
// tidy-alphabetical-start
320+
#![feature(array_chunks)]
320321
#![feature(c_str_module)]
321322
#![feature(char_internals)]
322323
#![feature(clone_to_uninit)]
@@ -346,6 +347,7 @@
346347
#![feature(prelude_2024)]
347348
#![feature(ptr_as_uninit)]
348349
#![feature(ptr_mask)]
350+
#![feature(random)]
349351
#![feature(slice_internals)]
350352
#![feature(slice_ptr_get)]
351353
#![feature(slice_range)]
@@ -593,6 +595,8 @@ pub mod path;
593595
#[unstable(feature = "anonymous_pipe", issue = "127154")]
594596
pub mod pipe;
595597
pub mod process;
598+
#[unstable(feature = "random", issue = "none")]
599+
pub mod random;
596600
pub mod sync;
597601
pub mod time;
598602

library/std/src/random.rs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//! Random value generation.
2+
//!
3+
//! The [`Random`] trait allows generating a random value for a type using a
4+
//! given [`RandomSource`].
5+
6+
#[unstable(feature = "random", issue = "none")]
7+
pub use core::random::*;
8+
9+
use crate::sys::random as sys;
10+
11+
/// The default random source.
12+
///
13+
/// This asks the system for random data suitable for cryptographic purposes
14+
/// such as key generation. If security is a concern, consult the platform
15+
/// documentation below for the specific guarantees your target provides.
16+
///
17+
/// The high quality of randomness provided by this source means it can be quite
18+
/// slow. If you need a large quantity of random numbers and security is not a
19+
/// concern, consider using an alternative random number generator (potentially
20+
/// seeded from this one).
21+
///
22+
/// # Underlying sources
23+
///
24+
/// Platform | Source
25+
/// -----------------------|---------------------------------------------------------------
26+
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
27+
/// Windows | [`ProcessPrng`]
28+
/// macOS and other UNIXes | [`getentropy`]
29+
/// other Apple platforms | `CCRandomGenerateBytes`
30+
/// ESP-IDF | [`esp_fill_random`]
31+
/// Fuchsia | [`cprng_draw`]
32+
/// Hermit | `read_entropy`
33+
/// Horizon | `getrandom` shim
34+
/// Hurd, L4Re, QNX | `/dev/urandom`
35+
/// NetBSD before 10.0 | [`kern.arandom`]
36+
/// Redox | `/scheme/rand`
37+
/// SGX | [`rdrand`]
38+
/// SOLID | `SOLID_RNG_SampleRandomBytes`
39+
/// TEEOS | `TEE_GenerateRandom`
40+
/// UEFI | [`EFI_RNG_PROTOCOL`]
41+
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
42+
/// WASI | `random_get`
43+
/// ZKVM | `sys_rand`
44+
///
45+
/// **Disclaimer:** The sources used might change over time.
46+
///
47+
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
48+
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
49+
/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
50+
/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
51+
/// [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
52+
/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
53+
/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
54+
/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
55+
/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
56+
#[derive(Default, Debug, Clone, Copy)]
57+
#[unstable(feature = "random", issue = "none")]
58+
pub struct DefaultRandomSource;
59+
60+
#[unstable(feature = "random", issue = "none")]
61+
impl RandomSource for DefaultRandomSource {
62+
fn fill_bytes(&mut self, bytes: &mut [u8]) {
63+
sys::fill_bytes(bytes)
64+
}
65+
}
66+
67+
/// Generates a random value with the default random source.
68+
///
69+
/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and
70+
/// will sample according to the same distribution as the underlying [`Random`]
71+
/// trait implementation.
72+
///
73+
/// **Warning:** Be careful when manipulating random values! The
74+
/// [`random`](Random::random) method on integers samples them with a uniform
75+
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
76+
/// modulo operations, some of the resulting values can become more likely than
77+
/// others. Use audited crates when in doubt.
78+
///
79+
/// # Examples
80+
///
81+
/// Generating a [version 4/variant 1 UUID] represented as text:
82+
/// ```
83+
/// #![feature(random)]
84+
///
85+
/// use std::random::random;
86+
///
87+
/// let bits = random::<u128>();
88+
/// let g1 = (bits >> 96) as u32;
89+
/// let g2 = (bits >> 80) as u16;
90+
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
91+
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
92+
/// let g5 = (bits & 0xffffffffffff) as u64;
93+
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
94+
/// println!("{uuid}");
95+
/// ```
96+
///
97+
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
98+
pub fn random<T: Random>() -> T {
99+
T::random(&mut DefaultRandomSource)
100+
}

library/std/src/sys/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod cmath;
1313
pub mod exit_guard;
1414
pub mod os_str;
1515
pub mod path;
16+
pub mod random;
1617
pub mod sync;
1718
pub mod thread_local;
1819

library/std/src/sys/pal/hermit/mod.rs

-14
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,6 @@ pub fn abort_internal() -> ! {
5353
unsafe { hermit_abi::abort() }
5454
}
5555

56-
pub fn hashmap_random_keys() -> (u64, u64) {
57-
let mut buf = [0; 16];
58-
let mut slice = &mut buf[..];
59-
while !slice.is_empty() {
60-
let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
61-
.expect("failed to generate random hashmap keys");
62-
slice = &mut slice[res as usize..];
63-
}
64-
65-
let key1 = buf[..8].try_into().unwrap();
66-
let key2 = buf[8..].try_into().unwrap();
67-
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
68-
}
69-
7056
// This function is needed by the panic runtime. The symbol is named in
7157
// pre-link args for the target specification, so keep that in sync.
7258
#[cfg(not(test))]

library/std/src/sys/pal/sgx/abi/usercalls/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::cmp;
22
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
3-
use crate::sys::rand::rdrand64;
3+
use crate::random::{DefaultRandomSource, Random};
44
use crate::time::{Duration, Instant};
55

66
pub(crate) mod alloc;
@@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
164164
// trusted to ensure accurate timeouts.
165165
if let Ok(timeout_signed) = i64::try_from(timeout) {
166166
let tenth = timeout_signed / 10;
167-
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
167+
let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
168168
timeout = timeout_signed.saturating_add(deviation) as _;
169169
}
170170
}

library/std/src/sys/pal/sgx/mod.rs

-18
Original file line numberDiff line numberDiff line change
@@ -133,24 +133,6 @@ pub extern "C" fn __rust_abort() {
133133
abort_internal();
134134
}
135135

136-
pub mod rand {
137-
pub fn rdrand64() -> u64 {
138-
unsafe {
139-
let mut ret: u64 = 0;
140-
for _ in 0..10 {
141-
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
142-
return ret;
143-
}
144-
}
145-
rtabort!("Failed to obtain random data");
146-
}
147-
}
148-
}
149-
150-
pub fn hashmap_random_keys() -> (u64, u64) {
151-
(self::rand::rdrand64(), self::rand::rdrand64())
152-
}
153-
154136
pub use crate::sys_common::{AsInner, FromInner, IntoInner};
155137

156138
pub trait TryIntoInner<Inner>: Sized {

library/std/src/sys/pal/solid/mod.rs

-10
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
6363
pub fn abort_internal() -> ! {
6464
unsafe { libc::abort() }
6565
}
66-
67-
pub fn hashmap_random_keys() -> (u64, u64) {
68-
unsafe {
69-
let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
70-
let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
71-
assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
72-
let [x1, x2] = out.assume_init();
73-
(x1, x2)
74-
}
75-
}

library/std/src/sys/pal/teeos/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
#![allow(unused_variables)]
77
#![allow(dead_code)]
88

9-
pub use self::rand::hashmap_random_keys;
10-
119
pub mod alloc;
1210
#[path = "../unsupported/args.rs"]
1311
pub mod args;
@@ -24,7 +22,6 @@ pub mod os;
2422
pub mod pipe;
2523
#[path = "../unsupported/process.rs"]
2624
pub mod process;
27-
mod rand;
2825
pub mod stdio;
2926
pub mod thread;
3027
#[allow(non_upper_case_globals)]

library/std/src/sys/pal/teeos/rand.rs

-21
This file was deleted.

library/std/src/sys/pal/uefi/mod.rs

+1-35
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod args;
1818
pub mod env;
1919
#[path = "../unsupported/fs.rs"]
2020
pub mod fs;
21+
pub mod helpers;
2122
#[path = "../unsupported/io.rs"]
2223
pub mod io;
2324
#[path = "../unsupported/net.rs"]
@@ -30,8 +31,6 @@ pub mod stdio;
3031
pub mod thread;
3132
pub mod time;
3233

33-
mod helpers;
34-
3534
#[cfg(test)]
3635
mod tests;
3736

@@ -181,39 +180,6 @@ pub extern "C" fn __rust_abort() {
181180
abort_internal();
182181
}
183182

184-
#[inline]
185-
pub fn hashmap_random_keys() -> (u64, u64) {
186-
get_random().unwrap()
187-
}
188-
189-
fn get_random() -> Option<(u64, u64)> {
190-
use r_efi::protocols::rng;
191-
192-
let mut buf = [0u8; 16];
193-
let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?;
194-
for handle in handles {
195-
if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
196-
let r = unsafe {
197-
((*protocol.as_ptr()).get_rng)(
198-
protocol.as_ptr(),
199-
crate::ptr::null_mut(),
200-
buf.len(),
201-
buf.as_mut_ptr(),
202-
)
203-
};
204-
if r.is_error() {
205-
continue;
206-
} else {
207-
return Some((
208-
u64::from_le_bytes(buf[..8].try_into().ok()?),
209-
u64::from_le_bytes(buf[8..].try_into().ok()?),
210-
));
211-
}
212-
}
213-
}
214-
None
215-
}
216-
217183
/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled
218184
extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) {
219185
uefi::env::disable_boot_services();

0 commit comments

Comments
 (0)