From 0721c725b41b013eaea20c91f84053cbcb7d08df Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Aug 2026 21:54:06 -0700 Subject: [PATCH 1/2] `Rng`: Uninitialized buffer support Since `BorrowedBuf`/`BorrowedCursor` are on their way to stabilization, add support for uninitialized buffers in `Rng`. Add a `fill_buf` method that takes a `BorrowedCursor` and fills it in. Adapt all the system implementations accordingly. Make `fill_bytes` a `final fn` helper, which callers can use but trait implementations ignore in favor of implementing `fill_buf`. --- library/core/src/random.rs | 21 ++++++++-- library/std/src/random.rs | 5 ++- library/std/src/sys/random/apple.rs | 12 +++++- library/std/src/sys/random/arc4random.rs | 12 +++++- library/std/src/sys/random/espidf.rs | 8 +++- library/std/src/sys/random/fuchsia.rs | 9 ++++- library/std/src/sys/random/getentropy.rs | 10 ++++- library/std/src/sys/random/getrandom.rs | 14 +++++-- library/std/src/sys/random/hermit.rs | 15 +++++-- library/std/src/sys/random/linux.rs | 24 ++++++----- library/std/src/sys/random/mod.rs | 49 ++++++++++++----------- library/std/src/sys/random/motor.rs | 11 ++++- library/std/src/sys/random/redox.rs | 6 +-- library/std/src/sys/random/sgx.rs | 25 +++++------- library/std/src/sys/random/solid.rs | 11 +++-- library/std/src/sys/random/teeos.rs | 12 +++++- library/std/src/sys/random/trusty.rs | 12 +++++- library/std/src/sys/random/uefi.rs | 40 ++++++++++-------- library/std/src/sys/random/unix_legacy.rs | 6 +-- library/std/src/sys/random/unsupported.rs | 3 +- library/std/src/sys/random/vxworks.rs | 14 ++++--- library/std/src/sys/random/wasi.rs | 6 ++- library/std/src/sys/random/wasip1.rs | 11 ++++- library/std/src/sys/random/windows.rs | 18 +++++---- library/std/src/sys/random/zkvm.rs | 18 ++++++--- 25 files changed, 246 insertions(+), 126 deletions(-) diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 2fec70ffbee7f..6c5d41196d754 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -1,5 +1,6 @@ //! Random value generation. +use crate::io::{BorrowedBuf, BorrowedCursor}; use crate::range::{RangeFull, RangeInclusive}; /// A source of randomness. @@ -11,15 +12,29 @@ 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. + /// + /// 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>); } /// 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); } } diff --git a/library/std/src/random.rs b/library/std/src/random.rs index ef561d1ed0c60..fb68892715532 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -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. @@ -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) } } diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs index 417198c9d850a..a1c37be11a9f2 100644 --- a/library/std/src/sys/random/apple.rs +++ b/library/std/src/sys/random/apple.rs @@ -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()); + } } diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs index 92e7fbaf87d76..64317aefe5181 100644 --- a/library/std/src/sys/random/arc4random.rs +++ b/library/std/src/sys/random/arc4random.rs @@ -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()); + } } diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs index 6f48f7f1f2952..4561c88ccd5c5 100644 --- a/library/std/src/sys/random/espidf.rs +++ b/library/std/src/sys/random/espidf.rs @@ -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()); + } } diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs index 269e0d9aeeb57..795de1940451c 100644 --- a/library/std/src/sys/random/fuchsia.rs +++ b/library/std/src/sys/random/fuchsia.rs @@ -3,11 +3,16 @@ //! Fuchsia, as always, is quite nice and provides exactly the API we need: //! . +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()); + } } diff --git a/library/std/src/sys/random/getentropy.rs b/library/std/src/sys/random/getentropy.rs index 110ac134c1f47..85b97e57b7779 100644 --- a/library/std/src/sys/random/getentropy.rs +++ b/library/std/src/sys/random/getentropy.rs @@ -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()); + } } diff --git a/library/std/src/sys/random/getrandom.rs b/library/std/src/sys/random/getrandom.rs index 0be2eae20a727..ee1906ae1d5cb 100644 --- a/library/std/src/sys/random/getrandom.rs +++ b/library/std/src/sys/random/getrandom.rs @@ -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); + } } } diff --git a/library/std/src/sys/random/hermit.rs b/library/std/src/sys/random/hermit.rs index 92c0550d2d584..942ed2309b18d 100644 --- a/library/std/src/sys/random/hermit.rs +++ b/library/std/src/sys/random/hermit.rs @@ -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); + } } } diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index 6a4e4f3e670db..1682b2422380f 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -61,7 +61,7 @@ // 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}; @@ -69,7 +69,7 @@ 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) @@ -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; } @@ -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, @@ -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) diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index e5a66dc463c6b..29eb003ed5f86 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -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( @@ -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", @@ -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"), @@ -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}; } _ => {} } @@ -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) diff --git a/library/std/src/sys/random/motor.rs b/library/std/src/sys/random/motor.rs index 386b3704a91ea..c3b7d011d2fb0 100644 --- a/library/std/src/sys/random/motor.rs +++ b/library/std/src/sys/random/motor.rs @@ -1,3 +1,10 @@ -pub fn fill_bytes(bytes: &mut [u8]) { - moto_rt::fill_random_bytes(bytes) +use crate::io::BorrowedCursor; + +// https://github.com/moturus/motor-os/issues/49 +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + moto_rt::fill_random_bytes(cursor.ensure_init()); + // SAFETY: We've just initialized all the bytes + unsafe { + cursor.advance(cursor.capacity()); + } } diff --git a/library/std/src/sys/random/redox.rs b/library/std/src/sys/random/redox.rs index b004335a35176..40dc3a65d271e 100644 --- a/library/std/src/sys/random/redox.rs +++ b/library/std/src/sys/random/redox.rs @@ -1,12 +1,12 @@ use crate::fs::File; -use crate::io::Read; +use crate::io::{BorrowedCursor, Read}; use crate::sync::OnceLock; static SCHEME: OnceLock = OnceLock::new(); -pub fn fill_bytes(bytes: &mut [u8]) { +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { SCHEME .get_or_try_init(|| File::open("/scheme/rand")) - .and_then(|mut scheme| scheme.read_exact(bytes)) + .and_then(|mut scheme| scheme.read_buf_exact(cursor)) .expect("failed to generate random data"); } diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs index 462b19003fad2..bf673d47ccf36 100644 --- a/library/std/src/sys/random/sgx.rs +++ b/library/std/src/sys/random/sgx.rs @@ -1,4 +1,5 @@ use crate::arch::x86_64::{_rdrand16_step, _rdrand32_step, _rdrand64_step}; +use crate::io::BorrowedCursor; const RETRIES: u32 = 10; @@ -45,23 +46,17 @@ fn rdrand16() -> u16 { } } -pub fn fill_bytes(bytes: &mut [u8]) { - let (chunks, remainder) = bytes.as_chunks_mut(); - for chunk in chunks { - *chunk = rdrand64().to_ne_bytes(); +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + while cursor.capacity() >= 8 { + cursor.append(&rdrand64().to_ne_bytes()); } - - let (chunks, remainder) = remainder.as_chunks_mut(); - for chunk in chunks { - *chunk = rdrand32().to_ne_bytes(); + if cursor.capacity() >= 4 { + cursor.append(&rdrand32().to_ne_bytes()); } - - let (chunks, remainder) = remainder.as_chunks_mut(); - for chunk in chunks { - *chunk = rdrand16().to_ne_bytes(); + if cursor.capacity() >= 2 { + cursor.append(&rdrand16().to_ne_bytes()); } - - if let [byte] = remainder { - *byte = rdrand16() as u8; + if cursor.capacity() == 1 { + cursor.append(&[rdrand16() as u8]); } } diff --git a/library/std/src/sys/random/solid.rs b/library/std/src/sys/random/solid.rs index 545771150e284..89339462abd26 100644 --- a/library/std/src/sys/random/solid.rs +++ b/library/std/src/sys/random/solid.rs @@ -1,8 +1,13 @@ +use crate::io::BorrowedCursor; use crate::sys::pal::abi; -pub fn fill_bytes(bytes: &mut [u8]) { +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + let result = unsafe { + abi::SOLID_RNG_SampleRandomBytes(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity()) + }; + assert_eq!(result, 0, "failed to generate random data"); + // SAFETY: We've just initialized all the bytes with random data unsafe { - let result = abi::SOLID_RNG_SampleRandomBytes(bytes.as_mut_ptr(), bytes.len()); - assert_eq!(result, 0, "failed to generate random data"); + cursor.advance(cursor.capacity()); } } diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs index 6ca59cc12c98f..95907daa1d50c 100644 --- a/library/std/src/sys/random/teeos.rs +++ b/library/std/src/sys/random/teeos.rs @@ -1,7 +1,15 @@ +use crate::io::BorrowedCursor; + unsafe extern "C" { fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); } -pub fn fill_bytes(bytes: &mut [u8]) { - unsafe { TEE_GenerateRandom(bytes.as_mut_ptr().cast(), bytes.len()) } +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + unsafe { + TEE_GenerateRandom(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()); + } } diff --git a/library/std/src/sys/random/trusty.rs b/library/std/src/sys/random/trusty.rs index e4db24695f8bd..8341cee8018aa 100644 --- a/library/std/src/sys/random/trusty.rs +++ b/library/std/src/sys/random/trusty.rs @@ -1,7 +1,15 @@ +use crate::io::BorrowedCursor; + unsafe extern "C" { fn trusty_rng_secure_rand(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); } -pub fn fill_bytes(bytes: &mut [u8]) { - unsafe { trusty_rng_secure_rand(bytes.as_mut_ptr().cast(), bytes.len()) } +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + unsafe { + trusty_rng_secure_rand(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()); + } } diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs index f7a7600835195..165578cd0ed2e 100644 --- a/library/std/src/sys/random/uefi.rs +++ b/library/std/src/sys/random/uefi.rs @@ -1,11 +1,13 @@ -pub fn fill_bytes(bytes: &mut [u8]) { +use crate::io::BorrowedCursor; + +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { // Handle zero-byte request - if bytes.is_empty() { + if cursor.capacity() == 0 { return; } // Try EFI_RNG_PROTOCOL - if rng_protocol::fill_bytes(bytes) { + if rng_protocol::fill_buf(cursor) { return; } @@ -13,7 +15,7 @@ pub fn fill_bytes(bytes: &mut [u8]) { // // For real-world example, see [issue-13825](https://github.com/rust-lang/rust/issues/138252#issuecomment-2891270323) #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] - if rdrand::fill_bytes(bytes) { + if rdrand::fill_buf(cursor) { return; } @@ -23,9 +25,10 @@ pub fn fill_bytes(bytes: &mut [u8]) { mod rng_protocol { use r_efi::protocols::rng; + use crate::io::BorrowedCursor; use crate::sys::pal::helpers; - pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool { + pub(crate) fn fill_buf(cursor: BorrowedCursor<'_, u8>) -> bool { if let Ok(handles) = helpers::locate_handles(rng::PROTOCOL_GUID) { for handle in handles { if let Ok(protocol) = @@ -35,13 +38,17 @@ mod rng_protocol { ((*protocol.as_ptr()).get_rng)( protocol.as_ptr(), crate::ptr::null_mut(), - bytes.len(), - bytes.as_mut_ptr(), + cursor.capacity(), + cursor.as_mut().as_mut_ptr().cast(), ) }; if r.is_error() { continue; } else { + // SAFETY: We've just initialized all the bytes with random data + unsafe { + cursor.advance(cursor.capacity()); + } return true; } } @@ -55,6 +62,8 @@ mod rng_protocol { /// Port from [getrandom](https://github.com/rust-random/getrandom/blob/master/src/backends/rdrand.rs) #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] mod rdrand { + use crate::io::BorrowedCursor; + cfg_select! { target_arch = "x86_64" => { use crate::arch::x86_64 as arch; @@ -138,21 +147,18 @@ mod rdrand { unsafe { self_test() } } - unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> { - let (chunks, tail) = dest.as_chunks_mut(); - for chunk in chunks { - *chunk = unsafe { rdrand() }?.to_ne_bytes(); + unsafe fn rdrand_exact_buf(cursor: BorrowedCursor<'_, u8>) -> Option<()> { + while cursor.capacity() >= size_of::() { + cursor.append(&unsafe { rdrand() }?.to_ne_bytes()); } - - let n = tail.len(); - if n > 0 { + if cursor.capacity() != 0 { let src = unsafe { rdrand() }?.to_ne_bytes(); - tail.copy_from_slice(&src[..n]); + cursor.append(&src[..cursor.capacity()]); } Some(()) } - pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool { - if *RDRAND_GOOD { unsafe { rdrand_exact(bytes).is_some() } } else { false } + pub(crate) fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + if *RDRAND_GOOD { unsafe { rdrand_exact_buf(cursor).is_some() } } else { false } } } diff --git a/library/std/src/sys/random/unix_legacy.rs b/library/std/src/sys/random/unix_legacy.rs index 587068b0d6641..7afaf6e17442f 100644 --- a/library/std/src/sys/random/unix_legacy.rs +++ b/library/std/src/sys/random/unix_legacy.rs @@ -7,14 +7,14 @@ //! yet, we just read from the file. use crate::fs::File; -use crate::io::Read; +use crate::io::{BorrowedCursor, Read}; use crate::sync::OnceLock; static DEVICE: OnceLock = OnceLock::new(); -pub fn fill_bytes(bytes: &mut [u8]) { +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { 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(bytes)) .expect("failed to generate random data"); } diff --git a/library/std/src/sys/random/unsupported.rs b/library/std/src/sys/random/unsupported.rs index 894409b395abb..ad53b70cb7ea3 100644 --- a/library/std/src/sys/random/unsupported.rs +++ b/library/std/src/sys/random/unsupported.rs @@ -1,6 +1,7 @@ +use crate::io::BorrowedCursor; use crate::ptr; -pub fn fill_bytes(_: &mut [u8]) { +pub fn fill_buf(_: BorrowedCursor<'_, u8>) { panic!("this target does not support random data generation"); } diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs index 14f02e8ecd220..5ef00b2b7655e 100644 --- a/library/std/src/sys/random/vxworks.rs +++ b/library/std/src/sys/random/vxworks.rs @@ -1,9 +1,10 @@ +use crate::io::BorrowedCursor; use crate::sync::atomic::Ordering::Relaxed; use crate::sync::atomic::{Atomic, AtomicBool}; static RNG_INIT: Atomic = AtomicBool::new(false); -pub fn fill_bytes(mut bytes: &mut [u8]) { +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { while !RNG_INIT.load(Relaxed) { let ret = unsafe { libc::randSecure() }; if ret < 0 { @@ -16,10 +17,13 @@ pub fn fill_bytes(mut bytes: &mut [u8]) { unsafe { libc::usleep(10) }; } - while !bytes.is_empty() { - let len = bytes.len().try_into().unwrap_or(libc::c_int::MAX); - let ret = unsafe { libc::randABytes(bytes.as_mut_ptr(), len) }; + while cursor.capacity() != 0 { + let len = cursor.capacity().try_into().unwrap_or(libc::c_int::MAX); + let ret = unsafe { libc::randABytes(cursor.as_mut().as_mut_ptr().cast(), len) }; assert!(ret >= 0, "failed to generate random data"); - bytes = &mut bytes[len as usize..]; + // SAFETY: We've just initialized `len` bytes + unsafe { + cursor.advance(len as usize); + } } } diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasi.rs index 80a9153bdb923..cdd30ef013bb8 100644 --- a/library/std/src/sys/random/wasi.rs +++ b/library/std/src/sys/random/wasi.rs @@ -3,8 +3,10 @@ use wasip2::random::{insecure_seed::insecure_seed as get_insecure_seed, random:: #[cfg(target_env = "p3")] use wasip3::random::{insecure_seed::get_insecure_seed, random::get_random_bytes}; -pub fn fill_bytes(bytes: &mut [u8]) { - bytes.copy_from_slice(&get_random_bytes(u64::try_from(bytes.len()).unwrap())); +use crate::io::BorrowedCursor; + +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + cursor.append(&get_random_bytes(u64::try_from(cursor.capacity()).unwrap())); } pub fn hashmap_random_keys() -> (u64, u64) { diff --git a/library/std/src/sys/random/wasip1.rs b/library/std/src/sys/random/wasip1.rs index cb91575fe30ef..adf2fc061497b 100644 --- a/library/std/src/sys/random/wasip1.rs +++ b/library/std/src/sys/random/wasip1.rs @@ -1,5 +1,12 @@ -pub fn fill_bytes(bytes: &mut [u8]) { +use crate::io::BorrowedCursor; + +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { unsafe { - wasip1::random_get(bytes.as_mut_ptr(), bytes.len()).expect("failed to generate random data") + wasip1::random_get(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity()) + .expect("failed to generate random data") + } + // SAFETY: We've just initialized all the bytes with random data + unsafe { + cursor.advance(cursor.capacity()); } } diff --git a/library/std/src/sys/random/windows.rs b/library/std/src/sys/random/windows.rs index f5da637f56ca9..a435b21931355 100644 --- a/library/std/src/sys/random/windows.rs +++ b/library/std/src/sys/random/windows.rs @@ -1,20 +1,24 @@ +use crate::io::BorrowedCursor; use crate::sys::c; #[cfg(not(target_vendor = "win7"))] #[inline] -pub fn fill_bytes(bytes: &mut [u8]) { - let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) }; +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + let ret = unsafe { c::ProcessPrng(cursor.as_mut().as_mut_ptr().cast(), cursor.capacity()) }; // ProcessPrng is documented as always returning `TRUE`. // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value debug_assert_eq!(ret, c::TRUE); } #[cfg(target_vendor = "win7")] -pub fn fill_bytes(mut bytes: &mut [u8]) { - while !bytes.is_empty() { - let len = bytes.len().try_into().unwrap_or(u32::MAX); - let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) }; +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + while cursor.capacity() != 0 { + let len = cursor.capacity().try_into().unwrap_or(u32::MAX); + let ret = unsafe { c::RtlGenRandom(cursor.as_mut().as_mut_ptr().cast(), len) }; assert!(ret, "failed to generate random data"); - bytes = &mut bytes[len as usize..]; + // SAFETY: We've just initialized `len` bytes + unsafe { + cursor.advance(len as usize); + } } } diff --git a/library/std/src/sys/random/zkvm.rs b/library/std/src/sys/random/zkvm.rs index 3011942f6b26b..be407d1eee6ee 100644 --- a/library/std/src/sys/random/zkvm.rs +++ b/library/std/src/sys/random/zkvm.rs @@ -1,10 +1,13 @@ +use crate::io::BorrowedCursor; +use crate::mem::MaybeUninit; use crate::sys::pal::abi; -pub fn fill_bytes(bytes: &mut [u8]) { - let (pre, words, post) = unsafe { bytes.align_to_mut::() }; +pub fn fill_buf(mut cursor: BorrowedCursor<'_, u8>) { + let bytes = cursor.as_mut(); + let (pre, words, post) = unsafe { bytes.align_to_mut::>() }; if !words.is_empty() { unsafe { - abi::sys_rand(words.as_mut_ptr(), words.len()); + abi::sys_rand(words.as_mut_ptr().cast(), words.len()); } } @@ -16,6 +19,11 @@ pub fn fill_bytes(bytes: &mut [u8]) { let buf = buf.map(u32::to_ne_bytes); let buf = buf.as_flattened(); - pre.copy_from_slice(&buf[..pre.len()]); - post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]); + pre.write_copy_of_slice(&buf[..pre.len()]); + post.write_copy_of_slice(&buf[pre.len()..pre.len() + post.len()]); + + // SAFETY: We've just initialized all the bytes with random data + unsafe { + cursor.advance(cursor.capacity()); + } } From 95a001f42d4d2f77f29651f4735ffda18c11e898 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 2 Aug 2026 11:18:02 -0700 Subject: [PATCH 2/2] Add documentation note that `fill_buf` must always fill the entire cursor --- library/core/src/random.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 6c5d41196d754..6c4dcedca26db 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -22,6 +22,8 @@ pub trait Rng { /// 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