Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions library/core/src/random.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Random value generation.

use crate::io::{BorrowedBuf, BorrowedCursor};
use crate::range::{RangeFull, RangeInclusive};

/// A source of randomness.
Expand All @@ -11,15 +12,31 @@ pub trait Rng {
/// with a larger buffer. An `Rng` is allowed to return different bytes for those two cases. For
/// instance, this allows an `Rng` to generate a word at a time and throw part of it away if not
/// needed.
fn fill_bytes(&mut self, bytes: &mut [u8]);
///
/// This is always implemented in terms of `fill_buf`, and cannot be overridden. Implementations
/// of this trait only need to define `fill_buf`.
#[inline(always)]
final fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.fill_buf(BorrowedBuf::from(bytes).unfilled());
}

/// Fills `buf` with random bytes.
///
/// Implementations must always fill the entire cursor.
///
/// Note that calling `fill_buf` multiple times is not equivalent to calling `fill_buf` once
/// with a larger buffer. An `Rng` is allowed to return different bytes for those two cases. For
/// instance, this allows an `Rng` to generate a word at a time and throw part of it away if not
/// needed.
fn fill_buf(&mut self, cursor: BorrowedCursor<'_, u8>);
Comment thread
joshtriplett marked this conversation as resolved.
}

/// Implements `Rng` for mutable references to random number generators by
/// forwarding all methods to the referenced generator.
#[unstable(feature = "random", issue = "130703")]
impl<'a, R: Rng + ?Sized> Rng for &'a mut R {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
R::fill_bytes(self, bytes);
fn fill_buf(&mut self, cursor: BorrowedCursor<'_, u8>) {
R::fill_buf(self, cursor);
}
}

Expand Down
5 changes: 3 additions & 2 deletions library/std/src/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#[unstable(feature = "random", issue = "130703")]
pub use core::random::*;

use crate::io::BorrowedCursor;
use crate::sys::random as sys;

/// The system random number generator.
Expand Down Expand Up @@ -145,8 +146,8 @@ pub struct SystemRng;

#[unstable(feature = "random", issue = "130703")]
impl Rng for SystemRng {
fn fill_bytes(&mut self, bytes: &mut [u8]) {
sys::fill_bytes(bytes)
fn fill_buf(&mut self, cursor: BorrowedCursor<'_, u8>) {
sys::fill_buf(cursor)
}
}

Expand Down
12 changes: 10 additions & 2 deletions library/std/src/sys/random/apple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@
//! into the same system service anyway, and `CCRandomGenerateBytes` has been
//! proven to be App Store-compatible.

pub fn fill_bytes(bytes: &mut [u8]) {
let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };
use crate::io::BorrowedCursor;

pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) {
let ret = unsafe {
libc::CCRandomGenerateBytes(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity())
};
assert_eq!(ret, libc::kCCSuccess, "failed to generate random data");
// SAFETY: We've just initialized all the bytes with random data
unsafe {
cursor.advance(cursor.capacity());
}
}
12 changes: 10 additions & 2 deletions library/std/src/sys/random/arc4random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@
#[cfg(not(target_os = "vita"))]
use libc::arc4random_buf;

use crate::io::BorrowedCursor;

// FIXME: move this to libc
#[cfg(target_os = "vita")] // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
unsafe extern "C" {
fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
}

pub fn fill_bytes(bytes: &mut [u8]) {
unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) }
pub fn fill_buf(&mut self, mut cursor: BorrowedCursor<'_, u8>) {
unsafe {
arc4random_buf(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity());
}
// SAFETY: We've just initialized all the bytes with random data
unsafe {
cursor.advance(cursor.capacity());
}
}
8 changes: 6 additions & 2 deletions library/std/src/sys/random/espidf.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::ffi::c_void;
use crate::io::BorrowedCursor;

unsafe extern "C" {
fn esp_fill_random(buf: *mut c_void, len: usize);
}

pub fn fill_bytes(bytes: &mut [u8]) {
unsafe { esp_fill_random(bytes.as_mut_ptr().cast(), bytes.len()) }
pub fn fill_buf(&mut self, mut cursor: BorrowedCursor<'_, u8>) {
unsafe {
esp_fill_random(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity());
cursor.advance(cursor.capacity());
}
}
9 changes: 7 additions & 2 deletions library/std/src/sys/random/fuchsia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
//! Fuchsia, as always, is quite nice and provides exactly the API we need:
//! <https://fuchsia.dev/reference/syscalls/cprng_draw>.

use crate::io::BorrowedCursor;

#[link(name = "zircon")]
unsafe extern "C" {
fn zx_cprng_draw(buffer: *mut u8, len: usize);
}

pub fn fill_bytes(bytes: &mut [u8]) {
unsafe { zx_cprng_draw(bytes.as_mut_ptr(), bytes.len()) }
pub fn fill_buf(&mut self, mut cursor: BorrowedCursor<'_, u8>) {
unsafe {
zx_cprng_draw(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity());
cursor.advance(cursor.capacity());
}
}
10 changes: 8 additions & 2 deletions library/std/src/sys/random/getentropy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
//! it where `arc4random_buf` and friends aren't available or secure (currently
//! that's only the case on Emscripten).

pub fn fill_bytes(bytes: &mut [u8]) {
use crate::io::BorrowedCursor;

pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) {
// GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated
// to be at least 256, so just use that as limit.
for chunk in bytes.chunks_mut(256) {
for chunk in cursor.as_mut().chunks_mut(256) {
let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) };
assert_ne!(r, -1, "failed to generate random data");
}
// SAFETY: We've just fully initialized the cursor
unsafe {
cursor.advance(cursor.capacity());
}
}
14 changes: 10 additions & 4 deletions library/std/src/sys/random/getrandom.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
pub fn fill_bytes(mut bytes: &mut [u8]) {
while !bytes.is_empty() {
let r = unsafe { libc::getrandom(bytes.as_mut_ptr().cast(), bytes.len(), 0) };
use crate::io::BorrowedCursor;

pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) {
while cursor.capacity() != 0 {
let r =
unsafe { libc::getrandom(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity(), 0) };
assert_ne!(r, -1, "failed to generate random data");
bytes = &mut bytes[r as usize..];
// SAFETY: We've just initialized `r` bytes.
unsafe {
cursor.advance(r as usize);
}
}
}
15 changes: 11 additions & 4 deletions library/std/src/sys/random/hermit.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
pub fn fill_bytes(mut bytes: &mut [u8]) {
while !bytes.is_empty() {
let res = unsafe { hermit_abi::read_entropy(bytes.as_mut_ptr(), bytes.len(), 0) };
use crate::io::BorrowedCursor;

pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) {
while cursor.capacity() != 0 {
let res = unsafe {
hermit_abi::read_entropy(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity(), 0)
};
assert_ne!(res, -1, "failed to generate random data");
bytes = &mut bytes[res as usize..];
// SAFETY: We've just initialized `res` bytes.
unsafe {
cursor.advance(res as usize);
}
}
}
24 changes: 14 additions & 10 deletions library/std/src/sys/random/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@
// when secure data is required.

use crate::fs::File;
use crate::io::Read;
use crate::io::{BorrowedBuf, BorrowedCursor, Read};
use crate::os::fd::AsRawFd;
use crate::sync::OnceLock;
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use crate::sync::atomic::{Atomic, AtomicBool};
use crate::sys::io::errno;
use crate::sys::pal::weak::syscall;

fn getrandom(mut bytes: &mut [u8], insecure: bool) {
fn getrandom(mut cursor: BorrowedCursor<'_, u8>, insecure: bool) {
// A weak symbol allows interposition, e.g. for perf measurements that want to
// disable randomness for consistency. Otherwise, we'll try a raw syscall.
// (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28)
Expand All @@ -88,7 +88,7 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) {

if GETRANDOM_AVAILABLE.load(Relaxed) {
loop {
if bytes.is_empty() {
if cursor.capacity() == 0 {
return;
}

Expand All @@ -102,9 +102,13 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) {
0
};

let ret = unsafe { getrandom(bytes.as_mut_ptr().cast(), bytes.len(), flags) };
let ret =
unsafe { getrandom(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity(), flags) };
if ret != -1 {
bytes = &mut bytes[ret as usize..];
// SAFETY: We've just initialized `ret` bytes
unsafe {
cursor.advance(ret as usize);
}
} else {
match errno() {
libc::EINTR => continue,
Expand Down Expand Up @@ -155,17 +159,17 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) {

DEVICE
.get_or_try_init(|| File::open("/dev/urandom"))
.and_then(|mut dev| dev.read_exact(bytes))
.and_then(|mut dev| dev.read_buf_exact(cursor))
.expect("failed to generate random data");
}

pub fn fill_bytes(bytes: &mut [u8]) {
getrandom(bytes, false);
pub fn fill_buf(cursor: BorrowedCursor<'_, u8>) {
getrandom(cursor, false);
}

pub fn hashmap_random_keys() -> (u64, u64) {
let mut bytes = [0; 16];
getrandom(&mut bytes, true);
let mut bytes = [0u8; 16];
getrandom(BorrowedBuf::from(bytes.as_mut_slice()).unfilled(), true);
let k1 = u64::from_ne_bytes(bytes[..8].try_into().unwrap());
let k2 = u64::from_ne_bytes(bytes[8..].try_into().unwrap());
(k1, k2)
Expand Down
49 changes: 25 additions & 24 deletions library/std/src/sys/random/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ cfg_select! {
// Tier 1
any(target_os = "linux", target_os = "android") => {
mod linux;
pub use linux::{fill_bytes, hashmap_random_keys};
pub use linux::{fill_buf, hashmap_random_keys};
}
target_os = "windows" => {
mod windows;
pub use windows::fill_bytes;
pub use windows::fill_buf;
}
target_vendor = "apple" => {
mod apple;
pub use apple::fill_bytes;
pub use apple::fill_buf;
// Others, in alphabetical ordering.
}
any(
Expand All @@ -26,28 +26,28 @@ cfg_select! {
target_os = "nuttx",
) => {
mod arc4random;
pub use arc4random::fill_bytes;
pub use arc4random::fill_buf;
}
target_os = "emscripten" => {
mod getentropy;
pub use getentropy::fill_bytes;
pub use getentropy::fill_buf;
}
target_os = "espidf" => {
mod espidf;
pub use espidf::fill_bytes;
pub use espidf::fill_buf;
}
target_os = "fuchsia" => {
mod fuchsia;
pub use fuchsia::fill_bytes;
pub use fuchsia::fill_buf;
}
target_os = "hermit" => {
mod hermit;
pub use hermit::fill_bytes;
pub use hermit::fill_buf;
}
any(target_os = "horizon", target_os = "cygwin") => {
// FIXME(horizon): add arc4random_buf to shim-3ds
mod getrandom;
pub use getrandom::fill_bytes;
pub use getrandom::fill_buf;
}
any(
target_os = "aix",
Expand All @@ -57,51 +57,51 @@ cfg_select! {
target_os = "qnx",
) => {
mod unix_legacy;
pub use unix_legacy::fill_bytes;
pub use unix_legacy::fill_buf;
}
target_os = "redox" => {
mod redox;
pub use redox::fill_bytes;
pub use redox::fill_buf;
}
target_os = "motor" => {
mod motor;
pub use motor::fill_bytes;
pub use motor::fill_buf;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::fill_bytes;
pub use sgx::fill_buf;
}
target_os = "solid_asp3" => {
mod solid;
pub use solid::fill_bytes;
pub use solid::fill_buf;
}
target_os = "teeos" => {
mod teeos;
pub use teeos::fill_bytes;
pub use teeos::fill_buf;
}
target_os = "trusty" => {
mod trusty;
pub use trusty::fill_bytes;
pub use trusty::fill_buf;
}
target_os = "uefi" => {
mod uefi;
pub use uefi::fill_bytes;
pub use uefi::fill_buf;
}
target_os = "vxworks" => {
mod vxworks;
pub use vxworks::fill_bytes;
pub use vxworks::fill_buf;
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::fill_bytes;
pub use wasip1::fill_buf;
}
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasi;
pub use wasi::{fill_bytes, hashmap_random_keys};
pub use wasi::{fill_buf, hashmap_random_keys};
}
target_os = "zkvm" => {
mod zkvm;
pub use zkvm::fill_bytes;
pub use zkvm::fill_buf;
}
any(
all(target_family = "wasm", target_os = "unknown"),
Expand All @@ -111,7 +111,7 @@ cfg_select! {
// FIXME: finally remove std support for wasm32-unknown-unknown
// FIXME: add random data generation to xous
mod unsupported;
pub use unsupported::{fill_bytes, hashmap_random_keys};
pub use unsupported::{fill_buf, hashmap_random_keys};
}
_ => {}
}
Expand All @@ -125,8 +125,9 @@ cfg_select! {
target_os = "vexos",
)))]
pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];
fill_bytes(&mut buf);
use crate::io::BorrowedBuf;
let mut buf = [0u8; 16];
fill_buf(BorrowedBuf::from(buf.as_mut_slice()).unfilled());
let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap());
let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap());
(k1, k2)
Expand Down
Loading
Loading