Downstream-only patch to add arc4random to support/ for use in qsort testing. The arc4random implementation is up-to-date with upstream commit 2642002380aafb71a1d3b569b6d7ebeab3284816, with minor changes to keep everything self-contained within support infrastructure. Unlike the upstream version, this implementation is a cancellation point. diff --git a/support/Makefile b/support/Makefile index d6d03c2ed3af3e6d..bffcb06d7185d674 100644 --- a/support/Makefile +++ b/support/Makefile @@ -41,6 +41,8 @@ libsupport-routines = \ resolv_response_context_free \ resolv_test \ set_fortify_handler \ + support-arc4random \ + support-arc4random_uniform \ support-open-dev-null-range \ support_become_root \ support_can_chroot \ diff --git a/support/support-arc4random.c b/support/support-arc4random.c new file mode 100644 index 0000000000000000..c4462b098c68cef5 --- /dev/null +++ b/support/support-arc4random.c @@ -0,0 +1,99 @@ +/* Pseudo Random Number Generator + Copyright (C) 2022-2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void +arc4random_getrandom_failure (void) +{ + __libc_fatal ("Fatal glibc error: cannot get entropy for arc4random\n"); +} + +void +arc4random_buf (void *p, size_t n) +{ + static int seen_initialized; + ssize_t l; + int fd; + + if (n == 0) + return; + + for (;;) + { + l = TEMP_FAILURE_RETRY (getrandom (p, n, 0)); + if (l > 0) + { + if ((size_t) l == n) + return; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + continue; /* Interrupted by a signal; keep going. */ + } + else if (l < 0 && errno == ENOSYS) + break; /* No syscall, so fallback to /dev/urandom. */ + arc4random_getrandom_failure (); + } + + if (atomic_load_relaxed (&seen_initialized) == 0) + { + /* Poll /dev/random as an approximation of RNG initialization. */ + struct pollfd pfd = { .events = POLLIN }; + pfd.fd = TEMP_FAILURE_RETRY ( + __open64_nocancel ("/dev/random", O_RDONLY | O_CLOEXEC | O_NOCTTY)); + if (pfd.fd < 0) + arc4random_getrandom_failure (); + if (TEMP_FAILURE_RETRY (poll (&pfd, 1, -1)) < 0) + arc4random_getrandom_failure (); + if (__close_nocancel (pfd.fd) < 0) + arc4random_getrandom_failure (); + atomic_store_relaxed (&seen_initialized, 1); + } + + fd = TEMP_FAILURE_RETRY ( + __open64_nocancel ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY)); + if (fd < 0) + arc4random_getrandom_failure (); + for (;;) + { + l = TEMP_FAILURE_RETRY (__read_nocancel (fd, p, n)); + if (l <= 0) + arc4random_getrandom_failure (); + if ((size_t) l == n) + break; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + } + if (__close_nocancel (fd) < 0) + arc4random_getrandom_failure (); +} + +uint32_t +arc4random (void) +{ + uint32_t r; + arc4random_buf (&r, sizeof (r)); + return r; +} diff --git a/support/support-arc4random_uniform.c b/support/support-arc4random_uniform.c new file mode 100644 index 0000000000000000..20108e7409cca81b --- /dev/null +++ b/support/support-arc4random_uniform.c @@ -0,0 +1,70 @@ +/* Random pseudo generator number which returns a single 32 bit value + uniformly distributed but with an upper_bound. + Copyright (C) 2022-2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +/* Return a uniformly distributed random number less than N. The algorithm + calculates a mask being the lowest power of two bounding the upper bound + N, successively queries new random values, and rejects values outside of + the request range. + + For reject values, it also tries if the remaining entropy could fit on + the asked range after range adjustment. + + The algorithm avoids modulo and divide operations, which might be costly + depending on the architecture. */ +uint32_t +arc4random_uniform (uint32_t n) +{ + if (n <= 1) + /* There is no valid return value for a zero limit, and 0 is the + only possible result for limit 1. */ + return 0; + + /* Powers of two are easy. */ + if (powerof2 (n)) + return arc4random () & (n - 1); + + /* mask is the smallest power of 2 minus 1 number larger than n. */ + int z = __builtin_clz (n); + uint32_t mask = ~UINT32_C(0) >> z; + int bits = CHAR_BIT * sizeof (uint32_t) - z; + + while (1) + { + uint32_t value = arc4random (); + + /* Return if the lower power of 2 minus 1 satisfy the condition. */ + uint32_t r = value & mask; + if (r < n) + return r; + + /* Otherwise check if remaining bits of entropy provides fits in the + bound. */ + for (int bits_left = z; bits_left >= bits; bits_left -= bits) + { + value >>= bits; + r = value & mask; + if (r < n) + return r; + } + } +} diff --git a/support/support.h b/support/support.h index b69f588e2edce6be..ed7862daf9e4120a 100644 --- a/support/support.h +++ b/support/support.h @@ -220,6 +220,19 @@ void support_stack_free (struct support_stack *stack); The returned value is the lowest file descriptor number. */ int support_open_dev_null_range (int num, int flags, mode_t mode); +/* Return a random integer between zero and 2**32-1 (inclusive). */ +extern uint32_t arc4random (void) + __THROW __wur; + +/* Fill the buffer with random data. */ +extern void arc4random_buf (void *__buf, size_t __size) + __THROW __nonnull ((1)); + +/* Return a random number between zero (inclusive) and the specified + limit (exclusive). */ +extern uint32_t arc4random_uniform (__uint32_t __upper_bound) + __THROW __wur; + __END_DECLS #endif /* SUPPORT_H */