git: cb48c371f277 - stable/13 - rtld-elf: Fix UB for direct exec with no extra rtld arguments
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 15 Dec 2025 18:18:52 UTC
The branch stable/13 has been updated by jrtc27:
URL: https://cgit.FreeBSD.org/src/commit/?id=cb48c371f2770a33c7934bb0dc098fee76e28711
commit cb48c371f2770a33c7934bb0dc098fee76e28711
Author: Jessica Clarke <jrtc27@FreeBSD.org>
AuthorDate: 2025-05-06 22:14:51 +0000
Commit: Jessica Clarke <jrtc27@FreeBSD.org>
CommitDate: 2025-12-15 17:56:36 +0000
rtld-elf: Fix UB for direct exec with no extra rtld arguments
If no extra rtld arguments are provided, rtld_argc will be 1 (for
argv[0] and so we are shifting the entire memory range down by a single
pointer. However, unlike argv and envp, auxp's entries are two pointers
in size, not one, and so in this case the source and destination
overlap, meaning simple assignment is UB (C99 6.5.16.1p3). On many
architectures this ends up being harmless as the compiler will emit
double machine word loads and stores, or if it splits them it may still
schedule them such that it works in this case, but our RISC-V baseline
does not include such instructions and LLVM ends up picking a schedule
that copies the second word before the first word, thereby replacing the
first word with a copy of the second word. This results in direct exec
mode segfaulting on RISC-V when given no arguments.
Fix this by using a temporary in the source and let the compiler safely
elide its use.
Reviewed by: kib
Fixes: 0fc65b0ab82c ("Make ld-elf.so.1 directly executable.")
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D50185
(cherry picked from commit 2b04ba6e08b983d8756552286846059507bca7a3)
---
libexec/rtld-elf/rtld.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 1cab00bb1c59..980f5c89edb7 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -510,7 +510,7 @@ rtld_trunc_page(uintptr_t x)
func_ptr_type
_rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
{
- Elf_Auxinfo *aux, *auxp, *auxpf, *aux_info[AT_COUNT];
+ Elf_Auxinfo *aux, *auxp, *auxpf, *aux_info[AT_COUNT], auxtmp;
Objlist_Entry *entry;
Obj_Entry *last_interposer, *obj, *preload_tail;
const Elf_Phdr *phdr;
@@ -675,7 +675,12 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
dbg("move aux from %p to %p", auxpf, aux);
/* XXXKIB insert place for AT_EXECPATH if not present */
for (;; auxp++, auxpf++) {
- *auxp = *auxpf;
+ /*
+ * NB: Use a temporary since *auxpf and
+ * *auxp overlap if rtld_argc is 1
+ */
+ auxtmp = *auxpf;
+ *auxp = auxtmp;
if (auxp->a_type == AT_NULL)
break;
}