[PATCH v3 4/4] elf: test ld.so --program-fd
Christian Brauner <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
Run the dynamic linker with --program-fd on an inherited descriptor of a helper program: it must be loaded from the descriptor, with the program name argument only naming it. This exercises the same loading path as AT_EXECFD without any kernel support, giving the descriptor-loading code deterministic coverage on kernels and CI setups where the binfmt_misc test reports UNSUPPORTED. Unlike a descriptor installed by the kernel, one given on the command line need not be positioned at the start of the file, so one subtest hands the loader a descriptor deliberately seeked mid-file and verifies that the program runs and that the shared file position is left undisturbed (the ELF header is read with pread). The remaining subtests verify that --program-fd composes with --argv0 and that a closed descriptor or a non-numeric argument produces a clean error, not a crash. Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- elf/Makefile | 4 ++ elf/tst-rtld-program-fd-prog.c | 28 ++++++++ elf/tst-rtld-program-fd.c | 150 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/elf/Makefile b/elf/Makefile index 01e77f2ca0..2acf8d9c71 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -493,6 +493,7 @@ tests += \ tst-rtld-no-malloc \ tst-rtld-no-malloc-audit \ tst-rtld-no-malloc-preload \ + tst-rtld-program-fd \ tst-rtld-run-static \ tst-single_threaded \ tst-single_threaded-pthread \ @@ -595,6 +596,7 @@ tests-container += \ test-srcs = \ tst-pathopt \ + tst-rtld-program-fd-prog \ tst-sprof-basic \ # tests-srcs @@ -3248,6 +3250,8 @@ $(objpfx)tst-rtld-list-diagnostics.out: tst-rtld-list-diagnostics.py \ > $@; \ $(evaluate-test) +$(objpfx)tst-rtld-program-fd.out: $(objpfx)tst-rtld-program-fd-prog + $(objpfx)tst-rtld-run-static.out: $(objpfx)ldconfig $(objpfx)tst-dl_find_object.out: \ diff --git a/elf/tst-rtld-program-fd-prog.c b/elf/tst-rtld-program-fd-prog.c new file mode 100644 index 0000000000..183358f4aa --- /dev/null +++ b/elf/tst-rtld-program-fd-prog.c @@ -0,0 +1,28 @@ +/* Helper program for tst-rtld-program-fd: report the argument vector. + 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 <https://www.gnu.org/licenses/>. */ + +#include <stdio.h> + +int +main (int argc, char **argv) +{ + printf ("argc=%d\n", argc); + for (int i = 0; i < argc; ++i) + printf ("argv[%d]=%s\n", i, argv[i]); + return 0; +} diff --git a/elf/tst-rtld-program-fd.c b/elf/tst-rtld-program-fd.c new file mode 100644 index 0000000000..9c3966943f --- /dev/null +++ b/elf/tst-rtld-program-fd.c @@ -0,0 +1,150 @@ +/* Test the ld.so --program-fd option. + 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 <https://www.gnu.org/licenses/>. */ + +/* Run the dynamic linker with --program-fd on an inherited descriptor + of the helper program: it must be loaded from the descriptor, with + the program name argument only naming it. Exercises the same + loading path as AT_EXECFD, without requiring kernel support. */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <support/capture_subprocess.h> +#include <support/check.h> +#include <support/support.h> +#include <support/xunistd.h> + +static int +do_test (void) +{ + char *prog = xasprintf ("%s/elf/tst-rtld-program-fd-prog", + support_objdir_root); + char *libpath = xasprintf ("%s:%s/elf", support_objdir_root, + support_objdir_root); + + /* No O_CLOEXEC: the descriptor must survive into ld.so. */ + int fd = xopen (prog, O_RDONLY, 0); + char *fdstr = xasprintf ("%d", fd); + + /* Plain use: the program comes from the descriptor, the name + argument becomes argv[0]. */ + { + char *argv[] = + { + (char *) "ld.so", (char *) "--library-path", libpath, + (char *) "--program-fd", fdstr, + (char *) "displayed-name", (char *) "tail-arg", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "program-fd", 0, + sc_allow_stdout); + TEST_COMPARE_STRING (cap.out.buffer, + "argc=2\n" + "argv[0]=displayed-name\n" + "argv[1]=tail-arg\n"); + support_capture_subprocess_free (&cap); + } + + /* The descriptor's file position must be irrelevant and preserved: + the loader reads the ELF header with pread. Hand over the + descriptor deliberately positioned mid-file and verify that the + program still runs and that the position - shared with the + subprocess - is where it was left. */ + { + xlseek (fd, 123, SEEK_SET); + char *argv[] = + { + (char *) "ld.so", (char *) "--library-path", libpath, + (char *) "--program-fd", fdstr, + (char *) "displayed-name", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "program-fd position", 0, + sc_allow_stdout); + TEST_COMPARE_STRING (cap.out.buffer, + "argc=1\n" + "argv[0]=displayed-name\n"); + TEST_COMPARE (xlseek (fd, 0, SEEK_CUR), 123); + support_capture_subprocess_free (&cap); + } + + /* Composes with --argv0. No rewind: the previous subtest left the + position mid-file, which the loader ignores. */ + { + char *argv[] = + { + (char *) "ld.so", (char *) "--library-path", libpath, + (char *) "--program-fd", fdstr, (char *) "--argv0", + (char *) "overridden", (char *) "displayed-name", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "program-fd --argv0", 0, + sc_allow_stdout); + TEST_COMPARE_STRING (cap.out.buffer, + "argc=1\n" + "argv[0]=overridden\n"); + support_capture_subprocess_free (&cap); + } + + /* A closed descriptor must produce a clean error, not a crash. */ + { + char *argv[] = + { + (char *) "ld.so", (char *) "--program-fd", (char *) "977", + (char *) "does-not-matter", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "program-fd bad fd", 127, + sc_allow_stderr); + TEST_VERIFY (strstr (cap.err.buffer, + "cannot load main program from descriptor") + != NULL); + support_capture_subprocess_free (&cap); + } + + /* A non-numeric argument must produce a clean error. */ + { + char *argv[] = + { + (char *) "ld.so", (char *) "--program-fd", (char *) "pear", + (char *) "does-not-matter", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "program-fd non-numeric", 127, + sc_allow_stderr); + TEST_VERIFY (strstr (cap.err.buffer, "invalid descriptor") != NULL); + support_capture_subprocess_free (&cap); + } + + xclose (fd); + free (fdstr); + free (libpath); + free (prog); + return 0; +} + +#include <support/test-driver.c> -- 2.53.0