From 0ce9764cc735f588e0695a792ca0f120af21f491 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 14:56:40 +0000 Subject: [PATCH 1/8] Make ReseedingRng private --- src/rngs/mod.rs | 1 - src/rngs/reseeding.rs | 49 ------------------------------------------- src/rngs/thread.rs | 8 +++---- 3 files changed, 3 insertions(+), 55 deletions(-) diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 70f9f9dd2c..91afe244db 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -95,7 +95,6 @@ //! [xoshiro]: https://prng.di.unimi.it/ mod reseeding; -pub use reseeding::ReseedingRng; #[cfg(feature = "small_rng")] mod small; diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 8e14548d35..c09090e2c4 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -18,55 +18,6 @@ use rand_core::{SeedableRng, TryCryptoRng, TryRng}; /// A wrapper around any PRNG that implements [`Generator`], that adds the /// ability to reseed it. -/// -/// `ReseedingRng` reseeds the underlying PRNG in the following cases: -/// -/// - On a manual call to [`reseed()`]. -/// - After the PRNG has generated a configurable number of random bytes. -/// -/// # When should reseeding after a fixed number of generated bytes be used? -/// -/// Reseeding after a fixed number of generated bytes is never strictly -/// *necessary*. Cryptographic PRNGs don't have a limited number of bytes they -/// can output, or at least not a limit reachable in any practical way. There is -/// no such thing as 'running out of entropy'. -/// -/// Occasionally reseeding can be seen as some form of 'security in depth'. Even -/// if in the future a cryptographic weakness is found in the CSPRNG being used, -/// or a flaw in the implementation, occasionally reseeding should make -/// exploiting it much more difficult or even impossible. -/// -/// Use [`ReseedingRng::new`] with a `threshold` of `0` to disable reseeding -/// after a fixed number of generated bytes. -/// -/// # Error handling -/// -/// Although unlikely, reseeding the wrapped PRNG can fail. `ReseedingRng` will -/// never panic but try to handle the error intelligently through some -/// combination of retrying and delaying reseeding until later. -/// If handling the source error fails `ReseedingRng` will continue generating -/// data from the wrapped PRNG without reseeding. -/// -/// Manually calling [`reseed()`] will not have this retry or delay logic, but -/// reports the error. -/// -/// # Example -/// -/// ``` -/// use chacha20::ChaCha20Core; // Internal part of ChaChaRng that -/// // implements Generator -/// use rand::prelude::*; -/// use rand::rngs::SysRng; -/// use rand::rngs::ReseedingRng; -/// -/// let mut reseeding_rng = ReseedingRng::::new(0, SysRng).unwrap(); -/// -/// println!("{}", reseeding_rng.random::()); -/// ``` -/// -/// [`Generator`]: rand_core::block::Generator -/// [`ReseedingRng::new`]: ReseedingRng::new -/// [`reseed()`]: ReseedingRng::reseed #[derive(Debug)] pub struct ReseedingRng(BlockRng>) where diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 529b004901..586355c5aa 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -13,7 +13,7 @@ use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{ReseedingRng, SysError, SysRng, std::Core}; +use super::{SysError, SysRng, reseeding::ReseedingRng, std::Core}; use rand_core::{TryCryptoRng, TryRng}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -48,9 +48,8 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// requirements. The Rand project can provide no guarantee of fitness for /// purpose. The design criteria for `ThreadRng` are as follows: /// -/// - Automatic seeding via [`SysRng`] and periodically thereafter (see -/// ([`ReseedingRng`] documentation). Limitation: there is no automatic -/// reseeding on process fork (see [below](#fork)). +/// - Automatic seeding via [`SysRng`] and after every 64 kB of output. +/// Limitation: there is no automatic reseeding on process fork (see [below](#fork)). /// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator /// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)). /// The currently selected algorithm is ChaCha (12-rounds). @@ -85,7 +84,6 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no /// other method on the same `ThreadRng` is currently executing. /// -/// [`ReseedingRng`]: crate::rngs::ReseedingRng /// [`StdRng`]: crate::rngs::StdRng #[derive(Clone)] pub struct ThreadRng { From 9499e7c1eea1880e66e872014dd7d4ac0b2e6688 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:02:58 +0000 Subject: [PATCH 2/8] Inline ReseedingRng methods --- src/rngs/reseeding.rs | 78 +++---------------------------------------- src/rngs/thread.rs | 7 ++-- 2 files changed, 8 insertions(+), 77 deletions(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index c09090e2c4..5caf3781a9 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -10,16 +10,15 @@ //! A wrapper around another PRNG that reseeds it after it //! generates a certain number of random bytes. -use core::convert::Infallible; use core::mem::size_of_val; -use rand_core::block::{BlockRng, CryptoGenerator, Generator}; -use rand_core::{SeedableRng, TryCryptoRng, TryRng}; +use rand_core::block::{BlockRng, Generator}; +use rand_core::{SeedableRng, TryRng}; /// A wrapper around any PRNG that implements [`Generator`], that adds the /// ability to reseed it. #[derive(Debug)] -pub struct ReseedingRng(BlockRng>) +pub struct ReseedingRng(pub BlockRng>) where G: Generator + SeedableRng, Rsdr: TryRng; @@ -49,41 +48,8 @@ where self.0.core.reseed() } } - -// TODO: this should be implemented for any type where the inner type -// implements TryRng, but we can't specify that because ReseedingCore is private -impl TryRng for ReseedingRng -where - G: Generator + SeedableRng, - Rsdr: TryRng, -{ - type Error = Infallible; - - #[inline(always)] - fn try_next_u32(&mut self) -> Result { - Ok(self.0.next_word()) - } - - #[inline(always)] - fn try_next_u64(&mut self) -> Result { - Ok(self.0.next_u64_from_u32()) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> { - self.0.fill_bytes(dest); - Ok(()) - } -} - -impl TryCryptoRng for ReseedingRng -where - G: Generator + SeedableRng + CryptoGenerator, - Rsdr: TryCryptoRng, -{ -} - #[derive(Debug)] -struct ReseedingCore { +pub struct ReseedingCore { inner: G, reseeder: Rsdr, threshold: i64, @@ -165,39 +131,3 @@ where self.inner.generate(results); } } - -impl CryptoGenerator for ReseedingCore -where - G: Generator + SeedableRng + CryptoGenerator, - Rsdr: TryCryptoRng, -{ -} - -#[cfg(feature = "std_rng")] -#[cfg(test)] -mod test { - use crate::RngExt; - use crate::rngs::std::Core; - use crate::test::const_rng; - - use super::ReseedingRng; - - #[test] - fn test_reseeding() { - let zero = const_rng(0); - let thresh = 1; // reseed every time the buffer is exhausted - let mut reseeding = ReseedingRng::::new(thresh, zero).unwrap(); - - // RNG buffer size is [u32; 64] - // Debug is only implemented up to length 32 so use two arrays - let mut buf = ([0u32; 32], [0u32; 32]); - reseeding.fill(&mut buf.0); - reseeding.fill(&mut buf.1); - let seq = buf; - for _ in 0..10 { - reseeding.fill(&mut buf.0); - reseeding.fill(&mut buf.1); - assert_eq!(buf, seq); - } - } -} diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 586355c5aa..677016a230 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -168,7 +168,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_next_u32() + Ok(rng.0.next_word()) } #[inline(always)] @@ -176,7 +176,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_next_u64() + Ok(rng.0.next_u64_from_u32()) } #[inline(always)] @@ -184,7 +184,8 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.try_fill_bytes(dest) + rng.0.fill_bytes(dest); + Ok(()) } } From 36307d8f1758e5bbf987d57da4570f049d7f186c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:21:47 +0000 Subject: [PATCH 3/8] Fix reseeding source to SysRng --- src/rngs/reseeding.rs | 37 +++++++++++++++---------------------- src/rngs/thread.rs | 9 ++++----- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 5caf3781a9..7900979d24 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -12,21 +12,20 @@ use core::mem::size_of_val; +use super::{SysError, SysRng}; +use rand_core::SeedableRng; use rand_core::block::{BlockRng, Generator}; -use rand_core::{SeedableRng, TryRng}; /// A wrapper around any PRNG that implements [`Generator`], that adds the /// ability to reseed it. #[derive(Debug)] -pub struct ReseedingRng(pub BlockRng>) +pub struct ReseedingRng(pub BlockRng>) where - G: Generator + SeedableRng, - Rsdr: TryRng; + G: Generator + SeedableRng; -impl ReseedingRng +impl ReseedingRng where G: Generator + SeedableRng, - Rsdr: TryRng, { /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG /// to use as reseeder. @@ -34,32 +33,28 @@ where /// `threshold` sets the number of generated bytes after which to reseed the /// PRNG. Set it to zero to never reseed based on the number of generated /// values. - pub fn new(threshold: u64, reseeder: Rsdr) -> Result { - Ok(ReseedingRng(BlockRng::new(ReseedingCore::new( - threshold, reseeder, - )?))) + pub fn new(threshold: u64) -> Result { + Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(threshold)?))) } /// Immediately reseed the generator /// /// This discards any remaining random data in the cache. - pub fn reseed(&mut self) -> Result<(), Rsdr::Error> { + pub fn reseed(&mut self) -> Result<(), SysError> { self.0.reset_and_skip(0); self.0.core.reseed() } } #[derive(Debug)] -pub struct ReseedingCore { +pub struct ReseedingCore { inner: G, - reseeder: Rsdr, threshold: i64, bytes_until_reseed: i64, } -impl Generator for ReseedingCore +impl Generator for ReseedingCore where G: Generator + SeedableRng, - Rsdr: TryRng, { type Output = ::Output; @@ -76,16 +71,15 @@ where } } -impl ReseedingCore +impl ReseedingCore where G: Generator + SeedableRng, - Rsdr: TryRng, { /// Create a new `ReseedingCore`. /// /// `threshold` is the maximum number of bytes produced by /// [`Generator::generate`] before attempting reseeding. - fn new(threshold: u64, mut reseeder: Rsdr) -> Result { + fn new(threshold: u64) -> Result { // Because generating more values than `i64::MAX` takes centuries on // current hardware, we just clamp to that value. // Also we set a threshold of 0, which indicates no limit, to that @@ -98,19 +92,18 @@ where i64::MAX }; - let inner = G::try_from_rng(&mut reseeder)?; + let inner = G::try_from_rng(&mut SysRng)?; Ok(ReseedingCore { inner, - reseeder, threshold, bytes_until_reseed: threshold, }) } /// Reseed the internal PRNG. - fn reseed(&mut self) -> Result<(), Rsdr::Error> { - G::try_from_rng(&mut self.reseeder).map(|result| { + fn reseed(&mut self) -> Result<(), SysError> { + G::try_from_rng(&mut SysRng).map(|result| { self.bytes_until_reseed = self.threshold; self.inner = result }) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 677016a230..4502468cc6 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -13,7 +13,7 @@ use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{SysError, SysRng, reseeding::ReseedingRng, std::Core}; +use super::{SysError, reseeding::ReseedingRng, std::Core}; use rand_core::{TryCryptoRng, TryRng}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -88,7 +88,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>>, + rng: Rc>>, } impl ThreadRng { @@ -113,9 +113,8 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { - let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD, - SysRng).unwrap_or_else(|err| + static THREAD_RNG_KEY: Rc>> = { + let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD).unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); Rc::new(UnsafeCell::new(rng)) } From c7a5353efdbe1d06b59918da69f4103f2838e0f6 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:25:13 +0000 Subject: [PATCH 4/8] Fix reseeding core --- src/rngs/reseeding.rs | 41 ++++++++++++++++------------------------- src/rngs/thread.rs | 6 +++--- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 7900979d24..5745624c89 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -12,21 +12,18 @@ use core::mem::size_of_val; +use super::std::Core; use super::{SysError, SysRng}; use rand_core::SeedableRng; use rand_core::block::{BlockRng, Generator}; +type Results = ::Output; + /// A wrapper around any PRNG that implements [`Generator`], that adds the /// ability to reseed it. -#[derive(Debug)] -pub struct ReseedingRng(pub BlockRng>) -where - G: Generator + SeedableRng; - -impl ReseedingRng -where - G: Generator + SeedableRng, -{ +pub struct ReseedingRng(pub BlockRng); + +impl ReseedingRng { /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG /// to use as reseeder. /// @@ -45,20 +42,17 @@ where self.0.core.reseed() } } -#[derive(Debug)] -pub struct ReseedingCore { - inner: G, + +pub struct ReseedingCore { + inner: Core, threshold: i64, bytes_until_reseed: i64, } -impl Generator for ReseedingCore -where - G: Generator + SeedableRng, -{ - type Output = ::Output; +impl Generator for ReseedingCore { + type Output = Results; - fn generate(&mut self, results: &mut Self::Output) { + fn generate(&mut self, results: &mut Results) { if self.bytes_until_reseed <= 0 { // We get better performance by not calling only `reseed` here // and continuing with the rest of the function, but by directly @@ -71,10 +65,7 @@ where } } -impl ReseedingCore -where - G: Generator + SeedableRng, -{ +impl ReseedingCore { /// Create a new `ReseedingCore`. /// /// `threshold` is the maximum number of bytes produced by @@ -92,7 +83,7 @@ where i64::MAX }; - let inner = G::try_from_rng(&mut SysRng)?; + let inner = Core::try_from_rng(&mut SysRng)?; Ok(ReseedingCore { inner, @@ -103,14 +94,14 @@ where /// Reseed the internal PRNG. fn reseed(&mut self) -> Result<(), SysError> { - G::try_from_rng(&mut SysRng).map(|result| { + Core::try_from_rng(&mut SysRng).map(|result| { self.bytes_until_reseed = self.threshold; self.inner = result }) } #[inline(never)] - fn reseed_and_generate(&mut self, results: &mut G::Output) { + fn reseed_and_generate(&mut self, results: &mut Results) { trace!("Reseeding RNG (periodic reseed)"); let num_bytes = size_of_val(results); diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 4502468cc6..20aa4e972f 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -13,7 +13,7 @@ use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{SysError, reseeding::ReseedingRng, std::Core}; +use super::{SysError, reseeding::ReseedingRng}; use rand_core::{TryCryptoRng, TryRng}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -88,7 +88,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>>, + rng: Rc>, } impl ThreadRng { @@ -113,7 +113,7 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { + static THREAD_RNG_KEY: Rc> = { let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD).unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); Rc::new(UnsafeCell::new(rng)) From a03188fd5ef2a10f83901b2b0142beb6a508bac2 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:26:50 +0000 Subject: [PATCH 5/8] Move ReseedingRng --- src/rngs/mod.rs | 2 - src/rngs/reseeding.rs | 117 ------------------------------------------ src/rngs/thread.rs | 105 ++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 120 deletions(-) delete mode 100644 src/rngs/reseeding.rs diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 91afe244db..a9b5566b18 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -94,8 +94,6 @@ //! [PCG]: https://www.pcg-random.org/ //! [xoshiro]: https://prng.di.unimi.it/ -mod reseeding; - #[cfg(feature = "small_rng")] mod small; #[cfg(feature = "small_rng")] diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs deleted file mode 100644 index 5745624c89..0000000000 --- a/src/rngs/reseeding.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A wrapper around another PRNG that reseeds it after it -//! generates a certain number of random bytes. - -use core::mem::size_of_val; - -use super::std::Core; -use super::{SysError, SysRng}; -use rand_core::SeedableRng; -use rand_core::block::{BlockRng, Generator}; - -type Results = ::Output; - -/// A wrapper around any PRNG that implements [`Generator`], that adds the -/// ability to reseed it. -pub struct ReseedingRng(pub BlockRng); - -impl ReseedingRng { - /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG - /// to use as reseeder. - /// - /// `threshold` sets the number of generated bytes after which to reseed the - /// PRNG. Set it to zero to never reseed based on the number of generated - /// values. - pub fn new(threshold: u64) -> Result { - Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(threshold)?))) - } - - /// Immediately reseed the generator - /// - /// This discards any remaining random data in the cache. - pub fn reseed(&mut self) -> Result<(), SysError> { - self.0.reset_and_skip(0); - self.0.core.reseed() - } -} - -pub struct ReseedingCore { - inner: Core, - threshold: i64, - bytes_until_reseed: i64, -} - -impl Generator for ReseedingCore { - type Output = Results; - - fn generate(&mut self, results: &mut Results) { - if self.bytes_until_reseed <= 0 { - // We get better performance by not calling only `reseed` here - // and continuing with the rest of the function, but by directly - // returning from a non-inlined function. - return self.reseed_and_generate(results); - } - let num_bytes = size_of_val(results); - self.bytes_until_reseed -= num_bytes as i64; - self.inner.generate(results); - } -} - -impl ReseedingCore { - /// Create a new `ReseedingCore`. - /// - /// `threshold` is the maximum number of bytes produced by - /// [`Generator::generate`] before attempting reseeding. - fn new(threshold: u64) -> Result { - // Because generating more values than `i64::MAX` takes centuries on - // current hardware, we just clamp to that value. - // Also we set a threshold of 0, which indicates no limit, to that - // value. - let threshold = if threshold == 0 { - i64::MAX - } else if threshold <= i64::MAX as u64 { - threshold as i64 - } else { - i64::MAX - }; - - let inner = Core::try_from_rng(&mut SysRng)?; - - Ok(ReseedingCore { - inner, - threshold, - bytes_until_reseed: threshold, - }) - } - - /// Reseed the internal PRNG. - fn reseed(&mut self) -> Result<(), SysError> { - Core::try_from_rng(&mut SysRng).map(|result| { - self.bytes_until_reseed = self.threshold; - self.inner = result - }) - } - - #[inline(never)] - fn reseed_and_generate(&mut self, results: &mut Results) { - trace!("Reseeding RNG (periodic reseed)"); - - let num_bytes = size_of_val(results); - - if let Err(e) = self.reseed() { - warn!("Reseeding RNG failed: {}", e); - let _ = e; - } - - self.bytes_until_reseed = self.threshold - num_bytes as i64; - self.inner.generate(results); - } -} diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 20aa4e972f..1085c4e810 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -8,12 +8,16 @@ //! Thread-local random number generator +use core::mem::size_of_val; use core::{cell::UnsafeCell, convert::Infallible}; use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{SysError, reseeding::ReseedingRng}; +use super::std::Core; +use super::{SysError, SysRng}; +use rand_core::SeedableRng; +use rand_core::block::{BlockRng, Generator}; use rand_core::{TryCryptoRng, TryRng}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -35,6 +39,105 @@ use rand_core::{TryCryptoRng, TryRng}; // of 32 kB and less. We choose 64 kB to avoid significant overhead. const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; +type Results = ::Output; + +/// A wrapper around any PRNG that implements [`Generator`], that adds the +/// ability to reseed it. +struct ReseedingRng(BlockRng); + +impl ReseedingRng { + /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG + /// to use as reseeder. + /// + /// `threshold` sets the number of generated bytes after which to reseed the + /// PRNG. Set it to zero to never reseed based on the number of generated + /// values. + fn new(threshold: u64) -> Result { + Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(threshold)?))) + } + + /// Immediately reseed the generator + /// + /// This discards any remaining random data in the cache. + fn reseed(&mut self) -> Result<(), SysError> { + self.0.reset_and_skip(0); + self.0.core.reseed() + } +} + +struct ReseedingCore { + inner: Core, + threshold: i64, + bytes_until_reseed: i64, +} + +impl Generator for ReseedingCore { + type Output = Results; + + fn generate(&mut self, results: &mut Results) { + if self.bytes_until_reseed <= 0 { + // We get better performance by not calling only `reseed` here + // and continuing with the rest of the function, but by directly + // returning from a non-inlined function. + return self.reseed_and_generate(results); + } + let num_bytes = size_of_val(results); + self.bytes_until_reseed -= num_bytes as i64; + self.inner.generate(results); + } +} + +impl ReseedingCore { + /// Create a new `ReseedingCore`. + /// + /// `threshold` is the maximum number of bytes produced by + /// [`Generator::generate`] before attempting reseeding. + fn new(threshold: u64) -> Result { + // Because generating more values than `i64::MAX` takes centuries on + // current hardware, we just clamp to that value. + // Also we set a threshold of 0, which indicates no limit, to that + // value. + let threshold = if threshold == 0 { + i64::MAX + } else if threshold <= i64::MAX as u64 { + threshold as i64 + } else { + i64::MAX + }; + + let inner = Core::try_from_rng(&mut SysRng)?; + + Ok(ReseedingCore { + inner, + threshold, + bytes_until_reseed: threshold, + }) + } + + /// Reseed the internal PRNG. + fn reseed(&mut self) -> Result<(), SysError> { + Core::try_from_rng(&mut SysRng).map(|result| { + self.bytes_until_reseed = self.threshold; + self.inner = result + }) + } + + #[inline(never)] + fn reseed_and_generate(&mut self, results: &mut Results) { + trace!("Reseeding RNG (periodic reseed)"); + + let num_bytes = size_of_val(results); + + if let Err(e) = self.reseed() { + warn!("Reseeding RNG failed: {}", e); + let _ = e; + } + + self.bytes_until_reseed = self.threshold - num_bytes as i64; + self.inner.generate(results); + } +} + /// A reference to the thread-local generator /// /// This type is a reference to a lazily-initialized thread-local generator. From 83111c228c5585f7d8654b8229a8ee7e3f3bed4c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:32:02 +0000 Subject: [PATCH 6/8] Fix reseeding threshold --- src/rngs/thread.rs | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 1085c4e810..bdfe70e63d 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -37,7 +37,7 @@ use rand_core::{TryCryptoRng, TryRng}; // Number of generated bytes after which to reseed `ThreadRng`. // According to benchmarks, reseeding has a noticeable impact with thresholds // of 32 kB and less. We choose 64 kB to avoid significant overhead. -const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; +const THREAD_RNG_RESEED_THRESHOLD: i64 = 1024 * 64; type Results = ::Output; @@ -46,16 +46,6 @@ type Results = ::Output; struct ReseedingRng(BlockRng); impl ReseedingRng { - /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG - /// to use as reseeder. - /// - /// `threshold` sets the number of generated bytes after which to reseed the - /// PRNG. Set it to zero to never reseed based on the number of generated - /// values. - fn new(threshold: u64) -> Result { - Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(threshold)?))) - } - /// Immediately reseed the generator /// /// This discards any remaining random data in the cache. @@ -67,7 +57,6 @@ impl ReseedingRng { struct ReseedingCore { inner: Core, - threshold: i64, bytes_until_reseed: i64, } @@ -89,35 +78,19 @@ impl Generator for ReseedingCore { impl ReseedingCore { /// Create a new `ReseedingCore`. - /// - /// `threshold` is the maximum number of bytes produced by - /// [`Generator::generate`] before attempting reseeding. - fn new(threshold: u64) -> Result { - // Because generating more values than `i64::MAX` takes centuries on - // current hardware, we just clamp to that value. - // Also we set a threshold of 0, which indicates no limit, to that - // value. - let threshold = if threshold == 0 { - i64::MAX - } else if threshold <= i64::MAX as u64 { - threshold as i64 - } else { - i64::MAX - }; - + fn new() -> Result { let inner = Core::try_from_rng(&mut SysRng)?; Ok(ReseedingCore { inner, - threshold, - bytes_until_reseed: threshold, + bytes_until_reseed: THREAD_RNG_RESEED_THRESHOLD, }) } /// Reseed the internal PRNG. fn reseed(&mut self) -> Result<(), SysError> { Core::try_from_rng(&mut SysRng).map(|result| { - self.bytes_until_reseed = self.threshold; + self.bytes_until_reseed = THREAD_RNG_RESEED_THRESHOLD; self.inner = result }) } @@ -133,7 +106,7 @@ impl ReseedingCore { let _ = e; } - self.bytes_until_reseed = self.threshold - num_bytes as i64; + self.bytes_until_reseed = THREAD_RNG_RESEED_THRESHOLD - num_bytes as i64; self.inner.generate(results); } } @@ -217,9 +190,9 @@ thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. static THREAD_RNG_KEY: Rc> = { - let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD).unwrap_or_else(|err| + let core = ReseedingCore::new().unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); - Rc::new(UnsafeCell::new(rng)) + Rc::new(UnsafeCell::new(ReseedingRng(BlockRng::new(core)))) } ); From ea008592bdd9900dd618cbc3d899f2180d111208 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:35:29 +0000 Subject: [PATCH 7/8] Remove ReseedingRng --- src/rngs/thread.rs | 46 +++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index bdfe70e63d..5008d34b4c 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -41,20 +41,6 @@ const THREAD_RNG_RESEED_THRESHOLD: i64 = 1024 * 64; type Results = ::Output; -/// A wrapper around any PRNG that implements [`Generator`], that adds the -/// ability to reseed it. -struct ReseedingRng(BlockRng); - -impl ReseedingRng { - /// Immediately reseed the generator - /// - /// This discards any remaining random data in the cache. - fn reseed(&mut self) -> Result<(), SysError> { - self.0.reset_and_skip(0); - self.0.core.reseed() - } -} - struct ReseedingCore { inner: Core, bytes_until_reseed: i64, @@ -77,16 +63,6 @@ impl Generator for ReseedingCore { } impl ReseedingCore { - /// Create a new `ReseedingCore`. - fn new() -> Result { - let inner = Core::try_from_rng(&mut SysRng)?; - - Ok(ReseedingCore { - inner, - bytes_until_reseed: THREAD_RNG_RESEED_THRESHOLD, - }) - } - /// Reseed the internal PRNG. fn reseed(&mut self) -> Result<(), SysError> { Core::try_from_rng(&mut SysRng).map(|result| { @@ -164,7 +140,7 @@ impl ReseedingCore { #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>, + rng: Rc>>, } impl ThreadRng { @@ -175,7 +151,8 @@ impl ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.reseed() + rng.reset_and_skip(0); + rng.core.reseed() } } @@ -189,10 +166,13 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc> = { - let core = ReseedingCore::new().unwrap_or_else(|err| - panic!("could not initialize ThreadRng: {}", err)); - Rc::new(UnsafeCell::new(ReseedingRng(BlockRng::new(core)))) + static THREAD_RNG_KEY: Rc>> = { + Rc::new(UnsafeCell::new(BlockRng::new(ReseedingCore { + inner: Core::try_from_rng(&mut SysRng).unwrap_or_else(|err| { + panic!("could not initialize ThreadRng: {}", err) + }), + bytes_until_reseed: THREAD_RNG_RESEED_THRESHOLD, + }))) } ); @@ -243,7 +223,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - Ok(rng.0.next_word()) + Ok(rng.next_word()) } #[inline(always)] @@ -251,7 +231,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - Ok(rng.0.next_u64_from_u32()) + Ok(rng.next_u64_from_u32()) } #[inline(always)] @@ -259,7 +239,7 @@ impl TryRng for ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let rng = unsafe { &mut *self.rng.get() }; - rng.0.fill_bytes(dest); + rng.fill_bytes(dest); Ok(()) } } From 3bee44674cb19c74a2f6e8d3ae4e9112c57ec2fe Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 28 Jan 2026 15:44:05 +0000 Subject: [PATCH 8/8] CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df174a78a3..c341f0e50a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,13 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. - Pub export `Xoshiro128PlusPlus`, `Xoshiro256PlusPlus` prngs (#1649) - Pub export `ChaCha8Rng`, `ChaCha12Rng`, `ChaCha20Rng` behind `chacha` feature (#1659) +### Removals +- Removed `ReseedingRng` ([#1722]) + [#1695]: https://github.com/rust-random/rand/pull/1695 [#1697]: https://github.com/rust-random/rand/pull/1697 [#1717]: https://github.com/rust-random/rand/pull/1717 +[#1722]: https://github.com/rust-random/rand/pull/1722 ## [0.9.2] - 2025-07-20 ### Deprecated