Skip to content

Improve random number generation - #258

Merged
magicant merged 6 commits into
trunkfrom
refactor-random
Sep 12, 2026
Merged

magicant merged 6 commits into
trunkfrom
refactor-random

Conversation

@magicant

Copy link
Copy Markdown
Owner

[Summary by GitHub Copilot]

This pull request makes significant improvements to the $RANDOM special variable in the shell, ensuring that random number sequences are independent between subshells and more secure. It also introduces new configuration checks and enhances the randomness of temporary file names. Comprehensive tests have been added to validate the new randomization behavior.

Random Number Generation Improvements:

  • In different subshells, the $RANDOM variable now produces independent sequences of random numbers, preventing subshells from repeating the parent shell’s sequence. This is achieved by introducing a new reseeding mechanism and seed management logic. (NEWS, NEWS.ja, variable.c, exec.c, variable.h, [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • The random seed is now generated using a combination of high-resolution time and process ID, improving unpredictability. (util.c, util.h, [1] [2] [3] [4]

Configuration and Portability:

  • The build system now detects the presence of getentropy and clock_gettime for improved randomness and portability. (configure, configureR1299-R1362)

Temporary File Randomization:

  • The algorithm for generating temporary file names has been improved to use better randomization, including process-specific and system entropy where available. (path.c, [1] [2] [3] [4] [5]

Testing Enhancements:

  • Extensive new tests have been added to verify that $RANDOM produces different values in subshells, when re-seeded, and across shell invocations, as well as to ensure correct behavior when setting or unsetting the variable. (tests/random-y.tst, [1] [2] [3] [4]

Code Modernization:

  • Copyright years updated. (util.c, util.h, variable.h, [1] [2] [3]

These changes together make random number generation in the shell more robust, secure, and predictable for scripting use cases.

Previously, subshells would inherit the same random number sequence as
the parent shell, which could lead to unexpected behavior in scripts
that rely on randomness. This change ensures that each subshell
generates a different sequence of random numbers.

The sequence is still deterministic if the user seeds $RANDOM to a
specific value. When subshells are forked after seeding to the same
value, subshells in the same position in the fork tree will yield the
same sequence, but other subshells will yield different sequences. This
is achieved by saving the given seed in the `seed_candidate` variable,
which is then updated deterministically when a subshell is forked.

Fixes #256
This refactoring extracts the generate_seed function from variable.c to
util.c, so that it can be used from path.c as well.
The `num` variable is now initialized using `generate_seed()` instead of
relying only on the current time. The `n` variable has been replaced
with a `salt` variable that is initialized with the current process ID,
which helps to reduce the likelihood of generating the same random
number in different subshells. The random number generation formula has
also been updated to use a larger multiplier, which should help to
produce a more diverse set of random numbers.

One notable change is that `num` is no longer updated when the temporary
file is created successfully in the first iteration. This means that
successive calls to `create_temporary_file` will result in same-named
temporary files being created as long as the previously created file is
removed before the next call, which is often the case in practice.
The `generate_seed' function now uses `clock_gettime` with
`CLOCK_REALTIME` to generate a more precise seed for random number
generation. If `clock_gettime` is not available, it falls back to using
`time(NULL)`. This should provide more entropy for the seed, especially
helpful when the function is called multiple times in quick succession.
In environments where uintptr_t is defined, we can use the address of
the string buffer to mix into the seed for generating temporary file
names. This helps to reduce the likelihood of collisions when multiple
subshells sharing the same seed are creating files simultaneously.
The existing algorithm for generating temporary file names is not very
secure because the random number seed is derived from public information
like the process ID and the current time. This makes it possible for an
attacker to guess the file names and create files with those names
before the shell does, which can lead all the 100 attempts to fail.

This commit adds a call to getentropy() to generate a random salt after
8 failed attempts. Since getentropy() is designed to provide
cryptographically secure random numbers, this should make it much harder
for an attacker to guess the file names and cause collisions.

getentropy() is used only after 8 failed attempts to avoid unnecessarily
consuming entropy from the system.
@magicant
magicant merged commit 1080db5 into trunk Sep 12, 2026
6 checks passed
@magicant
magicant deleted the refactor-random branch September 12, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant