[PATCH 8/8] elf: Scrub and reseed the AT_RANDOM bytes after deriving the guards (BZ 34197)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Wed Jun 3 00:04:39 GMT 2026
Once the pointer and stack guards have been derived from AT_RANDOM, scrub
the bytes and refill them with new random data unrelated to the guards.
On Linux, it uses getrandom syscall (as for tcache_key_initialize), and
fallback to zero the memory if the syscall is not avaiable.
This keeps AT_RANDOM useful to applications while ensuring those bytes no
longer reveal the guards.
The work is done by _dl_reseed_random, called once the guards are in place
and before any ELF constructor can observe AT_RANDOM: in security_init for
the dynamic loader and in __libc_start_main for statically linked programs.
Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.
---
csu/libc-start.c | 4 ++
elf/Makefile | 5 ++
elf/rtld.c | 6 +-
elf/tst-atrandom-scrub-static.c | 1 +
elf/tst-atrandom-scrub.c | 74 ++++++++++++++++++++++
sysdeps/generic/dl-reseed-random.h | 34 ++++++++++
sysdeps/unix/sysv/linux/dl-reseed-random.h | 43 +++++++++++++
7 files changed, 163 insertions(+), 4 deletions(-)
create mode 100644 elf/tst-atrandom-scrub-static.c
create mode 100644 elf/tst-atrandom-scrub.c
create mode 100644 sysdeps/generic/dl-reseed-random.h
create mode 100644 sysdeps/unix/sysv/linux/dl-reseed-random.h
diff --git a/csu/libc-start.c b/csu/libc-start.c
index 03d770ef157..66ef4e3ffc4 100644
--- a/csu/libc-start.c
+++ b/csu/libc-start.c
@@ -44,6 +44,8 @@ extern void __libc_init_first (int argc, char **argv, char **envp);
#include <tls.h>
#ifndef SHARED
# include <dl-osinfo.h>
+# include <dl-reseed-random.h>
+# include <dl-symbol-redir-ifunc.h>
# ifndef THREAD_SET_STACK_GUARD
/* Only exported for architectures that don't store the stack guard canary
in thread local area. */
@@ -300,6 +302,8 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
__pointer_chk_guard_local = pointer_chk_guard;
# endif
+ _dl_reseed_random (&_dl_random);
+
/* Now that the TCB, canary, and pointer guard are in place, run the
deferred IFUNC relocations. For non-PIE static binaries this is
ARCH_SETUP_IREL (apply_irel); for static-pie it is the IRELATIVE
diff --git a/elf/Makefile b/elf/Makefile
index a940b3045e5..75e86dadc4f 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -279,6 +279,7 @@ tests-static-normal := \
# tests-static-normal
tests-static-internal := \
+ tst-atrandom-scrub-static \
tst-dl-printf-static \
tst-dl_find_object-static \
tst-env-setuid-tunables \
@@ -548,6 +549,7 @@ tests-internal += \
neededtest2 \
neededtest3 \
neededtest4 \
+ tst-atrandom-scrub \
tst-audit19a \
tst-create_format1 \
tst-dl-hwcaps_split \
@@ -2410,6 +2412,9 @@ tst-ptrguard1-ARGS = --command "$(host-test-program-cmd) --child"
CFLAGS-tst-ptrguard1-static.c += -DPTRGUARD_LOCAL
tst-ptrguard1-static-ARGS = --command "$(objpfx)tst-ptrguard1-static --child"
+# Likewise, the static pointer guard lives in __pointer_chk_guard_local.
+CFLAGS-tst-atrandom-scrub-static.c += -DPTRGUARD_LOCAL
+
$(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@; \
$(evaluate-test)
diff --git a/elf/rtld.c b/elf/rtld.c
index 12e1b4dd71f..4d27b2fccc8 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -35,6 +35,7 @@
#include <unsecvars.h>
#include <dl-cache.h>
#include <dl-osinfo.h>
+#include <dl-reseed-random.h>
#include <dl-prop.h>
#include <dl-vdso.h>
#include <dl-vdso-setup.h>
@@ -832,10 +833,7 @@ security_init (void)
#endif
__pointer_chk_guard_local = pointer_chk_guard;
- /* We do not need the _dl_random value anymore. The less
- information we leave behind, the better, so clear the
- variable. */
- _dl_random = NULL;
+ _dl_reseed_random (&_dl_random);
}
#include <setup-vdso.h>
diff --git a/elf/tst-atrandom-scrub-static.c b/elf/tst-atrandom-scrub-static.c
new file mode 100644
index 00000000000..b68362fbb3c
--- /dev/null
+++ b/elf/tst-atrandom-scrub-static.c
@@ -0,0 +1 @@
+#include "tst-atrandom-scrub.c"
diff --git a/elf/tst-atrandom-scrub.c b/elf/tst-atrandom-scrub.c
new file mode 100644
index 00000000000..1fabfa63dcb
--- /dev/null
+++ b/elf/tst-atrandom-scrub.c
@@ -0,0 +1,74 @@
+/* Verify the AT_RANDOM bytes do not reveal the guards after startup.
+ Copyright (C) 2026 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/>. */
+
+/* The loader (security_init) and the static startup code (__libc_start_main)
+ derive the stack and pointer guards from the AT_RANDOM bytes, scrub those
+ bytes, and refill them with fresh entropy unrelated to the guards. The
+ AT_RANDOM entry is kept, so getauxval (AT_RANDOM) keeps returning 16 random
+ bytes, but they no longer reveal the guards. Check that neither guard can
+ be reconstructed from AT_RANDOM and that no auxiliary vector entry holds a
+ guard value. */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/auxv.h>
+
+#include <stackguard-macros.h>
+#include <tls.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ uintptr_t stack_guard = STACK_CHK_GUARD;
+ uintptr_t pointer_guard = POINTER_CHK_GUARD;
+
+ unsigned char *random = (unsigned char *) getauxval (AT_RANDOM);
+ if (random == NULL)
+ FAIL_UNSUPPORTED ("the kernel did not provide AT_RANDOM");
+
+ printf ("debug: stack guard = %0*jx\n",
+ (int) (2 * sizeof (uintptr_t)), (uintmax_t) stack_guard);
+ printf ("debug: pointer guard = %0*jx\n",
+ (int) (2 * sizeof (uintptr_t)), (uintmax_t) pointer_guard);
+ printf ("debug: AT_RANDOM = ");
+ for (int i = 0; i < 16; i++)
+ printf ("%02x", random[i]);
+ printf ("\n");
+
+ /* Reconstruct the guards from the (reseeded) AT_RANDOM bytes the way the
+ loader does and check that they no longer match the live guards. */
+ uintptr_t recovered_stack;
+ memcpy (&recovered_stack, random, sizeof (recovered_stack));
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ recovered_stack &= ~(uintptr_t) 0xff;
+#else
+ recovered_stack &= ~((uintptr_t) 0xff << (8 * (sizeof (recovered_stack) - 1)));
+#endif
+ TEST_VERIFY (recovered_stack != stack_guard);
+
+ uintptr_t recovered_pointer;
+ memcpy (&recovered_pointer, random + sizeof (uintptr_t),
+ sizeof (recovered_pointer));
+ TEST_VERIFY (recovered_pointer != pointer_guard);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/dl-reseed-random.h b/sysdeps/generic/dl-reseed-random.h
new file mode 100644
index 00000000000..7e1a3c3be21
--- /dev/null
+++ b/sysdeps/generic/dl-reseed-random.h
@@ -0,0 +1,34 @@
+/* Scrub and reseed the kernel-provided random bytes. Generic version.
+ Copyright (C) 2026 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/>. */
+
+#ifndef _DL_RESEED_RANDOM_H
+#define _DL_RESEED_RANDOM_H
+
+#include <string.h>
+
+static inline void __attribute__ ((always_inline))
+_dl_reseed_random (void **dl_random)
+{
+ if (*dl_random == NULL)
+ return;
+ memset (*dl_random, '\0', 16);
+ __asm__ __volatile__ ("" : : "r" (*dl_random) : "memory");
+ *dl_random = NULL;
+}
+
+#endif /* _DL_RESEED_RANDOM_H */
diff --git a/sysdeps/unix/sysv/linux/dl-reseed-random.h b/sysdeps/unix/sysv/linux/dl-reseed-random.h
new file mode 100644
index 00000000000..f954e48b7f1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-reseed-random.h
@@ -0,0 +1,43 @@
+/* Scrub and reseed the AT_RANDOM bytes. Linux version.
+ Copyright (C) 2026 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/>. */
+
+#ifndef _DL_RESEED_RANDOM_H
+#define _DL_RESEED_RANDOM_H
+
+#include <string.h>
+#include <not-cancel.h>
+#include <sys/random.h>
+
+/* The stack and pointer guards have been derived from the 16 AT_RANDOM
+ bytes pointed to by DL_RANDOM. Scrub them first, so the guards cannot be
+ recovered even if the refill below fails, then refill them with fresh
+ entropy unrelated to the guards so that getauxval (AT_RANDOM) keeps
+ returning random bytes. */
+static inline void __attribute__ ((always_inline))
+_dl_reseed_random (void **dl_random)
+{
+ if (*dl_random == NULL)
+ return;
+ memset (*dl_random, '\0', 16);
+ __asm__ __volatile__ ("" : : "r" (*dl_random) : "memory");
+
+ __getrandom_nocancel_nostatus_direct (*dl_random, 16, GRND_NONBLOCK);
+ _dl_random = NULL;
+}
+
+#endif /* _DL_RESEED_RANDOM_H */
--
2.43.0
More information about the Libc-alpha
mailing list