Skip to content

Commit

Permalink
std: implement the random feature
Browse files Browse the repository at this point in the history
Implements the ACP rust-lang/libs-team#393.
  • Loading branch information
joboet committed Aug 22, 2024
1 parent 8269be1 commit a796590
Show file tree
Hide file tree
Showing 42 changed files with 754 additions and 480 deletions.
2 changes: 2 additions & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ pub mod panicking;
#[unstable(feature = "core_pattern_types", issue = "123646")]
pub mod pat;
pub mod pin;
#[unstable(feature = "random", issue = "none")]
pub mod random;
#[unstable(feature = "new_range_api", issue = "125687")]
pub mod range;
pub mod result;
Expand Down
88 changes: 88 additions & 0 deletions library/core/src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Random value generation.
//!
//! The [`Random`] trait allows generating a random value for a type using a
//! given [`RandomSource`].

/// A source of randomness.
#[unstable(feature = "random", issue = "none")]
pub trait RandomSource {
/// Fills `bytes` with random bytes.
fn fill_bytes(&mut self, bytes: &mut [u8]);
}

/// A trait for getting a random value for a type.
///
/// **Warning:** Be careful when manipulating random values! The
/// [`random`](Random::random) method on integers samples them with a uniform
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
/// modulo operations, some of the resulting values can become more likely than
/// others. Use audited crates when in doubt.
#[unstable(feature = "random", issue = "none")]
pub trait Random: Sized {
/// Generates a random value.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
}

impl Random for bool {
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
u8::random(source) & 1 == 1
}
}

impl Random for u8 {
/// Generates a random value.
///
/// **Warning:** Be careful when manipulating the resulting value! This
/// method samples according to a uniform distribution, so a value of 1 is
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
/// values can become more likely than others. Use audited crates when in
/// doubt.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
let mut byte = [0];
source.fill_bytes(&mut byte);
byte[0]
}
}

impl Random for i8 {
/// Generates a random value.
///
/// **Warning:** Be careful when manipulating the resulting value! This
/// method samples according to a uniform distribution, so a value of 1 is
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
/// values can become more likely than others. Use audited crates when in
/// doubt.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
u8::random(source) as i8
}
}

macro_rules! impl_primitive {
($t:ty) => {
impl Random for $t {
/// Generates a random value.
///
/// **Warning:** Be careful when manipulating the resulting value! This
/// method samples according to a uniform distribution, so a value of 1 is
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
/// values can become more likely than others. Use audited crates when in
/// doubt.
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
let mut bytes = (0 as Self).to_ne_bytes();
source.fill_bytes(&mut bytes);
Self::from_ne_bytes(bytes)
}
}
};
}

impl_primitive!(u16);
impl_primitive!(i16);
impl_primitive!(u32);
impl_primitive!(i32);
impl_primitive!(u64);
impl_primitive!(i64);
impl_primitive!(u128);
impl_primitive!(i128);
impl_primitive!(usize);
impl_primitive!(isize);
6 changes: 4 additions & 2 deletions library/std/src/hash/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
#[allow(deprecated)]
use super::{BuildHasher, Hasher, SipHasher13};
use crate::cell::Cell;
use crate::{fmt, sys};
use crate::fmt;
use crate::random::{BestEffortRandomSource, Random};

/// `RandomState` is the default state for [`HashMap`] types.
///
Expand Down Expand Up @@ -65,7 +66,8 @@ impl RandomState {
// increment one of the seeds on every RandomState creation, giving
// every corresponding HashMap a different iteration order.
thread_local!(static KEYS: Cell<(u64, u64)> = {
Cell::new(sys::hashmap_random_keys())
let bits = u128::random(&mut BestEffortRandomSource);
Cell::new(((bits >> 64) as u64, bits as u64))
});

KEYS.with(|keys| {
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
//
// Library features (core):
// tidy-alphabetical-start
#![feature(array_chunks)]
#![feature(c_str_module)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
Expand Down Expand Up @@ -346,6 +347,7 @@
#![feature(prelude_2024)]
#![feature(ptr_as_uninit)]
#![feature(ptr_mask)]
#![feature(random)]
#![feature(slice_internals)]
#![feature(slice_ptr_get)]
#![feature(slice_range)]
Expand Down Expand Up @@ -593,6 +595,8 @@ pub mod path;
#[unstable(feature = "anonymous_pipe", issue = "127154")]
pub mod pipe;
pub mod process;
#[unstable(feature = "random", issue = "none")]
pub mod random;
pub mod sync;
pub mod time;

Expand Down
91 changes: 91 additions & 0 deletions library/std/src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Random value generation.
//!
//! The [`Random`] trait allows generating a random value for a type using a
//! given [`RandomSource`].

#[unstable(feature = "random", issue = "none")]
pub use core::random::*;

use crate::sys::random as sys;

/// The default random source.
///
/// This asks the system for the best random data it can provide, meaning the
/// resulting bytes *should* be usable for cryptographic purposes. Check your
/// platform's documentation for the specific guarantees it provides. The high
/// quality of randomness provided by this source means it is quite slow. If
/// you need a larger quantity of random numbers, consider using another random
/// number generator (potentially seeded from this one).
///
/// # Examples
///
/// Generating a [version 4/variant 1 UUID] represented as text:
/// ```
/// #![feature(random)]
///
/// use std::random::{DefaultRandomSource, Random};
///
/// let bits = u128::random(&mut DefaultRandomSource);
/// let g1 = (bits >> 96) as u32;
/// let g2 = (bits >> 80) as u16;
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
/// let g5 = (bits & 0xffffffffffff) as u64;
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
/// println!("{uuid}");
/// ```
///
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
///
/// # Underlying sources
///
/// Platform | Source
/// -----------------------|---------------------------------------------------------------
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
/// Windows | [`ProcessPrng`]
/// macOS and other UNIXes | [`getentropy`]
/// other Apple platforms | `CCRandomGenerateBytes`
/// Fuchsia | [`cprng_draw`]
/// Hermit | `read_entropy`
/// Horizon | `getrandom` shim
/// Hurd, L4Re, QNX | `/dev/urandom`
/// NetBSD before 10.0 | [`kern.arandom`]
/// Redox | `/scheme/rand`
/// SGX | [`rdrand`]
/// SOLID | `SOLID_RNG_SampleRandomBytes`
/// TEEOS | `TEE_GenerateRandom`
/// UEFI | [`EFI_RNG_PROTOCOL`]
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
/// WASI | `random_get`
/// ZKVM | `sys_rand`
///
/// **Disclaimer:** The sources used might change over time.
///
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
/// [`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
/// [`getentropy`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html
/// [`cprng_draw`]: https://fuchsia.dev/reference/syscalls/cprng_draw
/// [`kern.arandom`]: https://man.netbsd.org/rnd.4
/// [`rdrand`]: https://en.wikipedia.org/wiki/RDRAND
/// [`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol
#[derive(Default, Debug, Clone, Copy)]
#[unstable(feature = "random", issue = "none")]
pub struct DefaultRandomSource;

#[unstable(feature = "random", issue = "none")]
impl RandomSource for DefaultRandomSource {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
sys::fill_bytes(bytes, false)
}
}

/// A best-effort random source used for creating hash-map seeds.
#[unstable(feature = "random", issue = "none")]
pub(crate) struct BestEffortRandomSource;

impl RandomSource for BestEffortRandomSource {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
sys::fill_bytes(bytes, true)
}
}
1 change: 1 addition & 0 deletions library/std/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod cmath;
pub mod exit_guard;
pub mod os_str;
pub mod path;
pub mod random;
pub mod sync;
pub mod thread_local;

Expand Down
14 changes: 0 additions & 14 deletions library/std/src/sys/pal/hermit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,6 @@ pub fn abort_internal() -> ! {
unsafe { hermit_abi::abort() }
}

pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];
let mut slice = &mut buf[..];
while !slice.is_empty() {
let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
.expect("failed to generate random hashmap keys");
slice = &mut slice[res as usize..];
}

let key1 = buf[..8].try_into().unwrap();
let key2 = buf[8..].try_into().unwrap();
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
}

// This function is needed by the panic runtime. The symbol is named in
// pre-link args for the target specification, so keep that in sync.
#[cfg(not(test))]
Expand Down
4 changes: 2 additions & 2 deletions library/std/src/sys/pal/sgx/abi/usercalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::cmp;
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
use crate::sys::rand::rdrand64;
use crate::random::{DefaultRandomSource, Random};
use crate::time::{Duration, Instant};

pub(crate) mod alloc;
Expand Down Expand Up @@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
// trusted to ensure accurate timeouts.
if let Ok(timeout_signed) = i64::try_from(timeout) {
let tenth = timeout_signed / 10;
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
timeout = timeout_signed.saturating_add(deviation) as _;
}
}
Expand Down
18 changes: 0 additions & 18 deletions library/std/src/sys/pal/sgx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,6 @@ pub extern "C" fn __rust_abort() {
abort_internal();
}

pub mod rand {
pub fn rdrand64() -> u64 {
unsafe {
let mut ret: u64 = 0;
for _ in 0..10 {
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
return ret;
}
}
rtabort!("Failed to obtain random data");
}
}
}

pub fn hashmap_random_keys() -> (u64, u64) {
(self::rand::rdrand64(), self::rand::rdrand64())
}

pub use crate::sys_common::{AsInner, FromInner, IntoInner};

pub trait TryIntoInner<Inner>: Sized {
Expand Down
10 changes: 0 additions & 10 deletions library/std/src/sys/pal/solid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
pub fn abort_internal() -> ! {
unsafe { libc::abort() }
}

pub fn hashmap_random_keys() -> (u64, u64) {
unsafe {
let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
let [x1, x2] = out.assume_init();
(x1, x2)
}
}
3 changes: 0 additions & 3 deletions library/std/src/sys/pal/teeos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#![allow(unused_variables)]
#![allow(dead_code)]

pub use self::rand::hashmap_random_keys;

pub mod alloc;
#[path = "../unsupported/args.rs"]
pub mod args;
Expand All @@ -24,7 +22,6 @@ pub mod os;
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
mod rand;
pub mod stdio;
pub mod thread;
#[allow(non_upper_case_globals)]
Expand Down
21 changes: 0 additions & 21 deletions library/std/src/sys/pal/teeos/rand.rs

This file was deleted.

Loading

0 comments on commit a796590

Please sign in to comment.