From patchwork Fri Jul 17 11:11:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 139400 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 09A0D4BA23DF for ; Fri, 17 Jul 2026 11:13:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 09A0D4BA23DF Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=ee2srLPN X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from sea.source.kernel.org (sea.source.kernel.org [IPv6:2600:3c0a:e001:78e:0:1991:8:25]) by sourceware.org (Postfix) with ESMTPS id 392F84BA2E04 for ; Fri, 17 Jul 2026 11:12:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 392F84BA2E04 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=kernel.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kernel.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 392F84BA2E04 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2600:3c0a:e001:78e:0:1991:8:25 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784286731; cv=none; b=fvp63X8pchq0fk2p4tJQjf8cbeNGbTSCidaliNb3CPDlt87XjcaGhrgwNQ10VxtQqXFri7DKf506t+WStBML1CpnbdHtoEDRu1rhlBV85YT8yRTzIMkpm5A1q0sFGUDQLPC7UmDRKBQYUY/fPsfbBr2hNer0Y0lb50p3JQHnRfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784286731; c=relaxed/simple; bh=/YqlyHHGq25uhkMl7ByxNMzUeHkChI2MNXMgxB2g/Sg=; h=DKIM-Signature:From:Date:Subject:MIME-Version:Message-Id:To; b=Pd5WNHuEQ/WzTsOYHGvhzg/WiuCM8f139fFO/w+CD+8Erl+nc1Qd57bmtMRwS2b9mjBqW3l3FoVDTOteBPJ7LprjUOwx4MPkiLW1N8qvtZnFD82rfXZz5bF/QbIXGDkpXi/ffBGGI2vVZ7L0pKRoaP76tdPyFyZ6G1KTqsoftvw= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=ee2srLPN DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 392F84BA2E04 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) by sea.source.kernel.org (Postfix) with ESMTP id 93C754077B; Fri, 17 Jul 2026 11:12:10 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D78721F000E9; Fri, 17 Jul 2026 11:12:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784286730; bh=10Ee5Sq6cvGtA8AShliBjJnUH5IEQOaD46NyWgNcXiU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ee2srLPNhJ+DQB4jGOfgEDHkzzr1UgbpCirVroFEoSxTTBQoW9Z2r4ZKjBn+61H/I SdNflPRdTuzFTbaDDV+pLCCOUj0ycKqndHWhcrOMQ1awATYLtRzpaHyImqNXkF6s6Y pdoDAr9OjnPbVXJI9mABOdBXWxHDGo24PB9YbJyKF7LGJd2iyZ4Z8d2sAuMGB3HTB9 R1dkwaw4oRjIk/xTIvxI0YJZ0BQhXJSP3/wtAnhTG5iCCm5TfB8n5y2sFJpsL1B+Hr FA9A/vXpQ6/KQS8hhR0JbSCa2DwZILdaUD7Q2R3S04iomd6cyKFwp1G2EXgKYu2ozW +hMFXNZ6Umn6w== From: Christian Brauner Date: Fri, 17 Jul 2026 13:11:58 +0200 Subject: [PATCH v3 1/4] elf: load the main program from AT_EXECFD when run as a binfmt interpreter MIME-Version: 1.0 Message-Id: <20260717-work-glibc-binfmt_misc-v3-1-45129bfb13fe@kernel.org> References: <20260717-work-glibc-binfmt_misc-v3-0-45129bfb13fe@kernel.org> In-Reply-To: <20260717-work-glibc-binfmt_misc-v3-0-45129bfb13fe@kernel.org> To: Florian Weimer , libc-alpha@sourceware.org Cc: Adhemerval Zanella Netto , Carlos O'Donell , "Christian Brauner (Amutable)" X-Mailer: b4 0.16-dev-4217c X-Developer-Signature: v=1; a=openpgp-sha256; l=20955; i=brauner@kernel.org; h=from:subject:message-id; bh=/YqlyHHGq25uhkMl7ByxNMzUeHkChI2MNXMgxB2g/Sg=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRF8bGWeCcXp6SWbO3e+lnuSYfNj4sFTJ+t5mywP7NTK 1f8HNP0jlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgIkcSWL4Z582iVHziZvPFt0z zmfS97L+XfH2dOfpiX/0bZKzT64/5MbwT0Xl8KrJ9q+j92/fqqP3UF3F8hoHg9Ll0gnuV6ZfEVF dwgsA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 X-Spam-Status: No, score=-10.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org A Linux binfmt_misc entry registered with the 'O' (open-binary) or 'C' (credentials) flag keeps the executed binary open across the handler dispatch and passes the descriptor to the interpreter in the AT_EXECFD auxiliary vector entry. The semantics are SVR4's: AT_EXECFD is the "file descriptor of program to load", the alternative to AT_PHDR. FreeBSD's rtld consumes it, qemu-user has consumed it since 2013 - and rtld never has ("We also do not handle AT_EXECFD even if it would be passed up"), so handlers dispatching to ld.so had the descriptor ignored, leaked into the application, and the program re-opened by path. Consume the descriptor, gated on the existing rtld_is_main detection. When ld.so is the main program and AT_EXECFD is present, load the main object from the descriptor via open_verify/_dl_map_object_from_fd instead of re-opening rtld_progname by path. Argument processing is entirely unchanged. Every producer of AT_EXECFD splices the interpreter and the binary path into the argument vector, so the program name argument is consumed exactly as before and the application-visible argument vector is identical. What changes is that the loaded file is now the very struct file that execve() access-checked (no re-open race), and that no path-based open happens at all, so execute-only (--x) binaries that execve() permits but ld.so previously failed to open with EACCES now run. The canonical name for $ORIGIN is derived from the descriptor by the existing __RTLD_OPENEXEC handling. The descriptor is consumed and closed while mapping, and the on-stack auxv entry is neutralized to AT_IGNORE so the program does not observe a dangling descriptor number. If the descriptor arrived on a standard descriptor slot (the kernel installs it on the lowest free one), consuming it closes that slot, so for secure processes the startup standard-descriptor check is re-run once the program is mapped, because it originally ran while the descriptor still occupied the slot. The descriptor is deliberately not moved out of the standard range before mapping: it is closed either way, so moving it would only change which descriptor number ends up closed. Chain loading is not attempted for a descriptor-loaded program. Re-executing the spliced path would run the process through the very binfmt handler that chose this dynamic linker again, and there may be no path that can be executed in the first place. Statically linked binaries are refused with a clear error instead, otherwise they would crash in-process again (bug 28648). The static-versus-dynamic classification is rtld_chain_load's, factored into a shared helper. Trace mode (LD_TRACE_LOADED_OBJECTS) never chain-loaded and behaves exactly as the explicit-loader invocation does today, including listing the dependencies of a descriptor-loaded dynamic executable. When rtld runs as the PT_INTERP of someone else's kernel-loaded main program (e.g. a dynamically linked qemu-user registered with 'O'), rtld_is_main is false and AT_EXECFD is addressed to that program, so nothing changes there. Signed-off-by: Christian Brauner (Amutable) --- NEWS | 9 +++ elf/dl-load.c | 65 +++++++++++++++- elf/rtld.c | 125 ++++++++++++++++++++++++++---- sysdeps/generic/dl-standard-fds.h | 34 ++++++++ sysdeps/generic/ldsodefs.h | 7 ++ sysdeps/mips/dl-machine-reject-phdr.h | 4 +- sysdeps/unix/sysv/linux/dl-standard-fds.h | 33 ++++++++ 7 files changed, 259 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index f9d90c5194..cacd3f8be6 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,15 @@ Version 2.44 Major new features: +* When the dynamic linker is executed as a binfmt interpreter (for + example through a Linux binfmt_misc entry registered with the 'O' + flag) and the kernel passes the executed program as an open + descriptor in the AT_EXECFD auxiliary vector entry, the dynamic + linker now loads the program from that descriptor instead of + re-opening it by path. Execute-only (--x) binaries work under such + handlers, and the descriptor refers to the file the kernel actually + access-checked, eliminating the re-open race. + * A new tunable, glibc.elf.thp, is added to map read-only segments with Transparent Huge Pages (THP) if THP isn't disable in kernel. When glibc.elf.thp is set to 1, malloc uses the actual kernel THP mode diff --git a/elf/dl-load.c b/elf/dl-load.c index 95404adae9..f2f14dd6f3 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1625,11 +1625,14 @@ open_verify (const char *name, int fd, __set_errno (0); fbp->len = 0; assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); - /* Read in the header. */ + /* Read the header with pread: an executable descriptor handed to + the loader may be shared, so its file position must be neither + relied upon nor disturbed. */ do { - ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len, - sizeof (fbp->buf) - fbp->len); + ssize_t retlen = __pread64_nocancel (fd, fbp->buf + fbp->len, + sizeof (fbp->buf) - fbp->len, + fbp->len); if (retlen <= 0) break; fbp->len += retlen; @@ -2241,6 +2244,62 @@ _dl_map_object (struct link_map *loader, const char *name, return _dl_map_new_object (loader, name, type, trace_mode, mode, nsid); } +/* Map in the main executable, already opened on FD. The descriptor + comes from the kernel (AT_EXECFD, from a binfmt interpreter dispatch + that kept the executed binary open) or from an explicit loader + invocation. NAME is the name the program is known by and is only + used for diagnostics; the canonical name used for $ORIGIN is derived + from the descriptor itself (__RTLD_OPENEXEC). There may be no path + the program could be opened by: the descriptor is readable even for + an execute-only binary, and it refers to the very file the kernel + access-checked, so no path re-open takes its place. */ +struct link_map * +_dl_map_object_execfd (int fd, const char *name) +{ + struct filebuf fb; + bool found_other_class = false; + + /* open_verify reads with pread and ignores the file position, so only + a descriptor that is not open at all needs rejecting (an explicit + loader invocation can be handed one). */ + if (__fcntl64_nocancel (fd, F_GETFD) == -1) + _dl_signal_error (errno, name, NULL, + N_("cannot load main program from descriptor")); + + fd = open_verify (name, fd, &fb, NULL, 0, __RTLD_OPENEXEC, + &found_other_class, false); + if (__glibc_unlikely (fd == -1)) + { + if (found_other_class) + _dl_signal_error (0, name, NULL, + ELFW(CLASS) == ELFCLASS32 + ? N_("wrong ELF class: ELFCLASS64") + : N_("wrong ELF class: ELFCLASS32")); + else if (errno == ENOENT) + /* The descriptor is open (checked above), so ENOENT here is + open_verify's report of an incompatible ELF machine, not a + missing file. */ + _dl_signal_error (0, name, NULL, + N_("cannot load main program from descriptor:" + " incompatible ELF machine")); + else + _dl_signal_error (errno, name, NULL, + N_("cannot load main program from descriptor")); + } + + char *realname = __strdup (name); + if (realname == NULL) + { + __close_nocancel (fd); + _dl_signal_error (ENOMEM, name, NULL, + N_("cannot allocate name record")); + } + + return _dl_map_object_from_fd (name, NULL, fd, &fb, realname, NULL, + lt_executable, __RTLD_OPENEXEC, + __libc_stack_end, LM_ID_BASE); +} + struct add_path_state { diff --git a/elf/rtld.c b/elf/rtld.c index e5ba71fef1..3d383ae3b9 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -54,6 +54,7 @@ #include #include #include +#include #include @@ -1044,10 +1045,11 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list) } } -/* Check if the executable is not actually dynamically linked, and - invoke it directly in that case. */ -static void -rtld_chain_load (struct link_map *main_map, char *argv0) +/* Diagnose the dynamic loader run against itself (fatal), and return + whether the main executable is dynamically linked: it has DT_NEEDED + dependencies or a program interpreter. */ +static bool +rtld_main_map_is_dynamic (struct link_map *main_map) { /* The dynamic loader run against itself. */ const char *rtld_soname = l_soname (&_dl_rtld_map); @@ -1058,14 +1060,26 @@ rtld_chain_load (struct link_map *main_map, char *argv0) /* With DT_NEEDED dependencies, the executable is dynamically linked. */ if (__glibc_unlikely (main_map->l_info[DT_NEEDED] != NULL)) - return; + return true; /* If the executable has program interpreter, it is dynamically linked. */ for (size_t i = 0; i < main_map->l_phnum; ++i) if (main_map->l_phdr[i].p_type == PT_INTERP) - return; + return true; + + return false; +} +/* Check if the executable is not actually dynamically linked, and + invoke it directly in that case. */ +static void +rtld_chain_load (struct link_map *main_map, char *argv0) +{ + if (rtld_main_map_is_dynamic (main_map)) + return; + + const char *rtld_soname = l_soname (&_dl_rtld_map); const char *pathname = _dl_argv[0]; if (argv0 != NULL) _dl_argv[0] = argv0; @@ -1079,6 +1093,20 @@ rtld_chain_load (struct link_map *main_map, char *argv0) rtld_soname, pathname, errcode); } +/* The main executable was loaded from a descriptor and cannot be + chain-loaded through execve: there may be no path it can be executed + by, and re-executing it would run the process through the binfmt + handler that chose this dynamic linker in the first place, again. + Refuse the cases rtld_chain_load would have chained instead of + crashing on them later (bug 28648). */ +static void +rtld_execfd_check (struct link_map *main_map) +{ + if (!rtld_main_map_is_dynamic (main_map)) + _dl_fatal_printf ("%s: cannot execute a statically linked binary" + " from a descriptor\n", l_soname (&_dl_rtld_map)); +} + /* Called to complete the initialization of the link map for the main executable. Returns true if there is a PT_INTERP segment. */ static bool @@ -1382,16 +1410,50 @@ dl_main (const ElfW(Phdr) *phdr, like that. We just load it and use its entry point; we don't pay attention to its PT_INTERP command (we are the interpreter ourselves). This is an easy way to test a new ld.so before - installing it. */ + installing it. + + This also happens when the kernel executes us on behalf of a + binfmt interpreter dispatch (binfmt_misc): the handler splices + our path and the program's path into the argument vector, so + the arguments are processed just the same. If the handler + also kept the program open ('O' and 'C' entries), AT_EXECFD + carries the descriptor and the program is loaded from it + instead of re-opening the path (see below). */ rtld_is_main = true; char *argv0 = NULL; char **orig_argv = _dl_argv; + int execfd = -1; + /* True if the kernel dispatched us as a binfmt interpreter + (AT_EXECFD is present). */ + bool from_execfd = false; /* Note the place where the dynamic linker actually came from. */ _dl_rtld_map.l_name = rtld_progname; - while (_dl_argc > 1) +#ifdef HAVE_AUX_VECTOR + /* A binfmt interpreter dispatch that keeps the executed binary + open passes the descriptor in AT_EXECFD. Load the program + from it: the path spliced into the argument vector may not be + openable again (execute-only binaries), and the descriptor + refers to the very file the kernel access-checked, so no + re-open races against it. The raw vector must be scanned + because a valid descriptor 0 and an absent entry cannot be + told apart in the parsed values. */ + for (ElfW(auxv_t) *av = auxv; av->a_type != AT_NULL; av++) + if (av->a_type == AT_EXECFD) + { + execfd = av->a_un.a_val; + from_execfd = true; + break; + } +#endif + + /* When the kernel dispatches us as a binfmt interpreter, argv[1] + is the spliced program path, not a loader option. It is + attacker-controlled and may begin with "--" (e.g. a program + named "--preload"), so do not parse it as an option. */ + while (!from_execfd && _dl_argc > 1) if (! strcmp (_dl_argv[1], "--list")) { if (state.mode != rtld_mode_help) @@ -1507,6 +1569,17 @@ dl_main (const ElfW(Phdr) *phdr, else break; + /* A descriptor on a standard slot is consumed and closed while + the program is mapped below, and a secure process must not run + with a silently closed standard descriptor: the startup check + ran while the descriptor still occupied the slot. Re-run it + once the program is mapped and the slot is free. Moving the + descriptor out of the standard range instead would only change + which descriptor number ends up closed. */ + bool recheck_standard_fds + = (execfd >= 0 && execfd <= STDERR_FILENO + && __glibc_unlikely (__libc_enable_secure)); + if (__glibc_unlikely (state.mode == rtld_mode_list_tunables)) { __tunables_print (); @@ -1581,16 +1654,29 @@ dl_main (const ElfW(Phdr) *phdr, #ifdef HAVE_THP _dl_get_thp_config (); #endif - _dl_map_object (NULL, rtld_progname, lt_executable, 0, - __RTLD_OPENEXEC, LM_ID_BASE); + if (execfd != -1) + _dl_map_object_execfd (execfd, rtld_progname); + else + _dl_map_object (NULL, rtld_progname, lt_executable, 0, + __RTLD_OPENEXEC, LM_ID_BASE); rtld_timer_stop (&load_time, start); } + /* Fill the standard slot freed by closing the program + descriptor (see recheck_standard_fds above). */ + if (__glibc_unlikely (recheck_standard_fds)) + _dl_recheck_standard_fds (); + /* Now the map for the main executable is available. */ main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; if (__glibc_likely (state.mode == rtld_mode_normal)) - rtld_chain_load (main_map, argv0); + { + if (execfd != -1) + rtld_execfd_check (main_map); + else + rtld_chain_load (main_map, argv0); + } phdr = main_map->l_phdr; phnum = main_map->l_phnum; @@ -1622,6 +1708,13 @@ dl_main (const ElfW(Phdr) *phdr, case AT_EXECFN: av->a_un.a_val = (uintptr_t) _dl_argv[0]; break; + case AT_EXECFD: + /* An AT_EXECFD entry is present only when the kernel + dispatched us, and the program is then always loaded and + closed from the descriptor, so do not leave a dangling + number behind. */ + av->a_type = AT_IGNORE; + break; } #endif @@ -1650,8 +1743,14 @@ dl_main (const ElfW(Phdr) *phdr, /* At this point we are in a bit of trouble. We would have to fill in the values for l_dev and l_ino. But in general we - do not know where the file is. We also do not handle AT_EXECFD - even if it would be passed up. + do not know where the file is. AT_EXECFD is deliberately not + consumed here: it carries the file the kernel did *not* load + and is addressed to the main program the kernel *did* load - + the registered binfmt interpreter (e.g. a dynamically linked + qemu-user), whose PT_INTERP we merely are. Only when the + registered interpreter is ld.so itself is ld.so that main + program, and then the descriptor is consumed in the + rtld-as-command branch above. We leave the values here defined to 0. This is normally no problem as the program code itself is normally no shared diff --git a/sysdeps/generic/dl-standard-fds.h b/sysdeps/generic/dl-standard-fds.h new file mode 100644 index 0000000000..d6c3589ffe --- /dev/null +++ b/sysdeps/generic/dl-standard-fds.h @@ -0,0 +1,34 @@ +/* Re-check the standard file descriptors in the dynamic linker. Generic. + 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; see the file COPYING.LIB. If + not, see . */ + +#ifndef _DL_STANDARD_FDS_H +#define _DL_STANDARD_FDS_H + +/* Re-run the startup standard-descriptor check after the dynamic + linker closed a descriptor in the standard range: a secure process + must not start with a silently closed standard descriptor. The + generic version is a no-op: ports without the check in the dynamic + linker heal the standard descriptors during libc initialization + instead (the Hurd does from its _hurd_fd_subinit hook, and does not + define __libc_check_standard_fds for shared builds). */ +static inline void +_dl_recheck_standard_fds (void) +{ +} + +#endif diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index f94247ad9f..b97282aaac 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -933,6 +933,13 @@ struct link_map *_dl_map_new_object (struct link_map *loader, int type, int trace_mode, int mode, Lmid_t nsid) attribute_hidden; +/* Map in the main executable from the already-open descriptor FD + (AT_EXECFD or an explicit loader invocation). NAME is the name the + program is known by; the canonical name used for $ORIGIN is derived + from FD. FD is consumed. */ +extern struct link_map *_dl_map_object_execfd (int fd, const char *name) + attribute_hidden; + /* Call _dl_map_object on the dependencies of MAP, and set up MAP->l_searchlist. PRELOADS points to a vector of NPRELOADS previously diff --git a/sysdeps/mips/dl-machine-reject-phdr.h b/sysdeps/mips/dl-machine-reject-phdr.h index e64494c198..88d0a3cd6a 100644 --- a/sysdeps/mips/dl-machine-reject-phdr.h +++ b/sysdeps/mips/dl-machine-reject-phdr.h @@ -209,8 +209,8 @@ elf_machine_reject_phdr_p (const struct dl_machine_phdr_info *info, if (ph->p_filesz < size) REJECT (" contains malformed PT_MIPS_ABIFLAGS\n"); - __lseek (fd, ph->p_offset, SEEK_SET); - if (__libc_read (fd, (void *) &mips_abiflags, size) != size) + if (__pread64_nocancel (fd, (void *) &mips_abiflags, size, + ph->p_offset) != size) REJECT (" unable to read PT_MIPS_ABIFLAGS\n"); if (__glibc_unlikely (mips_abiflags.flags2 != 0)) diff --git a/sysdeps/unix/sysv/linux/dl-standard-fds.h b/sysdeps/unix/sysv/linux/dl-standard-fds.h new file mode 100644 index 0000000000..207af0ab0b --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-standard-fds.h @@ -0,0 +1,33 @@ +/* Re-check the standard file descriptors in the dynamic linker. Linux. + 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; see the file COPYING.LIB. If + not, see . */ + +#ifndef _DL_STANDARD_FDS_H +#define _DL_STANDARD_FDS_H + +#include + +/* Linux runs the startup standard-descriptor check inside the dynamic + linker (dl-sysdep.c), so the re-check after closing a program + descriptor runs there as well. */ +static inline void +_dl_recheck_standard_fds (void) +{ + __libc_check_standard_fds (); +} + +#endif