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

Make unpredictableSeed use getrandom (syscall) on Linux #10623

Merged
merged 1 commit into from
Jan 25, 2025
Merged
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
82 changes: 80 additions & 2 deletions std/random.d
Original file line number Diff line number Diff line change
Expand Up @@ -1772,11 +1772,53 @@ else
}
}

version (linux)
{
// `getrandom()` was introduced in Linux 3.17.

// Shim for missing bindings in druntime
version (none)
import core.sys.linux.sys.random : getrandom;
else
{
import core.sys.posix.sys.types : ssize_t;
extern extern(C) ssize_t getrandom(
void* buf,
size_t buflen,
uint flags,
) @system nothrow @nogc;
}
}

/**
A "good" seed for initializing random number engines. Initializing
with $(D_PARAM unpredictableSeed) makes engines generate different
random number sequences every run.

This function utilizes the system $(I cryptographically-secure pseudo-random
number generator (CSPRNG)) or $(I pseudo-random number generator (PRNG))
where available and implemented (currently `arc4random` on applicable BSD
systems or `getrandom` on Linux) to generate “high quality” pseudo-random
numbers – if possible.
As a consequence, calling it may block under certain circumstances (typically
during early boot when the system's entropy pool has not yet been
initialized).

On x86 CPU models which support the `RDRAND` instruction, that will be used
when no more specialized randomness source is implemented.

In the future, further platform-specific PRNGs may be incorporated.

Warning:
$(B This function must not be used for cryptographic purposes.)
Despite being implemented for certain targets, there are no guarantees
that it sources its randomness from a CSPRNG.
The implementation also includes a fallback option that provides very little
randomness and is used when no better source of randomness is available or
integrated on the target system.
As written earlier, this function only aims to provide randomness for seeding
ordinary (non-cryptographic) PRNG engines.

Returns:
A single unsigned integer seed value, different on each successive call
Note:
Expand All @@ -1788,7 +1830,25 @@ how excellent the source of entropy is.
*/
@property uint unpredictableSeed() @trusted nothrow @nogc
{
version (AnyARC4Random)
version (linux)
{
uint buffer;

/*
getrandom(2):
If the _urandom_ source has been initialized, reads of up to
256 bytes will always return as many bytes as requested and
will not be interrupted by signals. No such guarantees apply
for larger buffer sizes.
*/
static assert(buffer.sizeof <= 256);

const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
assert(status == buffer.sizeof);
Copy link
Member Author

Choose a reason for hiding this comment

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

  • Are we fine that this assert might not make it into user code because of -release?
  • Should we rather do if (status != buffer.sizeof) { assert(false); }?
  • Should we rather do if (status != buffer.sizeof) { return fallbackSeed(); }?

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't worry about it.

You get what you get when you use it.

Choose a reason for hiding this comment

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

there is enforce for that. Is this optional by the way? so I can set it by a flag , 0, this, 1 getrandom, 2, something else kinda thing?

Copy link
Member Author

Choose a reason for hiding this comment

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

Calling enforce() would make this nothrow @nogc function, well, throw and @gc.

Copy link
Member Author

Choose a reason for hiding this comment

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

This function is just a way to generate a "good" seed for initializing random number engines – not a CSPRNG. (And if it were one, we definitely shouldn’t overengineer it. It could probably return an “optional”-type result then – but make it configurable – with options not applicable to all operating system –, rather nope. Also keep in mind, this thing has already been utilizing arc4random on applicable targets.)


return buffer;
}
else version (AnyARC4Random)
{
return arc4random();
}
Expand Down Expand Up @@ -1837,7 +1897,25 @@ if (isUnsigned!UIntType)
/// ditto
@property UIntType unpredictableSeed() @nogc nothrow @trusted
{
version (AnyARC4Random)
version (linux)
{
UIntType buffer;

/*
getrandom(2):
If the _urandom_ source has been initialized, reads of up to
256 bytes will always return as many bytes as requested and
will not be interrupted by signals. No such guarantees apply
for larger buffer sizes.
*/
static assert(buffer.sizeof <= 256);

const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
assert(status == buffer.sizeof);

return buffer;
}
else version (AnyARC4Random)
{
static if (UIntType.sizeof <= uint.sizeof)
{
Expand Down
Loading