Skip to content

Stabilize Rng and SystemRng#157168

Open
joshtriplett wants to merge 1 commit into
rust-lang:mainfrom
joshtriplett:stabilize-random-source
Open

Stabilize Rng and SystemRng#157168
joshtriplett wants to merge 1 commit into
rust-lang:mainfrom
joshtriplett:stabilize-random-source

Conversation

@joshtriplett

@joshtriplett joshtriplett commented May 30, 2026

Copy link
Copy Markdown
Member

View all comments

Stabilization report

This partial stabilization provides enough of an interface for people to obtain random bytes, which is a common need in the ecosystem, currently fulfilled via the getrandom crate.

There have been many requests for a fill_bytes interface in the standard library. Per previous libs-api discussions, SystemRng.fill_bytes can serve that function, rather than adding a separate free function.

Alternatives and Future Work

Uninitialized buffers

We're likely to add a fill_buf function to fill a BorrowedCursor<'_, u8>. We can do so once BorrowedBuf/BorrowedCursor is stable. Deferring this means we will need to support trait impls that provide fill_bytes but not fill_buf, which we might not need to if we waited until after BorrowedBuf/BorrowedCursor is stable. However, that isn't any worse of a problem than we already have with io::Read, and we don't necessarily want to couple the stabilization of BorrowedBuf/BorrowedCursor with

Distributions

The Distribution trait and the random function remain unstable; those don't need to block stabilization of Rng and SystemRng.

Optimized paths for u32/u64

Some RNGs can provide faster results for generating a whole u32/u64 rather than individual bytes.

The definition and documentation of fill_bytes says:

Note that calling fill_bytes multiple times is not equivalent to calling fill_bytes once
with a larger buffer. A RandomSource is allowed to return different bytes for those two
For instance, this allows a RandomSource to generate a word at a time and throw
of it away if not needed.

We hope that this will allow RNGs that can generate whole words to do so efficiently as a fast path in fill_bytes/fill_buf. If dedicated next_u32/next_u64 functions still end up being substantially faster, we can always add them as optional trait methods in the future.

Some experimentation suggests that it's possible to match the performance.

Result versus panicking

There's been extensive discussion about whether the function should return a Result rather than panicking, or providing an additional such function. The previous conclusion from libs-api was that while it's possible for the first such call to fail (e.g. because the OS or sandbox provides no access to randomness at all), subsequent calls should never fail, and user code will not be prepared to deal with such failure.

Furthermore, an API returning Result would propagate throughout higher-level calls, forcing operations as simple as "roll a d20" to either return Result or call expect/unwrap. And even providing a try variant will lead to higher-level APIs having to consider which variant to call. We should, instead, make the guarantee that a well-behaved underlying OS won't panic after the first call.

Note, in particular, that HashMap already fails via panic if it can't get data from its RandomState.

If there's a need to allow error recovery for the "no OS/sandbox support" case, we could provide a one-time call to check for an error. Or, such users could continue using getrandom or the underlying OS APIs.

If we did want to make every call fallible, we have the capability, using upcoming language features ("supertrait auto impl"), to add a TryRng supertrait without breaking backwards compatibility.

@joshtriplett joshtriplett added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 30, 2026
@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 30, 2026
@rust-log-analyzer

This comment has been minimized.

@joshtriplett joshtriplett force-pushed the stabilize-random-source branch from 18dd02e to eb9d7c8 Compare May 30, 2026 19:04
@jdahlstrom

Copy link
Copy Markdown

If it’s only the first call that can fail, could we put DefaultRandomSource behind a fallible constructor and guarantee that fill_bytes won’t panic?

@joshtriplett

Copy link
Copy Markdown
Member Author

@jdahlstrom That would force every caller to deal with it, albeit only once. If we (in the future) provide a fallible have_random function or similar, then people who want to rule out failure can call that (and the standard library can make sure it gets evaluated only once), but most users won't have to care about it.

@joshtriplett

Copy link
Copy Markdown
Member Author

I'm un-marking this as a draft.

Based on experiments with next_u32/next_u64, it's not clear we need them for performance. (Thanks to @hanna-kruppe for providing crates and benchmarking to help explore this! I got nerdsniped into doing some optimization on chacha8rand as a result, to verify this.)

As for fill_buf, it definitely has some value (based on benchmarks), but that doesn't mean we should block waiting on it.

@joshtriplett joshtriplett marked this pull request as ready for review May 31, 2026 17:28
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 31, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 31, 2026
@rustbot

rustbot commented May 31, 2026

Copy link
Copy Markdown
Collaborator

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 8 candidates
  • Random selection from Mark-Simulacrum, jhpratt

@joshtriplett joshtriplett added I-libs-api-nominated Nominated for discussion during a libs-api team meeting. S-waiting-on-t-libs-api Status: Awaiting decision from T-libs-api and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 31, 2026
@hanna-kruppe

hanna-kruppe commented May 31, 2026

Copy link
Copy Markdown
Contributor

cc @dhardy @newpavlov

@newpavlov

newpavlov commented May 31, 2026

Copy link
Copy Markdown
Contributor

Personally, I do not support this stabilization.

The most pressing needs can be alleviated by stabilizing a free-standing (potentially panicking) fill_bytes function. The API ignores rand_core experience and misses the SeedableRng/CryptoRng traits important in practice. I also believe there should be a clear future path for overriding the "default" RNG and using it on no_std targets.

We could call this Rng/DefaultRng rather than RandomSource/DefaultRandomSource.

IMO they should be named Rng/SysRng. For my taste, RandomSource/DefaultRandomSource are simply abhorrent.

Optimized paths for u32/u64

I don't think that added next_u32/u64 methods should have blanket impls, see here.

The previous conclusion from libs-api was that while it's possible for the first such call to fail (e.g. because the OS or sandbox provides no access to randomness at all), subsequent calls should never fail, and user code will not be prepared to deal with such failure.

This does not apply to HW-based RNGs used in cryptography. Not only they are IO-based, but also commonly use internal security checks. The same somewhat applies to RNGs built-in into CPUs. For example, RDRAND may in theory fail at any moment and some buggy AMD CPUs are known to produce bad values (e.g. after hybernation) which are guarded against with runtime checks.

In some niche cases it's also important to prove absence of panics and the suggested potentially panicking behavior will be an annoying hindrance.

Checking for errors could also be useful in scenarios where we mix entropy from different sources where failure of one source does not stop the system.

Comment thread library/core/src/random.rs Outdated
/// A source of randomness.
#[unstable(feature = "random", issue = "130703")]
#[stable(feature = "random_source", since = "CURRENT_RUSTC_VERSION")]
pub trait RandomSource {

@hanna-kruppe hanna-kruppe May 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Regarding next_u32/next_u64, while I really want DefaultRandomSource.fill_bytes (in some form) stabilized ASAP, I have reservations about leaving it at "it's not clear we need them for performance and we can add them later". Personally I'd rather err on the side of adding these methods, unless we're quite sure we will never need them, or it's clear that we can't resolve the question in a reasonable time frame.

Adding the methods after stabilization has a cost (even besides opportunity cost). As @dhardy pointed out in the past, adding provided method later means existing implementers that want to offer reproducibility (as in stability of produced values) can't override the provided methods without breaking reproducibility for users who started using those methods. And for libraries that use RNGs to sample some distribution and want to promise reproducibility of that sampling, the same problem applies if they're first written against fill_bytes and later want to use next_uN.

Another (smaller) reason to err on the side of including these is to ease the ecosystem's transition from rand traits (which have always had next_u32/u64) to the std trait. If std doesn't have the methods at first and adds them later, that's two unnecessarily transitions (rand::Rng::next_uN -> fill_bytes + uN::from_*e_bytes -> RandomSource::next_uN). Stabilizing some subset of distributions would avoid this, but the distributions are far from ready for stabilization.

Finally, while the benchmarks in #157193 and on Zulip don't have a smoking gun that the methods are necessary for performance, it's also not clear that we won't want them. Even those benchmarks show a benefit for dyn RandomSource (the only argument is whether you consider that compatible with "cares about performance"), and @dhardy previously mentioned that rand has benchmarks justifying the methods in rand's context. At minimum we should look at those benchmarks as well and see if the fill_bytes semantics (which I think matches rand's) actually works for those benchmarks as well.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IIRC, it's possible to use inlining and https://doc.rust-lang.org/std/intrinsics/fn.is_val_statically_known.html to perform these optimisations without needing the API surface.

@hanna-kruppe hanna-kruppe May 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Inlining doesn't work for dyn RandomSource. And is_val_statically_known only helps when the two implementations that have the same behavior, but in this case, some potentially desirable optimizations change behavior. (Also, the intrinsic doesn't seem to have a clear path to being exposed on stable.)

@orlp

orlp commented May 31, 2026

Copy link
Copy Markdown
Contributor

I oppose this stabilization, as I've mentioned before I don't think we are at a point where we want to stabilize traits or anything that represents or implies a canonical "way to do random number generation". The current proposal with RandomSource being a trait and potentially having multiple sources with the provided one only "being a default" is way too close to that.

There is one real need from the standard library: a (no_std overridable) source of random bytes. This should simply be a function without further baggage or API precedent.

Only once we have a clear view of what an opinionated std random API should look like should we stabilize any generic traits, distributions or generators. Not a piece-wise stabilization that will only end up shooting us in the foot later.

@dhardy

dhardy commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Based on experiments with next_u32/next_u64, it's not clear we need them for performance. (Thanks to @hanna-kruppe for providing crates and benchmarking to help explore this! I got nerdsniped into doing some optimization on chacha8rand as a result, to verify this.)

next_u32/next_u64 aren't important for block-PRNGs like ChaCha. They're for word-PRNGs like Xoshiro, PCG, SFC. (It's still unclear to me whether the RandomSource trait is supposed to be a complete replacement for rand_core::Rng or only a trait over entropy sources, though in the latter case I don't see much justification in using a trait over a free function.)

To quote the DefaultRandomSource docs:

If security is a concern, consult the platform documentation below for the specific guarantees your target provides.

This is rather vague. Would a report of a defective implementation be considered a security issue? E.g. esp_fill_random advertises "true random values" but only given some additional criteria which might enable some form of side-channel attack (or worse, given that the documentation doesn't specify that the underlying PRNG is a CSPRNG).

But my biggest concern is what happens on unsupported platforms, e.g. wasm32-unknown-unknown? I think if the rand crate were to switch to DefaultRandomSource over the getrandom crate we'd have a minor rebellion (fork) due to the lost support for wasm32-unknown-unknown (this isn't the only target of concern, but by far the most widely used from the feedback we've had).

@hanna-kruppe

Copy link
Copy Markdown
Contributor

next_u32/next_u64 aren't important for block-PRNGs like ChaCha. They're for word-PRNGs like Xoshiro, PCG, SFC.

This was my understanding as well, but when I sat down and worked through it, I couldn’t come up with a benchmark that shows a difference (between next_uN vs fill_bytes+uN::from_le_bytes). When the fill_bytes loop generates words and writes them to the buffer, I’d expect LLVM to simplify all of that away when fill_bytes can be inlined. There is a difference for the dyn Rng case since that prevents inlining, but that also hurts block based RNGs similarly. If rand has benchmarks that show something different, it would be great to know.

Maybe this has changed over time as LLVM has improved? The way rand derives fill_bytes from next_uN generically involves a lot of small fixed sized memcpys and LLVM has historically been been pretty bad at optimizing those (it’s still not great but much better now).

@ChrisDenton

Copy link
Copy Markdown
Member

But my biggest concern is what happens on unsupported platforms, e.g. wasm32-unknown-unknown? I think if the rand crate were to switch to DefaultRandomSource over the getrandom crate we'd have a minor rebellion (fork) due to the lost support for wasm32-unknown-unknown (this isn't the only target of concern, but by far the most widely used from the feedback we've had).

The specific problem with wasm32-unknown-unknown is its dual nature as both a -none target and a -web target. Which one it is depends on the user of the target. I don't think this is solvable by std without separating out those use cases. In the meantime, it seems unfortunate to block improvements to std on that.

@dhardy

dhardy commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

@hanna-kruppe I tried benchmarking Xoshiro256++, Sfc32 and Sfc64 using rand_core::utils::next_word_via_fill to implement next_uN, and only Sfc64 was significantly slower (~10%). I then tried running the rand_distr benches with a modified Pcg64Mcg without significant affect on performance (thirty benches with <1% deltas, four 2-3% faster, nine 3-6% slower, one 15% slower). So, yes, it looks like LLVM can optimise over most performance issues of next_uN -> fill_bytes -> next_uN conversions; at least, on my (Zen 3) machine using static-dispatch with lots of function inlining.

If there's desire to use only a single method, I would consider using fill_words(&mut [u32; _]) instead. This guarantees alignment >= 4, and I can't recall ever seeing a use-case for less than one u32 word of output. The caveat is worse compatibility with every other RNG interface, but ultimately does that matter? Only a few trait impls (most in std and RNG libraries) are required and most users will want a higher-level interface like RngExt or choose anyway.

@ChrisDenton

Copy link
Copy Markdown
Member

Are there any code examples in the docs? If not it'd be great to add them before stabilisation.

@the8472

the8472 commented Jun 2, 2026

Copy link
Copy Markdown
Member

From zulip discussion, there seems to be some tension between goals

  • we want to promise cryptographic quality
  • people argue for the infallible API, which requires panicking when entropy can't be supplied
  • ESP-IDF can't unconditionally provide crypto-quality (thus has to panic)
  • libs-api has some precedent that stubbing APIs via panics can be something that disqualifies a target from advancing beyond tier 3 (but this isn't hard policy)
  • Promote tier 3 riscv32 ESP-IDF targets to tier 2 compiler-team#864

@tarcieri

tarcieri commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

people argue for the infallible API, which requires panicking when entropy can't be supplied

An alternative to panicking is to seed an infallible RNG from a fallible RNG. This at least defers the error condition to something that happens once up-front, and is avoidable thereafter.

I'd probably only recommend that for bare metal embedded use cases though. Anywhere you have a proper kernel entropy pool (and potentially have to worry about forking) you're better off using that.

@joshtriplett

Copy link
Copy Markdown
Member Author

I tried benchmarking Xoshiro256++, Sfc32 and Sfc64 using rand_core::utils::next_word_via_fill to implement next_uN, and only Sfc64 was significantly slower (~10%).

Can you provide the benchmarking code? I'd love to see if we can optimize that in the style of hanna-kruppe/chacha8rand#1 .

@dhardy

dhardy commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@joshtriplett here's a diff against rand_pcg code.
pcg128.diff.txt

@rust-bors

This comment has been minimized.

@joshtriplett joshtriplett force-pushed the stabilize-random-source branch from eb9d7c8 to 44519e1 Compare June 23, 2026 15:45
@rustbot

rustbot commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@joshtriplett joshtriplett changed the title Stabilize RandomSource and DefaultRandomSource Stabilize Rng and SystemRng Jun 23, 2026
@rust-log-analyzer

This comment has been minimized.

@nia-e

nia-e commented Jun 23, 2026

Copy link
Copy Markdown
Member

@rust-rfcbot merge libs-api

@orlp

orlp commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

I find it strange we're proposing to stabilize this as an alternative for getrandom when (in order of importance):

  1. This API does not allow for a custom implementation for e.g. no_std environments, getrandom does.
  2. This API does not let you handle fallible or unavailable random sources, getrandom does.
  3. This API does not allow for filling an uninit buffer, getrandom does.

If a crate were to switch from getrandom to this, I would consider it a downgrade. If I'm a no_std user and a crate I use switches from getrandom to this, the crate would become unusable.

Most of these limitations are due to the fact that the current implementation tries to make "easy to use Rng trait for general random number generation" and "the core functionality for getting randomness from my environment" one and the same.

Everything is sacrificed for ease of use of the common case, when no such sacrifices need to be made if these two use-cases are separated in dedicated solutions.

This provides enough of an interface for people to obtain random bytes.

The `Distribution` trait and the `random` function remain unstable;
those don't need to block stabilization of `Rng` and `SystemRng`.

Similarly, this leaves a `fill_buf` function using `BorrowedCursor` as
future work.
@joshtriplett joshtriplett force-pushed the stabilize-random-source branch from 44519e1 to 5119e06 Compare June 23, 2026 16:56
@Amanieu

Amanieu commented Jun 23, 2026

Copy link
Copy Markdown
Member
  1. This API does not allow for a custom implementation for e.g. no_std environments, getrandom does.

It does allow for a custom implementation via a trait. It specifically doesn't provide a "default" random source since nobody can agree on what properties this should have.

  1. This API does not let you handle fallible or unavailable random sources, getrandom does.

Fallible random sources don't really exist in practice, and it would make the API worse if users have to handle a condition that never occurs in practice.

On the other hand I agree that unavailable random sources are something that users may want to handle. This could be handled either with SystemRng::is_available or with a fallible SystemRng::new/get constructor.

  1. This API does not allow for filling an uninit buffer, getrandom does.

This will be added separately and in a backwards-compatible way once the read_buf API is stabilized.

@Amanieu

Amanieu commented Jun 23, 2026

Copy link
Copy Markdown
Member

@rfcbot merge libs-api

@rfcbot concern unavailable SystemRng

@rust-rfcbot

rust-rfcbot commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

@Amanieu has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jun 23, 2026
@Amanieu

Amanieu commented Jun 23, 2026

Copy link
Copy Markdown
Member

@rfcbot concern unavailable SystemRng

@joshtriplett

Copy link
Copy Markdown
Member Author

Concerns that came up in @rust-lang/libs-api discussion:

We should document that SystemRng will fail if there's no system randomness available (rather than, for instance, using something insecure).

@rfcbot concern document-systemrng-will-fail-if-no-secure-randomness

We should add a module-level example in std::random. For instance, the UUIDv4 example, adapted to use SystemRng.fill_bytes.

@rfcbot concern random-module-level-example

@dhardy

dhardy commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

It does allow for a custom implementation via a trait.

As has already been said a few times, this is less useful in practice because leveraging the trait would require passing a (generic or dyn) value into any code path which might want to use a random source — e.g. any code using rand::rng().

@orlp

orlp commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

It does allow for a custom implementation via a trait. It specifically doesn't provide a "default" random source since nobody can agree on what properties this should have.

It is precisely this what getrandom provides.

It's why ahash uses it. It's why I'd like to use it in foldhash, instead of the custom hacky stack-pointer-based-randomness I do now.

It means a crate like uuid can offer Uuid::new_v4 on no_std. In turn, a crate that uses UUID v4 internally can then also remain no_std. It means any crate can use a HashDoS-resistant hasher without having to rely on std or manually exposing a "with this Rng please" on every single method which ends up using a HashMap or HashSet.

I think randomness from the environment is such a core piece of the ecosystem it should have a similar system to #[global_allocator] and alloc, such that everyone can rely on it without the need to pass around context, including people on embedded systems. E.g. we could move std::hash::RandomState to random::hash::RandomState which is usable from no_std through extern crate random, similar to extern crate alloc.

As has already been said a few times, this is less useful in practice because leveraging the trait would require passing a (generic or dyn) value into any code path which might want to use a random source — e.g. any code using rand::rng().

Precisely. And this includes any code path which might want to use a HashSet or HashMap. In other words, almost any piece of non-trivial code.

@ChrisDenton

ChrisDenton commented Jun 23, 2026

Copy link
Copy Markdown
Member

If we stabilise SystemRng now, would anything stop us adding a #![system_rng] override later?

@tarcieri

Copy link
Copy Markdown
Contributor

I think randomness from the environment is such a core piece of the ecosystem it should have a similar system to #[global_allocator] and alloc, such that everyone can rely on it without the need to pass around context, including people on embedded systems.

I'd be a little worried if this mechanism were 100% ambient and there weren't something that needed to be explicitly configured outside of your crate dependencies to elect which crate is providing your secure ambient RNG, e.g. in something like Cargo.toml.

On an embedded platform, you could imagine someone adding a crate that provides a poison ambient RNG, and then later adding a different crate implementing cryptography that calls into the ambient system RNG expecting it to be secure, but getting a poison RNG instead. Since everything got wired up automatically, this can potentially go unnoticed by the developer pulling in these dependencies.

All that is to say: I think this deserves some careful design and wouldn't like to see it block a SystemRng MVP.

@orlp

orlp commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

I think that's trivially preventable by only allowing the root crate (e.g. the binary or cdylib) to set the random source.

I also don't think the attacker model you're describing makes any sense. If you're pulling in a malicious dependency it's over anyway. They're not going to bother with 'subtly' (I don't think there's anything subtle about installing a poisoned global RNG in the first place) trying to poison your RNG source in the hopes of you generating an insecure key, considering they already have arbitrary code execution.

@joshtriplett

Copy link
Copy Markdown
Member Author

@orlp It doesn't have to be malice; it's easy to go wrong just due to differing requirements.

Scenario: you're writing a game. You find the mechanism for setting a default RNG, and that looks convenient. You set up an RNG as the default that lets you have seeded games and daily runs and similar. Later, in another part of the game, you use HTTPS, which uses the RNG for critical TLS material.

The danger here is in allowing substitution of an RNG (e.g. via EII) that isn't suitable for secure purposes. If we ever allow doing that, then everyone who needs a secure RNG can no longer safely use the system RNG.

I'm not saying we should never have an interface for that; there's value for testing, and there's value for embedded systems. (Though personally I think we should make it easier for embedded systems to more easily use a custom target without hacking on rustc and std.) But we do need to address the possibility of conflicting requirements, and that will require a non-trivial amount of design and care.

@orlp

orlp commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@joshtriplett Those are good points, I agree with those. I think that's an indicator SystemRng is too vague and it should be called SecureSystemRng or something similar.

But we do need to address the possibility of conflicting requirements, and that will require a non-trivial amount of design and care.

We're discussing this on the stabilization PR of SystemRng. The one-way street, the final choice. We can't undo this, if we stabilize it, SystemRng and Rng are here to stay in their current form.

Considering this is being stabilized to provide, and I quote, "a common need in the ecosystem, currently fulfilled via the getrandom crate", I think we should do this non-trivial amount of design and care before we make our final commitment to any design, to ensure that we do in fact fulfill the ecosystem need met by getrandom. Because I do believe the current design does not meet that goal, especially for no_std.

@ChrisDenton

If we stabilise SystemRng now, would anything stop us adding a #![system_rng] override later?

Nothing inherently no, but:

  1. We have no proposed design or concrete future plan for it. If we discover some kind of incompatibility or requirement that should have been on the original trait, or some guarantee given we shouldn't have, we're out of luck.

  2. In the meantime any crate that switches from getrandom to SystemRng will become unavailable to no_std, degrading the ecosystem.

@tarcieri

Copy link
Copy Markdown
Contributor

If you're pulling in a malicious dependency it's over anyway. They're not going to bother with 'subtly'

Noisy attacks get noticed. The thing about a poison RNG is that it can be used for stealthy kleptographic attacks, in a way that its code might even withstand a fair degree of scrutiny.

The best example of an attack like this is probably the ScreenOS backdoor. Using a specially designed RNG cascade (using the infamous Dual_EC_DRBG) the attacker (likely NSA) weaponized the system random number generator into something which would deliver an elliptic curve point to the underlying VPN implementation at precisely the right time for it to get written directly to the wire, and that curve point could be used by someone who happened to know the Dual_EC_DRBG discrete log to decrypt the VPN session. They weaponized the system RNG into something which was effectively performing public-key encryption.

Anyway, that's all again to say that I wouldn't be too hasty to try to stabilize a mechanism for overriding the system RNG, specifically because it is a juicy target for these sorts of attacks.

@ChrisDenton

Copy link
Copy Markdown
Member

We have no proposed design or concrete future plan for it. If we discover some kind of incompatibility or requirement that should have been on the original trait, or some guarantee given we shouldn't have, we're out of luck.

The trait has a single function fill_bytes and there just aren't that many options for it. The only API points of contention left are:

  • should there be a way to handle RNG initialization errors
  • should fill_bytes itself return an error
  • what should the error type be

But all those things are compatible with the infallible trait because panicking can be used to make the fallible infallible.

I think designing the override mechanism itself will present far more difficulty than any extension to the rng API.

@hanna-kruppe

hanna-kruppe commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

The getrandom crate is a combination of two things:

  • An interface for agreeing on a single source of randomness across the ecosystem, including no_std
  • A bunch of good default implementations of this interface on most common platforms

The former is hard for the standard library to provide, both because of the aforementioned issues with this global resource specifically and because the story for adding more global hooks without ad-hoc language/compiler extensions hasn't been fully figured out yet. The trait proposed for stabilization here is not an adequate substitute for reasons discussed above.

What's up for stabilization here is the latter part of getrandom: the bulk of the platform specific bindings to APIs that provide secure randomness. The interface can still be supplied by the getrandom crate as before, and std's SystemRng can be used as implementation of this interface for applications that link std anyway and don't need to support weird targets like wasm32-unknown-unknown. This would keep the existing interface working (but out of the standard library) while moving a good chunk of getrandom's implementation and unsafe code into the standard library. That also gives std-based applications that only need randomness in application code (not libraries) an option to avoid getrandom entirely.

I do see a risk that providing only a trait and not a global source encourages the ecosystem to switch away from the getrandom interface and towards trait-based dependency injection, which probably leaves the ecosystem much worse off. IMO that's the main reason to prefer #157573 which avoids stabilizing any trait while still providing an implementation in std that can be used to back a no_std-compatible interface. But all the important API questions will still have to be answered by any potential hook for overriding the standard library source, so we need a forward compatibility story there too. As others have said, starting the with the fill_bytes(dest: &mut [u8]) -> () subset seems perfectly forward compatible with adding fallible and uninit-buffer entry points, while deferring the "what's the error type" issue.

@ChrisDenton

Copy link
Copy Markdown
Member

To be clear on my personal position, I do still think a std:: random::fill_bytes standalone function would be the best way to approach the initial version of this in std. Even when new interfaces, etc are fully designed I still think it pulls its weight as the default choice for cryptography (i.e. for std users without strict no-panic requirements).

But there doesn't seem to be any libs-api consensus on that and I don't think the trait will tie std's hands. So if this is what can be broadly agreed upon for now then I'm happy to go with it.

@newpavlov

newpavlov commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

@Amanieu

Fallible random sources don't really exist in practice, and it would make the API worse if users have to handle a condition that never occurs in practice.

Plenty of sources may fail in practice. And it's not only IO-based generators, even RDRAND is technically fallible and it was encountered in practice (sure, it was a buggy CPU, but still). Just replace "random sources" with "allocators" in your comment to see the potential mess you intend to bake in.

With distinct rand_core-like TryRng/Rng traits users of infallible RNGs (or RNGs which use panics to hide potential unlikely errors) do not have to deal with potential errors.

Finally, I believe it does not make sense to introduce just the Rng trait. If RNG traits to be stabilized, I believe they should aim to completely replace rand_core. Otherwise we are going to end up with a weird situation where Rng is defined in core, but widely used SeedableRng and CryptoRng reside in rand_core.

@bjorn3

bjorn3 commented Jun 24, 2026

Copy link
Copy Markdown
Member

The hardware may be fallible in those cases, but the OS normally retries accessing the hardware source later and either blocks or keep serving from the already seeded CSPRNG when you try to get random numbers and adds entropy from other lower quality but guaranteed to exist sources like interrupt jitter (if interrupts stop your system is completely broken and no userspace apps run anyway).

@newpavlov

newpavlov commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Firstly, you can not assume what OS does. Most OSes do not make the infallibility guarantee (as an exception, modern Windows and Fuchsia do make such guarantee), for example, (IIRC) Hermit was simply forwarding RDRAND without any entropy mixing. On top of that in future we may have a way to override the system source (e.g. a cryptographic application may use an external IO-based certified RNG).

Secondly, RNG traits defined in core will be used with other RNGs as well, not only with SysRng.

@bjorn3

bjorn3 commented Jun 24, 2026

Copy link
Copy Markdown
Member

On Linux, the only documented error conditions for getrandom by glibc are either unconditional (not supported by the kernel, which would effectively be running the binary on a kernel not supported by libstd at all), if you ask for non-blocking (which libstd doesn't), should be immediately retried (EINTR) or you messed up the arguments to getrandom (and got yourself a memory safety bug). So for all practical purposes on Linux it will not fail.

Secondly, RNG traits defined in core will be used with other RNGs as well, not only with SysRng

I would expect the Rng trait in libstd to correspond to CryptoRng in rand_core.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-libs-api-nominated Nominated for discussion during a libs-api team meeting. proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-t-libs-api Status: Awaiting decision from T-libs-api T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.