Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc: improve random, thread_rng, ThreadRng docs #1257

Merged
merged 4 commits into from
Sep 30, 2022
Merged
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
5 changes: 3 additions & 2 deletions rand_core/src/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down
33 changes: 4 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,10 @@ use crate::distributions::{Distribution, Standard};

/// 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.
/// This function is simply a shortcut for `thread_rng().gen()`:
///
/// # 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<T>`), 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:
///
/// * 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<T>` 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
///
Expand Down Expand Up @@ -177,6 +151,7 @@ use crate::distributions::{Distribution, Standard};
/// ```
///
/// [`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]
Expand Down
2 changes: 1 addition & 1 deletion src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down
64 changes: 47 additions & 17 deletions src/rngs/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,31 +40,43 @@ 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: threat -> threat

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but this is merged already. Can you make a PR? (Should be possible through the GitHub web UI.)

/// 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
#[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<UnsafeCell<ReseedingRng<Core, OsRng>>>,
}

/// Debug implementation does not leak internal state
impl fmt::Debug for ThreadRng {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "ThreadRng {{ .. }}")
}
}

thread_local!(
// We require Rc<..> to avoid premature freeing when thread_rng is used
// within thread-local destructors. See #968.
Expand All @@ -77,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::<i32>()`, 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.
///
/// For more information see [`ThreadRng`].
/// Example usage:
/// ```
/// use rand::Rng;
///
/// # fn main() {
/// // rand::random() may be used instead of rand::thread_rng().gen():
/// println!("A random boolean: {}", rand::random::<bool>());
///
/// 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());
Expand All @@ -92,7 +115,7 @@ pub fn thread_rng() -> ThreadRng {

impl Default for ThreadRng {
fn default() -> ThreadRng {
crate::prelude::thread_rng()
thread_rng()
}
}

Expand Down Expand Up @@ -140,4 +163,11 @@ mod test {
r.gen::<i32>();
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 { .. }");
}
}