From 1f042617d6133adeb0cfd5e5a139b1729cfe1c42 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 24 Sep 2022 12:38:30 +0100 Subject: [PATCH 1/4] Use a custom Debug impl for ThreadRng --- src/rngs/thread.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index baebb1d99c7..524deb01318 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -11,6 +11,7 @@ use core::cell::UnsafeCell; use std::rc::Rc; use std::thread_local; +use std::fmt; use super::std::Core; use crate::rngs::adapter::ReseedingRng; @@ -58,12 +59,19 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng /// [`StdRng`]: crate::rngs::StdRng #[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))] -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync rng: Rc>>, } +/// Debug implementation does not leak internal state +impl fmt::Debug for ThreadRng { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("ThreadRng").finish_non_exhaustive() + } +} + thread_local!( // We require Rc<..> to avoid premature freeing when thread_rng is used // within thread-local destructors. See #968. @@ -92,7 +100,7 @@ pub fn thread_rng() -> ThreadRng { impl Default for ThreadRng { fn default() -> ThreadRng { - crate::prelude::thread_rng() + thread_rng() } } @@ -140,4 +148,11 @@ mod test { r.gen::(); assert_eq!(r.gen_range(0..1), 0); } + + #[test] + fn test_debug_output() { + // We don't care about the exact output here, but it must not include + // private CSPRNG state or the cache stored by BlockRng! + assert_eq!(std::format!("{:?}", crate::thread_rng()), "ThreadRng { .. }"); + } } From 4ca7f8d6fcea45d9d32a7db1f70a888e2caca995 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 24 Sep 2022 12:40:24 +0100 Subject: [PATCH 2/4] Adjust documentation of random, thread_rng, ThreadRng --- rand_core/src/os.rs | 5 +++-- src/lib.rs | 34 +++++----------------------------- src/rng.rs | 2 +- src/rngs/thread.rs | 45 ++++++++++++++++++++++++++++++--------------- 4 files changed, 39 insertions(+), 47 deletions(-) diff --git a/rand_core/src/os.rs b/rand_core/src/os.rs index 6cd1b9cf5de..b43c9fdaf05 100644 --- a/rand_core/src/os.rs +++ b/rand_core/src/os.rs @@ -19,8 +19,9 @@ use getrandom::getrandom; /// The implementation is provided by the [getrandom] crate. Refer to /// [getrandom] documentation for details. /// -/// This struct is only available when specifying the crate feature `getrandom` -/// or `std`. When using the `rand` lib, it is also available as `rand::rngs::OsRng`. +/// This struct is available as `rand_core::OsRng` and as `rand::rngs::OsRng`. +/// In both cases, this requires the crate feature `getrandom` or `std` +/// (enabled by default in `rand` but not in `rand_core`). /// /// # Blocking and error handling /// diff --git a/src/lib.rs b/src/lib.rs index ef5c8a5a56c..e725603c11e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,39 +107,15 @@ pub use rng::{Fill, Rng}; #[cfg(all(feature = "std", feature = "std_rng"))] use crate::distributions::{Distribution, Standard}; +#[allow(unused)] +use crate::rngs::thread::ThreadRng; /// Generates a random value using the thread-local random number generator. /// -/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for -/// documentation of the entropy source and [`Standard`] for documentation of -/// distributions and type-specific generation. -/// -/// # Provided implementations -/// -/// The following types have provided implementations that -/// generate values with the following ranges and distributions: -/// -/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed -/// over all values of the type. -/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all -/// code points in the range `0...0x10_FFFF`, except for the range -/// `0xD800...0xDFFF` (the surrogate code points). This includes -/// unassigned/reserved code points. -/// * `bool`: Generates `false` or `true`, each with probability 0.5. -/// * Floating point types (`f32` and `f64`): Uniformly distributed in the -/// half-open range `[0, 1)`. See notes below. -/// * Wrapping integers (`Wrapping`), besides the type identical to their -/// normal integer variants. -/// -/// Also supported is the generation of the following -/// compound types where all component types are supported: +/// This function is simply a shortcut for `thread_rng().gen()`: /// -/// * Tuples (up to 12 elements): each element is generated sequentially. -/// * Arrays (up to 32 elements): each element is generated sequentially; -/// see also [`Rng::fill`] which supports arbitrary array length for integer -/// types and tends to be faster for `u32` and smaller types. -/// * `Option` first generates a `bool`, and if true generates and returns -/// `Some(value)` where `value: T`, otherwise returning `None`. +/// - See [`ThreadRng`] for documentation of the generator and security +/// - See [`Standard`] for documentation of supported types and distributions /// /// # Examples /// diff --git a/src/rng.rs b/src/rng.rs index e82f18854e8..c9f3a5f72e5 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -53,7 +53,7 @@ use core::{mem, slice}; /// # let v = foo(&mut thread_rng()); /// ``` pub trait Rng: RngCore { - /// Return a random value supporting the [`Standard`] distribution. + /// Return a random value via the [`Standard`] distribution. /// /// # Example /// diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 524deb01318..9888e9c806d 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -40,21 +40,26 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// A reference to the thread-local generator /// +/// This type is a reference to a lazily-initialized thread-local generator. /// An instance can be obtained via [`thread_rng`] or via `ThreadRng::default()`. /// This handle is safe to use everywhere (including thread-local destructors), /// though it is recommended not to use inside a fork handler. /// The handle cannot be passed between threads (is not `Send` or `Sync`). /// -/// `ThreadRng` uses the same PRNG as [`StdRng`] for security and performance -/// and is automatically seeded from [`OsRng`]. +/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with +/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations +/// of security and performance. /// -/// Unlike `StdRng`, `ThreadRng` uses the [`ReseedingRng`] wrapper to reseed -/// the PRNG from fresh entropy every 64 kiB of random data as well as after a -/// fork on Unix (though not quite immediately; see documentation of -/// [`ReseedingRng`]). -/// Note that the reseeding is done as an extra precaution against side-channel -/// attacks and mis-use (e.g. if somehow weak entropy were supplied initially). -/// The PRNG algorithms used are assumed to be secure. +/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding +/// (every 64 kiB, as well as "soon" after a fork on Unix — see [`ReseedingRng`] +/// documentation for details). +/// +/// Security must be considered relative to a thread model and validation +/// requirements. `ThreadRng` attempts to meet basic security considerations +/// for producing unpredictable random numbers: use a CSPRNG, use a +/// recommended platform-specific seed ([`OsRng`]), and avoid +/// leaking internal secrets e.g. via [`Debug`] implementation or serialization. +/// Memory is not zeroized on drop. /// /// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng /// [`StdRng`]: crate::rngs::StdRng @@ -85,13 +90,23 @@ thread_local!( } ); -/// Retrieve the lazily-initialized thread-local random number generator, -/// seeded by the system. Intended to be used in method chaining style, -/// e.g. `thread_rng().gen::()`, or cached locally, e.g. -/// `let mut rng = thread_rng();`. Invoked by the `Default` trait, making -/// `ThreadRng::default()` equivalent. +/// Access the thread-local generator +/// +/// Returns a reference to the local [`ThreadRng`], initializing the generator +/// on the first call on each thread. +/// +/// Example usage: +/// ``` +/// use rand::Rng; +/// +/// # fn main() { +/// // rand::random() may be used instead of rand::thread_rng().gen(): +/// println!("A random boolean: {}", rand::random::()); /// -/// For more information see [`ThreadRng`]. +/// let mut rng = rand::thread_rng(); +/// println!("A simulated die roll: {}", rng.gen_range(1..=6)); +/// # } +/// ``` #[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))] pub fn thread_rng() -> ThreadRng { let rng = THREAD_RNG_KEY.with(|t| t.clone()); From 23e4be659f2b4fb0a632b4edfd5681dd26abfc64 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 24 Sep 2022 14:23:27 +0100 Subject: [PATCH 3/4] Fix no-std build --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e725603c11e..755b5ba6e9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,8 +107,6 @@ pub use rng::{Fill, Rng}; #[cfg(all(feature = "std", feature = "std_rng"))] use crate::distributions::{Distribution, Standard}; -#[allow(unused)] -use crate::rngs::thread::ThreadRng; /// Generates a random value using the thread-local random number generator. /// @@ -153,6 +151,7 @@ use crate::rngs::thread::ThreadRng; /// ``` /// /// [`Standard`]: distributions::Standard +/// [`ThreadRng`]: rngs::ThreadRng #[cfg(all(feature = "std", feature = "std_rng"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))] #[inline] From 27302107e6ff73a2a2016f42e7f86c3269ee3907 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 29 Sep 2022 17:54:04 +0100 Subject: [PATCH 4/4] Compatibility with older rustc --- src/rngs/thread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 9888e9c806d..673c9a879d5 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -73,7 +73,7 @@ pub struct ThreadRng { /// Debug implementation does not leak internal state impl fmt::Debug for ThreadRng { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("ThreadRng").finish_non_exhaustive() + write!(fmt, "ThreadRng {{ .. }}") } }