227 lines
7.1 KiB
Diff
227 lines
7.1 KiB
Diff
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
|
|
+ <https://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <errno.h>
|
|
+#include <not-cancel.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <sys/mman.h>
|
|
+#include <sys/param.h>
|
|
+#include <sys/random.h>
|
|
+#include <poll.h>
|
|
+
|
|
+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
|
|
+ <https://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <sys/param.h>
|
|
+#include <support/support.h>
|
|
+
|
|
+/* 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 */
|