Skip to content

Commit a796590

Browse files
committed
std: implement the random feature
Implements the ACP rust-lang/libs-team#393.
1 parent 8269be1 commit a796590

Some content is hidden

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

42 files changed

+754
-480
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

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
impl Random for u8 {
33+
/// Generates a random value.
34+
///
35+
/// **Warning:** Be careful when manipulating the resulting value! This
36+
/// method samples according to a uniform distribution, so a value of 1 is
37+
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
38+
/// values can become more likely than others. Use audited crates when in
39+
/// doubt.
40+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
41+
let mut byte = [0];
42+
source.fill_bytes(&mut byte);
43+
byte[0]
44+
}
45+
}
46+
47+
impl Random for i8 {
48+
/// Generates a random value.
49+
///
50+
/// **Warning:** Be careful when manipulating the resulting value! This
51+
/// method samples according to a uniform distribution, so a value of 1 is
52+
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
53+
/// values can become more likely than others. Use audited crates when in
54+
/// doubt.
55+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
56+
u8::random(source) as i8
57+
}
58+
}
59+
60+
macro_rules! impl_primitive {
61+
($t:ty) => {
62+
impl Random for $t {
63+
/// Generates a random value.
64+
///
65+
/// **Warning:** Be careful when manipulating the resulting value! This
66+
/// method samples according to a uniform distribution, so a value of 1 is
67+
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
68+
/// values can become more likely than others. Use audited crates when in
69+
/// doubt.
70+
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
71+
let mut bytes = (0 as Self).to_ne_bytes();
72+
source.fill_bytes(&mut bytes);
73+
Self::from_ne_bytes(bytes)
74+
}
75+
}
76+
};
77+
}
78+
79+
impl_primitive!(u16);
80+
impl_primitive!(i16);
81+
impl_primitive!(u32);
82+
impl_primitive!(i32);
83+
impl_primitive!(u64);
84+
impl_primitive!(i64);
85+
impl_primitive!(u128);
86+
impl_primitive!(i128);
87+
impl_primitive!(usize);
88+
impl_primitive!(isize);

library/std/src/hash/random.rs

+4-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::random::{BestEffortRandomSource, Random};
1415

1516
/// `RandomState` is the default state for [`HashMap`] types.
1617
///
@@ -65,7 +66,8 @@ 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+
let bits = u128::random(&mut BestEffortRandomSource);
70+
Cell::new(((bits >> 64) as u64, bits as u64))
6971
});
7072

7173
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

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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 the best random data it can provide, meaning the
14+
/// resulting bytes *should* be usable for cryptographic purposes. Check your
15+
/// platform's documentation for the specific guarantees it provides. The high
16+
/// quality of randomness provided by this source means it is quite slow. If
17+
/// you need a larger quantity of random numbers, consider using another random
18+
/// number generator (potentially seeded from this one).
19+
///
20+
/// # Examples
21+
///
22+
/// Generating a [version 4/variant 1 UUID] represented as text:
23+
/// ```
24+
/// #![feature(random)]
25+
///
26+
/// use std::random::{DefaultRandomSource, Random};
27+
///
28+
/// let bits = u128::random(&mut DefaultRandomSource);
29+
/// let g1 = (bits >> 96) as u32;
30+
/// let g2 = (bits >> 80) as u16;
31+
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
32+
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
33+
/// let g5 = (bits & 0xffffffffffff) as u64;
34+
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
35+
/// println!("{uuid}");
36+
/// ```
37+
///
38+
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
39+
///
40+
/// # Underlying sources
41+
///
42+
/// Platform | Source
43+
/// -----------------------|---------------------------------------------------------------
44+
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
45+
/// Windows | [`ProcessPrng`]
46+
/// macOS and other UNIXes | [`getentropy`]
47+
/// other Apple platforms | `CCRandomGenerateBytes`
48+
/// Fuchsia | [`cprng_draw`]
49+
/// Hermit | `read_entropy`
50+
/// Horizon | `getrandom` shim
51+
/// Hurd, L4Re, QNX | `/dev/urandom`
52+
/// NetBSD before 10.0 | [`kern.arandom`]
53+
/// Redox | `/scheme/rand`
54+
/// SGX | [`rdrand`]
55+
/// SOLID | `SOLID_RNG_SampleRandomBytes`
56+
/// TEEOS | `TEE_GenerateRandom`
57+
/// UEFI | [`EFI_RNG_PROTOCOL`]
58+
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
59+
/// WASI | `random_get`
60+
/// ZKVM | `sys_rand`
61+
///
62+
/// **Disclaimer:** The sources used might change over time.
63+
///
64+
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
65+
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
66+
/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
67+
/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
68+
/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
69+
/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
70+
/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
71+
/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
72+
#[derive(Default, Debug, Clone, Copy)]
73+
#[unstable(feature = "random", issue = "none")]
74+
pub struct DefaultRandomSource;
75+
76+
#[unstable(feature = "random", issue = "none")]
77+
impl RandomSource for DefaultRandomSource {
78+
fn fill_bytes(&mut self, bytes: &mut [u8]) {
79+
sys::fill_bytes(bytes, false)
80+
}
81+
}
82+
83+
/// A best-effort random source used for creating hash-map seeds.
84+
#[unstable(feature = "random", issue = "none")]
85+
pub(crate) struct BestEffortRandomSource;
86+
87+
impl RandomSource for BestEffortRandomSource {
88+
fn fill_bytes(&mut self, bytes: &mut [u8]) {
89+
sys::fill_bytes(bytes, true)
90+
}
91+
}

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.

0 commit comments

Comments
 (0)