From 6748bccde4c4bd4e67441781f677a7c93361aa67 Mon Sep 17 00:00:00 2001 From: Patsy Griffin Date: Tue, 10 Feb 2026 08:38:45 -0500 Subject: [PATCH] Remove default value for LD_PROFILE_OUTPUT Resolves: RHEL-142193 --- glibc-RHEL-142193-1.patch | 267 ++++++++++++++++++++ glibc-RHEL-142193-2.patch | 171 +++++++++++++ glibc-RHEL-142193-3.patch | 28 +++ glibc-RHEL-142193-4.patch | 52 ++++ glibc-RHEL-142193-5.patch | 40 +++ glibc-RHEL-142193-6.patch | 506 ++++++++++++++++++++++++++++++++++++++ glibc-RHEL-142193-7.patch | 171 +++++++++++++ 7 files changed, 1235 insertions(+) create mode 100644 glibc-RHEL-142193-1.patch create mode 100644 glibc-RHEL-142193-2.patch create mode 100644 glibc-RHEL-142193-3.patch create mode 100644 glibc-RHEL-142193-4.patch create mode 100644 glibc-RHEL-142193-5.patch create mode 100644 glibc-RHEL-142193-6.patch create mode 100644 glibc-RHEL-142193-7.patch diff --git a/glibc-RHEL-142193-1.patch b/glibc-RHEL-142193-1.patch new file mode 100644 index 0000000..3652c05 --- /dev/null +++ b/glibc-RHEL-142193-1.patch @@ -0,0 +1,267 @@ +commit 1e1ad714ee9a663eda0e2bffad1d9f258b00a4e9 +Author: Adhemerval Zanella +Date: Mon May 6 13:18:47 2024 -0300 + + support: Add envp argument to support_capture_subprogram + + So tests can specify a list of environment variables. + Reviewed-by: Siddhesh Poyarekar + +Conflicts: + elf/tst-tunables-enable_secure.c + (file does not exist downstream) + +diff --git a/elf/tst-audit18.c b/elf/tst-audit18.c +index 841251dd7003aa7d..cec93e269ca0b4ef 100644 +--- a/elf/tst-audit18.c ++++ b/elf/tst-audit18.c +@@ -79,7 +79,7 @@ do_test (int argc, char *argv[]) + + setenv ("LD_AUDIT", "tst-auditmod18.so", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit18", 0, sc_allow_stderr); + + struct +diff --git a/elf/tst-audit19b.c b/elf/tst-audit19b.c +index 70bfe4eadf5ee845..88d99a416bbe93dd 100644 +--- a/elf/tst-audit19b.c ++++ b/elf/tst-audit19b.c +@@ -69,7 +69,7 @@ do_test (int argc, char *argv[]) + + setenv ("LD_AUDIT", "tst-auditmod18b.so", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit18b", 0, sc_allow_stderr); + + bool find_symbind = false; +diff --git a/elf/tst-audit22.c b/elf/tst-audit22.c +index 4e97be3be0c6a2e3..6aa18af948afa9c5 100644 +--- a/elf/tst-audit22.c ++++ b/elf/tst-audit22.c +@@ -83,7 +83,7 @@ do_test (int argc, char *argv[]) + + setenv ("LD_AUDIT", "tst-auditmod22.so", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr); + + /* The respawned process should always print the vDSO address (otherwise it +diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c +index 1b76336595fcd301..7786a74e648aeb6f 100644 +--- a/elf/tst-audit23.c ++++ b/elf/tst-audit23.c +@@ -87,7 +87,7 @@ do_one_test (int argc, char *argv[], bool pass_dlclose_flag) + + setenv ("LD_AUDIT", "tst-auditmod23.so", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr); + + { +diff --git a/elf/tst-audit25a.c b/elf/tst-audit25a.c +index b209ee820f2f2a02..cdd4f2ce2b54d622 100644 +--- a/elf/tst-audit25a.c ++++ b/elf/tst-audit25a.c +@@ -77,7 +77,7 @@ do_test (int argc, char *argv[]) + + { + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit25a", 0, + sc_allow_stderr); + +@@ -102,7 +102,7 @@ do_test (int argc, char *argv[]) + { + setenv ("LD_BIND_NOW", "1", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit25a", 0, + sc_allow_stderr); + +diff --git a/elf/tst-audit25b.c b/elf/tst-audit25b.c +index 9b8665d5171b7c6b..939f4d6188368540 100644 +--- a/elf/tst-audit25b.c ++++ b/elf/tst-audit25b.c +@@ -76,7 +76,7 @@ do_test (int argc, char *argv[]) + + { + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit25a", 0, + sc_allow_stderr); + +@@ -102,7 +102,7 @@ do_test (int argc, char *argv[]) + { + setenv ("LD_BIND_NOW", "1", 0); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit25a", 0, + sc_allow_stderr); + +diff --git a/elf/tst-glibc-hwcaps-2-cache.c b/elf/tst-glibc-hwcaps-2-cache.c +index 81ab44ff78ddbb57..af91476ccafeecff 100644 +--- a/elf/tst-glibc-hwcaps-2-cache.c ++++ b/elf/tst-glibc-hwcaps-2-cache.c +@@ -32,7 +32,7 @@ main (int argc, char **argv) + /* Run ldconfig to populate the cache. */ + char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir); + struct support_capture_subprocess result = +- support_capture_subprogram (command, &((char *) { NULL })); ++ support_capture_subprogram (command, &((char *) { NULL }), NULL); + support_capture_subprocess_check (&result, "ldconfig", 0, sc_allow_none); + free (command); + +diff --git a/elf/tst-rtld-run-static.c b/elf/tst-rtld-run-static.c +index b2650e85ffbcdc20..f05c00eb7b76958b 100644 +--- a/elf/tst-rtld-run-static.c ++++ b/elf/tst-rtld-run-static.c +@@ -30,7 +30,7 @@ do_test (void) + { + char *argv[] = { (char *) "ld.so", ldconfig_path, (char *) "--help", NULL }; + struct support_capture_subprocess cap +- = support_capture_subprogram (support_objdir_elf_ldso, argv); ++ = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "no --argv0", 0, sc_allow_stdout); + puts ("info: output without --argv0:"); + puts (cap.out.buffer); +@@ -46,7 +46,7 @@ do_test (void) + ldconfig_path, (char *) "--help", NULL + }; + struct support_capture_subprocess cap +- = support_capture_subprogram (support_objdir_elf_ldso, argv); ++ = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL); + support_capture_subprocess_check (&cap, "with --argv0", 0, sc_allow_stdout); + puts ("info: output with --argv0:"); + puts (cap.out.buffer); +diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c +index dff34ed748b4ae83..4884dd09f0ddc505 100644 +--- a/elf/tst-tunables.c ++++ b/elf/tst-tunables.c +@@ -396,7 +396,7 @@ do_test (int argc, char *argv[]) + tests[i].value); + setenv (tests[i].name, tests[i].value, 1); + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-tunables", 0, + sc_allow_stderr); + support_capture_subprocess_free (&result); +diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h +index 8cbdca3b9dfb41ba..57bb941e7d1e5c84 100644 +--- a/support/capture_subprocess.h ++++ b/support/capture_subprocess.h +@@ -35,11 +35,12 @@ struct support_capture_subprocess + struct support_capture_subprocess support_capture_subprocess + (void (*callback) (void *), void *closure); + +-/* Issue FILE with ARGV arguments by using posix_spawn and capture standard +- output, standard error, and the exit status. The out.buffer and err.buffer +- are handle as support_capture_subprocess. */ ++/* Issue FILE with ARGV arguments and ENVP environments by using posix_spawn ++ and capture standard output, standard error, and the exit status. If ++ ENVP is NULL the current environment variable is used. The out.buffer and ++ err.buffer are handle by support_capture_subprocess. */ + struct support_capture_subprocess support_capture_subprogram +- (const char *file, char *const argv[]); ++ (const char *file, char *const argv[], char *const envp[]); + + /* Copy the running program into a setgid binary and run it with + CHILD_ID argument. If the program exits with a non-zero status, +diff --git a/support/subprocess.h b/support/subprocess.h +index 8fbb895353d61965..8274a2b22bb10296 100644 +--- a/support/subprocess.h ++++ b/support/subprocess.h +@@ -33,10 +33,11 @@ struct support_subprocess + struct support_subprocess support_subprocess + (void (*callback) (void *), void *closure); + +-/* Issue FILE with ARGV arguments by using posix_spawn and return is PID, a +- pipe redirected to STDOUT, and a pipe redirected to STDERR. */ ++/* Issue FILE with ARGV arguments and ENVP environments by using posix_spawn ++ and return is PID, a pipe redirected to STDOUT, and a pipe redirected to ++ STDERR. If ENVP is NULL the current environment variable is used. */ + struct support_subprocess support_subprogram +- (const char *file, char *const argv[]); ++ (const char *file, char *const argv[], char *const envp[]); + + /* Invoke program FILE with ARGV arguments by using posix_spawn and wait for it + to complete. Return program exit status. */ +diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c +index 8dc95f8aa723b6bc..cbc695106483ab54 100644 +--- a/support/support_capture_subprocess.c ++++ b/support/support_capture_subprocess.c +@@ -98,13 +98,14 @@ support_capture_subprocess (void (*callback) (void *), void *closure) + } + + struct support_capture_subprocess +-support_capture_subprogram (const char *file, char *const argv[]) ++support_capture_subprogram (const char *file, char *const argv[], ++ char *const envp[]) + { + struct support_capture_subprocess result; + xopen_memstream (&result.out); + xopen_memstream (&result.err); + +- struct support_subprocess proc = support_subprogram (file, argv); ++ struct support_subprocess proc = support_subprogram (file, argv, envp); + + support_capture_poll (&result, &proc); + return result; +diff --git a/support/support_subprocess.c b/support/support_subprocess.c +index a2fef394d42ea4f9..b692a7f8b178502d 100644 +--- a/support/support_subprocess.c ++++ b/support/support_subprocess.c +@@ -69,7 +69,7 @@ support_subprocess (void (*callback) (void *), void *closure) + } + + struct support_subprocess +-support_subprogram (const char *file, char *const argv[]) ++support_subprogram (const char *file, char *const argv[], char *const envp[]) + { + struct support_subprocess result = support_subprocess_init (); + +@@ -84,7 +84,8 @@ support_subprogram (const char *file, char *const argv[]) + xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]); + xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]); + +- result.pid = xposix_spawn (file, &fa, NULL, argv, environ); ++ result.pid = xposix_spawn (file, &fa, NULL, argv, ++ envp == NULL ? environ : envp); + + xclose (result.stdout_pipe[1]); + xclose (result.stderr_pipe[1]); +diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c +index 8145548982a935cb..756fb75d195cdf8a 100644 +--- a/support/tst-support_capture_subprocess.c ++++ b/support/tst-support_capture_subprocess.c +@@ -238,7 +238,7 @@ do_subprogram (const struct test *test) + args[argc] = NULL; + TEST_VERIFY (argc < argv_size); + +- return support_capture_subprogram (args[0], args); ++ return support_capture_subprogram (args[0], args, NULL); + } + + enum test_type +diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c +index bc573c7435130dee..94307283d7cdbdc7 100644 +--- a/sysdeps/x86/tst-hwcap-tunables.c ++++ b/sysdeps/x86/tst-hwcap-tunables.c +@@ -133,7 +133,7 @@ do_test (int argc, char *argv[]) + setenv ("GLIBC_TUNABLES", tunable, 1); + + struct support_capture_subprocess result +- = support_capture_subprogram (spargv[0], spargv); ++ = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-tunables", 0, + sc_allow_stderr); + support_capture_subprocess_free (&result); diff --git a/glibc-RHEL-142193-2.patch b/glibc-RHEL-142193-2.patch new file mode 100644 index 0000000..0d4b684 --- /dev/null +++ b/glibc-RHEL-142193-2.patch @@ -0,0 +1,171 @@ +commit 7b543dcdf97d07fd4346feb17916e08fe83ad0ae +Author: Florian Weimer +Date: Thu Jan 15 22:29:46 2026 +0100 + + elf: Ignore LD_PROFILE if LD_PROFILE_OUTPUT is not set (bug 33797) + + The previous default for LD_PROFILE_OUTPUT, /var/tmp, is insecure + because it's typically a 1777 directory, and other systems could + place malicious files there which interfere with execution. + + Requiring the user to specify a profiling directory mitigates + the impact of bug 33797. Clear LD_PROFILE_OUTPUT alongside + with LD_PROFILE. + + Rework the test not to use predictable file names. + + Reviewed-by: Carlos O'Donell + +Conflicts: + elf/tst-env-setuid.c + (Downstream is missing commit 5d23dfb289174d73b8907b86d2bef7a3ca889840) + +diff --git a/elf/rtld.c b/elf/rtld.c +index cd790e37f2a323a4..8b189a87f76e7e08 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -360,7 +360,6 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = + ._dl_fpu_control = _FPU_DEFAULT, + ._dl_pagesize = EXEC_PAGESIZE, + ._dl_inhibit_cache = 0, +- ._dl_profile_output = "/var/tmp", + + /* Function pointers. */ + ._dl_debug_printf = _dl_debug_printf, +@@ -2735,6 +2734,15 @@ process_envvars_default (struct dl_main_state *state) + } + } + ++ /* There is no fixed, safe directory to store profiling data, so ++ activate LD_PROFILE only if LD_PROFILE_OUTPUT is set as well. */ ++ if (GLRO(dl_profile) != NULL && GLRO(dl_profile_output) == NULL) ++ { ++ _dl_error_printf ("\ ++warning: LD_PROFILE ignored because LD_PROFILE_OUTPUT not specified\n"); ++ GLRO(dl_profile) = NULL; ++ } ++ + /* If we have to run the dynamic linker in debugging mode and the + LD_DEBUG_OUTPUT environment variable is given, we write the debug + messages to this file. */ +diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c +index c084aa4c1a382152..ff4acd07e5e8b986 100644 +--- a/elf/tst-env-setuid.c ++++ b/elf/tst-env-setuid.c +@@ -40,6 +40,12 @@ static char SETGID_CHILD[] = "setgid-child"; + # define PROFILE_LIB "tst-sonamemove-runmod2.so" + #endif + ++/* Computed path for LD_DEBUG_OUTPUT. */ ++static char *debugoutputpath; ++ ++/* Expected file name for erroneous LD_PROFILE output. */ ++static char *profilepath; ++ + struct envvar_t + { + const char *env; +@@ -56,13 +62,14 @@ static const struct envvar_t filtered_envvars[] = + { "LD_LIBRARY_PATH", FILTERED_VALUE }, + { "LD_PRELOAD", FILTERED_VALUE }, + { "LD_PROFILE", PROFILE_LIB }, ++ { "LD_PROFILE_OUTPUT", "/var/tmp" }, /* Not actually used. */ + { "MALLOC_ARENA_MAX", FILTERED_VALUE }, + { "MALLOC_PERTURB_", FILTERED_VALUE }, + { "MALLOC_TRACE", FILTERED_VALUE }, + { "MALLOC_TRIM_THRESHOLD_", FILTERED_VALUE }, + { "RES_OPTIONS", FILTERED_VALUE }, + { "LD_DEBUG", "all" }, +- { "LD_DEBUG_OUTPUT", "/tmp/some-file" }, ++ { "LD_DEBUG_OUTPUT", "overwritten" }, /* Not actually used. */ + { "LD_WARN", FILTERED_VALUE }, + { "LD_VERBOSE", FILTERED_VALUE }, + { "LD_BIND_NOW", "0" }, +@@ -112,18 +119,12 @@ test_child (void) + } + + /* Also check if no profile file was created. +- The parent sets LD_DEBUG_OUTPUT="/tmp/some-file" +- which should be filtered. Then it falls back to "/var/tmp". + Note: LD_PROFILE is not supported for static binaries. */ +- { +- char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB); +- if (!access (profilepath, R_OK)) +- { +- printf ("FAIL: LD_PROFILE file at %s was created!\n", profilepath); +- ret = 1; +- } +- free (profilepath); +- } ++ if (!access (profilepath, R_OK)) ++ { ++ printf ("FAIL: LD_PROFILE file at %s was created!\n", profilepath); ++ ret = 1; ++ } + + return ret; + } +@@ -136,6 +137,11 @@ do_test (int argc, char **argv) + if (argc >= 2 && strstr (argv[1], LD_SO) != 0) + FAIL_UNSUPPORTED ("dynamic test requires --enable-hardcoded-path-in-tests"); + ++ profilepath = xasprintf ("%s/%s.profile", ++ support_objdir_root, PROFILE_LIB); ++ debugoutputpath = xasprintf ("%s/tst-env-setuid-file", ++ support_objdir_root); ++ + /* Setgid child process. */ + if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0) + { +@@ -148,7 +154,6 @@ do_test (int argc, char **argv) + + if (ret != 0) + exit (1); +- return 0; + } + else + { +@@ -162,20 +167,25 @@ do_test (int argc, char **argv) + e++) + setenv (e->env, e->value, 1); + ++ /* Dynamically computed values. */ ++ setenv ("LD_DEBUG_OUTPUT", debugoutputpath, 1); ++ setenv ("LD_PROFILE_OUTPUT", support_objdir_root, 1); ++ + /* Ensure that the profile output does not exist from a previous run + (e.g. if test_dir, which defaults to /tmp, is mounted nosuid.) + Note: support_capture_subprogram_self_sgid creates the SGID binary + in test_dir. */ +- { +- char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB); +- unlink (profilepath); +- free (profilepath); +- } ++ unlink (profilepath); + + support_capture_subprogram_self_sgid (SETGID_CHILD); + +- return 0; ++ /* And clean up afterwards if necessary. */ ++ unlink (profilepath); + } ++ ++ free (profilepath); ++ free (debugoutputpath); ++ return 0; + } + + #define TEST_FUNCTION_ARGV do_test +diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h +index f1724efe0f1fea7b..5ce5f090053eb09a 100644 +--- a/sysdeps/generic/unsecvars.h ++++ b/sysdeps/generic/unsecvars.h +@@ -17,6 +17,7 @@ + "LD_ORIGIN_PATH\0" \ + "LD_PRELOAD\0" \ + "LD_PROFILE\0" \ ++ "LD_PROFILE_OUTPUT\0" \ + "LD_SHOW_AUXV\0" \ + "LD_VERBOSE\0" \ + "LD_WARN\0" \ diff --git a/glibc-RHEL-142193-3.patch b/glibc-RHEL-142193-3.patch new file mode 100644 index 0000000..e1a8ab7 --- /dev/null +++ b/glibc-RHEL-142193-3.patch @@ -0,0 +1,28 @@ +commit 34d98aea6c1eaf7750a992bae55b2bca24898eab +Author: Florian Weimer +Date: Sat Jan 24 10:29:22 2026 +0100 + + support: Fix memory leaks in support_subprogram, support_subprogram_wait + + Reviewed-by: Adhemerval Zanella + +diff --git a/support/support_subprocess.c b/support/support_subprocess.c +index b692a7f8b178502d..ec0c069470a586ac 100644 +--- a/support/support_subprocess.c ++++ b/support/support_subprocess.c +@@ -86,6 +86,7 @@ support_subprogram (const char *file, char *const argv[], char *const envp[]) + + result.pid = xposix_spawn (file, &fa, NULL, argv, + envp == NULL ? environ : envp); ++ posix_spawn_file_actions_destroy (&fa); + + xclose (result.stdout_pipe[1]); + xclose (result.stderr_pipe[1]); +@@ -102,6 +103,7 @@ support_subprogram_wait (const char *file, char *const argv[]) + struct support_subprocess res = support_subprocess_init (); + + res.pid = xposix_spawn (file, &fa, NULL, argv, environ); ++ posix_spawn_file_actions_destroy (&fa); + + return support_process_wait (&res); + } diff --git a/glibc-RHEL-142193-4.patch b/glibc-RHEL-142193-4.patch new file mode 100644 index 0000000..eaac987 --- /dev/null +++ b/glibc-RHEL-142193-4.patch @@ -0,0 +1,52 @@ +commit e8502182f09211663c1583960442eb6ff502a33e +Author: Florian Weimer +Date: Sat Jan 24 10:29:22 2026 +0100 + + support: Add support_hardcoded_paths_in_test + + It indicates whether glibc was configured with + --enable-hardcoded-path-in-tests. + + Reviewed-by: Adhemerval Zanella + +diff --git a/support/Makefile b/support/Makefile +index aee37b9c82e94499..18051532e628b887 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -250,6 +250,12 @@ CFLAGS-support_paths.c = \ + -DCOMPLOCALEDIR_PATH=\"$(complocaledir)\" \ + -DSYSCONFDIR_PATH=\"$(sysconfdir)\" + ++ifeq ($(build-hardcoded-path-in-tests),yes) ++CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=true ++else ++CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=false ++endif ++ + # Build with exception handling and asynchronous unwind table support. + CFLAGS-.oS += -fexceptions -fasynchronous-unwind-tables + +diff --git a/support/support.h b/support/support.h +index 2717e5583add690b..df80c81e4178bc9e 100644 +--- a/support/support.h ++++ b/support/support.h +@@ -147,6 +147,9 @@ extern const char support_complocaledir_prefix[]; + /* Corresponds to the install's etc/ directory. */ + extern const char support_sysconfdir_prefix[]; + ++/* If true, glibc was configured with --enable-hardcoded-path-in-tests. */ ++extern const bool support_hardcoded_paths_in_test; ++ + /* Copies the file at the path FROM to TO. If TO does not exist, it + is created. If TO is a regular file, it is truncated before + copying. The file mode is copied, but the permissions are not. */ +diff --git a/support/support_paths.c b/support/support_paths.c +index 3cad09abd2244a3d..1ad605b6e2273525 100644 +--- a/support/support_paths.c ++++ b/support/support_paths.c +@@ -99,3 +99,5 @@ const char support_sysconfdir_prefix[] = SYSCONFDIR_PATH; + #else + # error please -DSYSCONFDIR_PATH=something in the Makefile + #endif ++ ++const bool support_hardcoded_paths_in_test = HARDCODED_PATHS_IN_TEST; diff --git a/glibc-RHEL-142193-5.patch b/glibc-RHEL-142193-5.patch new file mode 100644 index 0000000..68cdcff --- /dev/null +++ b/glibc-RHEL-142193-5.patch @@ -0,0 +1,40 @@ +commit 458a6a2b935f60a25a136846fe8b7a4723296dda +Author: Florian Weimer +Date: Sat Jan 24 10:29:39 2026 +0100 + + support: Reinitialize containers if /etc is present + + This prevents test failures because configuration file leftovers + unexpectedly change glibc for future tests. Whether this + triggers depends on test execution order. + + Adding postclean.req files manually (before this change) appears + too error-prone. + + Reviewed-by: DJ Delorie + +diff --git a/support/test-container.c b/support/test-container.c +index a9c9926c21f3a21e..f2853f6a71475420 100644 +--- a/support/test-container.c ++++ b/support/test-container.c +@@ -134,7 +134,9 @@ int verbose = 0; + - 'mkdirp': A minimal "mkdir -p FILE" command. + + * mytest.root/postclean.req causes fresh rsync (with delete) after +- test if present ++ test if present. If /etc is present, the testroot is cleaned, ++ too. This prevents further tests from using special ++ configurations in /etc from previous tests. + + * mytest.root/ldconfig.run causes ldconfig to be issued prior + test execution (to setup the initial ld.so.cache). +@@ -868,7 +870,8 @@ main (int argc, char **argv) + if (strrchr (so_base, '/') != NULL) + strrchr (so_base, '/')[1] = 0; + +- if (file_exists (concat (command_root, "/postclean.req", NULL))) ++ if (file_exists (concat (command_root, "/postclean.req", NULL)) ++ || file_exists (concat (command_root, "/etc", NULL))) + do_postclean = 1; + + if (file_exists (concat (command_root, "/ldconfig.run", NULL))) diff --git a/glibc-RHEL-142193-6.patch b/glibc-RHEL-142193-6.patch new file mode 100644 index 0000000..fa23772 --- /dev/null +++ b/glibc-RHEL-142193-6.patch @@ -0,0 +1,506 @@ +commit 229f65f5f322609283c7104c80c8af6434dff628 +Author: Florian Weimer +Date: Mon Feb 2 21:15:48 2026 +0100 + + support: Add support_spawn_wrap and related functionality + + It allows us to write test cases in C that run tests with + dynamic linker wrapping. + + The iconv test case was auto-generated. The posix_spawn usage + is mechanical, and the interface it tests is newly added in this + commit, so this should be acceptable. + + Reviewed-by: Adhemerval Zanella + +diff --git a/support/Makefile b/support/Makefile +index 18051532e628b887..f32652685e3fdc94 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -90,6 +90,7 @@ libsupport-routines = \ + support_shared_allocate \ + support_small_stack_thread_attribute \ + support_socket_so_timestamp_time64 \ ++ support_spawn_wrap \ + support_stack_alloc \ + support_stat_nanoseconds \ + support_subprocess \ +@@ -256,6 +257,11 @@ else + CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=false + endif + ++CFLAGS-support_spawn_wrap.c += \ ++ '-DRUN_PROGRAM_ENV=$(patsubst %, ELEMENT ("%"), $(run-program-env))' \ ++ '-DRTLD_PREFIX=$(patsubst %, ELEMENT ("%"), $(rtld-prefix))' \ ++ # CFLAGS-support_spawn_wrap.c ++ + # Build with exception handling and asynchronous unwind table support. + CFLAGS-.oS += -fexceptions -fasynchronous-unwind-tables + +@@ -344,6 +350,7 @@ tests = \ + tst-support_quote_string \ + tst-support_readdir \ + tst-support_record_failure \ ++ tst-support_spawn_wrap \ + tst-test_compare \ + tst-test_compare_blob \ + tst-test_compare_string \ +diff --git a/support/subprocess.h b/support/subprocess.h +index 8274a2b22bb10296..3438df28c23fac9e 100644 +--- a/support/subprocess.h ++++ b/support/subprocess.h +@@ -52,4 +52,36 @@ int support_process_wait (struct support_subprocess *proc); + then with a SIGKILL. Return the status as for waitpid call. */ + int support_process_terminate (struct support_subprocess *proc); + ++/* Arguments to pass to posix_spawn and related functions to run a ++ process under the built glibc. This overrides the dynamic linker, ++ its search path, and other search paths, such as for locales. */ ++struct support_spawn_wrapped ++{ ++ const char *path; ++ char *const *argv; ++ char *const *envp; ++}; ++ ++enum support_spawn_wrap_flags ++ { ++ /* Always wrap the invocation, even if test binaries are linked ++ with overridden the default paths to point into the build tree ++ (--enable-hardcoded-path-in-tests). Can be used to run ++ non-test binaries. */ ++ support_spawn_wrap_force = 1 << 0, ++ }; ++ ++/* Wrap the invocation for invoking testing. PATH is the program ++ path. If ARGV is null, no arguments are passed. If ENVP is null, ++ environ is used instead. The result must not be modified. It is a ++ deep copy of the inputs. */ ++struct support_spawn_wrapped *support_spawn_wrap (const char *path, ++ char *const argv[], ++ char *const envp[], ++ enum ++ support_spawn_wrap_flags); ++ ++/* Deallocate the result of support_spawn_wrap. */ ++void support_spawn_wrapped_free (struct support_spawn_wrapped *); ++ + #endif +diff --git a/support/support_spawn_wrap.c b/support/support_spawn_wrap.c +new file mode 100644 +index 0000000000000000..e61e12f486ca6a28 +--- /dev/null ++++ b/support/support_spawn_wrap.c +@@ -0,0 +1,171 @@ ++/* Wrap a subprocess invocation with an ld.so invocation. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ELEMENT(s) s, ++static const char *const rtld_prefix[] = { RTLD_PREFIX "--argv0" }; ++static const char *const run_program_env[] = { RUN_PROGRAM_ENV }; ++#undef ELEMENT ++ ++/* Return a newly allocated argument vector, with ld.so wrapping per ++ rtld_prefix applied if WRAP is true. */ ++static char *const * ++rewrite_argv (const char *path, char *const argv[], bool wrap) ++{ ++ char *const substitute[] = { (char *) path, NULL}; ++ if (argv == NULL) ++ argv = substitute; ++ TEST_VERIFY (argv[0] != NULL); ++ ++ size_t length; ++ for (length = 0; argv[length] != 0; ++length) ++ ; ++ /* Potential wrapping, injected path, and null terminator. */ ++ length += array_length (rtld_prefix) + 1 + 1; ++ ++ char **result = xcalloc (length, sizeof (result)); ++ ++ size_t inpos = 0; ++ size_t outpos = 0; ++ if (wrap) ++ { ++ for (size_t i = 0; i < array_length (rtld_prefix); ++i) ++ { ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (rtld_prefix[i]); ++ } ++ ++ /* --argv0 argument. */ ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (argv[0]); ++ inpos = 1; ++ ++ /* Path to program as used by ld.so. */ ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (path); ++ } ++ ++ for (; argv[inpos] != NULL; ++inpos) ++ { ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (argv[inpos]); ++ } ++ ++ TEST_VERIFY (outpos < length); ++ return result; ++ ++} ++ ++/* Return a newly allocated, rewritten environment, with the settings ++ from run_program_env. */ ++static char *const * ++rewrite_env (char *const envp[]) ++{ ++ if (envp == NULL) ++ envp = environ; ++ ++ size_t length; ++ for (length = 0; envp[length] != 0; ++length) ++ ; ++ length += array_length (run_program_env) + 1; ++ ++ /* Set to true if an element of run_program_env is copied. This is ++ used to avoid adding it again. */ ++ bool copied[array_length (run_program_env)] = { false, }; ++ ++ char **result = xcalloc (length, sizeof (result)); ++ size_t outpos = 0; ++ for (size_t inpos = 0; envp[inpos] != NULL; ++inpos) ++ { ++ const char *to_copy = envp[inpos]; ++ /* If there is no assignment operator, this environment string ++ cannot be overridden. */ ++ const char *envp_assign = strchr (to_copy, '='); ++ if (envp_assign != NULL) ++ { ++ size_t length_with_assign = envp_assign - to_copy + 1; ++ for (size_t i = 0; i < array_length (run_program_env); ++i) ++ { ++ if (strncmp (to_copy, run_program_env[i], length_with_assign) ++ == 0 && !copied[i]) ++ { ++ to_copy = run_program_env[i]; ++ copied[i] = true; ++ break; ++ } ++ } ++ } ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (to_copy); ++ } ++ ++ for (size_t i = 0; i < array_length (run_program_env); ++i) ++ { ++ TEST_VERIFY (strchr (run_program_env[i], '=') != 0); ++ if (!copied[i]) ++ { ++ TEST_VERIFY (outpos < length); ++ result[outpos++] = xstrdup (run_program_env[i]); ++ } ++ } ++ ++ TEST_VERIFY (outpos < length); ++ return result; ++} ++ ++struct support_spawn_wrapped * ++support_spawn_wrap (const char *path, ++ char *const argv[], ++ char *const envp[], ++ enum support_spawn_wrap_flags flags) ++{ ++ if (flags != 0) ++ TEST_COMPARE (flags, support_spawn_wrap_force); ++ bool force = flags & support_spawn_wrap_force; ++ bool wrap = force || !support_hardcoded_paths_in_test; ++ ++ struct support_spawn_wrapped *result = xmalloc (sizeof (*result)); ++ if (wrap) ++ result->path = xstrdup (support_objdir_elf_ldso); ++ else ++ result->path = xstrdup (path); ++ result->argv = rewrite_argv (path, argv, wrap); ++ result->envp = rewrite_env (envp); ++ return result; ++} ++ ++void ++support_spawn_wrapped_free (struct support_spawn_wrapped *wrapped) ++{ ++ free ((char *) wrapped->path); ++ for (size_t i = 0; wrapped->argv[i] != NULL; ++i) ++ free (wrapped->argv[i]); ++ free ((char **) wrapped->argv); ++ for (size_t i = 0; wrapped->envp[i] != NULL; ++i) ++ free (wrapped->envp[i]); ++ free ((char **) wrapped->envp); ++ free (wrapped); ++} +diff --git a/support/tst-support_spawn_wrap.c b/support/tst-support_spawn_wrap.c +new file mode 100644 +index 0000000000000000..d0bb8a419412c48d +--- /dev/null ++++ b/support/tst-support_spawn_wrap.c +@@ -0,0 +1,235 @@ ++/* Tests for support_spawn_wrap. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Return true if running via an explicit ld.so invocation. */ ++static bool ++running_via_ldso (void) ++{ ++ char *self = realpath ("/proc/self/exe", NULL); ++ if (self == NULL) ++ FAIL_UNSUPPORTED ("/proc/self/exe not available"); ++ char *ldso = realpath (support_objdir_elf_ldso, NULL); ++ TEST_VERIFY_EXIT (ldso != NULL); ++ bool result = strcmp (self, ldso) == 0; ++ free (ldso); ++ free (self); ++ return result; ++} ++ ++static void ++test_subprocess (int argc, char **argv) ++{ ++ { ++ char *argc_env = getenv ("argc"); ++ if (argc_env != NULL) ++ { ++ char *argc_arg = xasprintf ("%d", argc); ++ TEST_COMPARE_STRING (argc_arg, argc_env); ++ free (argc_arg); ++ } ++ } ++ ++ if (argc >= 2 && strcmp (argv[1], "alpha") == 0) ++ { ++ TEST_COMPARE (argc, 4); ++ TEST_COMPARE_STRING (argv[0], "program"); ++ TEST_COMPARE_STRING (argv[2], "beta"); ++ TEST_COMPARE_STRING (argv[3], "gamma"); ++ TEST_COMPARE_STRING (argv[4], NULL); ++ } ++ else if (argc >= 2 && strcmp (argv[1], "check-env") == 0) ++ printf ("%d %s\n", argc, getenv ("extra")); ++ else if (argc >= 2 && strcmp (argv[1], "check-ld.so") == 0) ++ TEST_VERIFY (running_via_ldso ()); ++} ++ ++/* The "recurse" environment variable and the --recurse option ++ indicate recursive invocation. */ ++static int flag_recurse; ++#define CMDLINE_OPTIONS \ ++ { "recurse", no_argument, &flag_recurse, 1 }, \ ++ /* CMDLINE_OPTION */ ++ ++static void ++prepare (int argc, char **argv) ++{ ++ if (getenv ("recurse") != NULL || flag_recurse) ++ { ++ test_subprocess (argc, argv); ++ support_record_failure_barrier (); ++ exit (0); ++ } ++} ++ ++#define PREPARE prepare ++ ++/* Test wrapping of a non-test program (iconv). This uses posix_spawn ++ directly, mainly for illustrative purposes. */ ++void ++test_iconv (void) ++{ ++ char *iconv_prog = xasprintf ("%s/iconv/iconv_prog", support_objdir_root); ++ char *argv[] = { (char *) "iconv", (char *) "-f", (char *) "UTF-8", ++ (char *) "-t", (char *) "ISO-8859-1", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (iconv_prog, argv, NULL, support_spawn_wrap_force); ++ ++ /* Set up pipes for stdin, stdout, and stderr. */ ++ int stdin_pipe[2]; ++ xpipe (stdin_pipe); ++ int stdout_pipe[2]; ++ xpipe (stdout_pipe); ++ int stderr_pipe[2]; ++ xpipe (stderr_pipe); ++ ++ posix_spawn_file_actions_t fa; ++ posix_spawn_file_actions_init (&fa); ++ xposix_spawn_file_actions_adddup2 (&fa, stdin_pipe[0], STDIN_FILENO); ++ xposix_spawn_file_actions_addclose (&fa, stdin_pipe[0]); ++ xposix_spawn_file_actions_addclose (&fa, stdin_pipe[1]); ++ xposix_spawn_file_actions_adddup2 (&fa, stdout_pipe[1], STDOUT_FILENO); ++ xposix_spawn_file_actions_addclose (&fa, stdout_pipe[0]); ++ xposix_spawn_file_actions_addclose (&fa, stdout_pipe[1]); ++ xposix_spawn_file_actions_adddup2 (&fa, stderr_pipe[1], STDERR_FILENO); ++ xposix_spawn_file_actions_addclose (&fa, stderr_pipe[0]); ++ xposix_spawn_file_actions_addclose (&fa, stderr_pipe[1]); ++ ++ pid_t pid = xposix_spawn (w->path, &fa, NULL, w->argv, w->envp); ++ posix_spawn_file_actions_destroy (&fa); ++ ++ xclose (stdin_pipe[0]); ++ xclose (stdout_pipe[1]); ++ xclose (stderr_pipe[1]); ++ ++ /* Write UTF-8 encoding of "äöü\n" to stdin. */ ++ xwrite (stdin_pipe[1], "\xc3\xa4\xc3\xb6\xc3\xbc\n", 7); ++ xclose (stdin_pipe[1]); ++ ++ /* Read the converted output from the pipe. */ ++ char buf[16]; ++ ssize_t ret = read (stdout_pipe[0], buf, sizeof (buf)); ++ xclose (stdout_pipe[0]); ++ ++ /* ISO-8859-1 encoding of "äöü\n". */ ++ TEST_COMPARE_BLOB (buf, ret, "\xe4\xf6\xfc\n", 4); ++ ++ int status; ++ xwaitpid (pid, &status, 0); ++ TEST_COMPARE (status, 0); ++ ++ /* Check that nothing has been written to stderr. */ ++ ret = read (stderr_pipe[0], buf, sizeof (buf)); ++ TEST_COMPARE (ret, 0); ++ xclose (stderr_pipe[0]); ++ ++ support_spawn_wrapped_free (w); ++ free (iconv_prog); ++} ++ ++static int ++do_test (void) ++{ ++ char *program = xasprintf ("%s/support/tst-support_spawn_wrap", ++ support_objdir_root); ++ ++ { ++ char *env[] = { (char *) "recurse=", (char *) "argc=1", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, NULL, env, 0); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ support_capture_subprocess_check (&proc, "no arguments", 0, sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ support_spawn_wrapped_free (w); ++ } ++ ++ { ++ char *argv[] = { (char *) "program", (char *) "--recurse", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, argv, NULL, 0); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ support_capture_subprocess_check (&proc, "default envvironment", 0, ++ sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ support_spawn_wrapped_free (w); ++ } ++ ++ { ++ char *argv[] = { (char *) "program", (char *) "alpha", (char *) "beta", ++ (char *) "gamma", NULL }; ++ char *env[] = { (char *) "recurse=", (char *) "argc=4", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, argv, env, 0); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ support_capture_subprocess_check (&proc, "3 arguments", 0, sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ support_spawn_wrapped_free (w); ++ } ++ ++ { ++ char *argv[] = { (char *) "program", (char *) "check-env", NULL }; ++ char *env[] = { (char *) "recurse=", (char *) "argc=2", ++ (char *) "extra=17", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, argv, env, 0); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ TEST_COMPARE_STRING (proc.out.buffer, "2 17\n"); ++ support_capture_subprocess_check (&proc, "check-env", 0, sc_allow_stdout); ++ support_capture_subprocess_free (&proc); ++ support_spawn_wrapped_free (w); ++ } ++ ++ test_iconv (); ++ ++ /* This may trigger EXIT_UNSUPPORTED, so run this before the tests ++ that rely on running_via_ldso. */ ++ TEST_COMPARE (!running_via_ldso (), support_hardcoded_paths_in_test); ++ ++ { ++ char *argv[] = { (char *) "program", (char *) "check-ld.so", NULL }; ++ char *env[] = { (char *) "recurse=", NULL }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, argv, env, support_spawn_wrap_force); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ support_capture_subprocess_check (&proc, "check-ld.so", 0, sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ support_spawn_wrapped_free (w); ++ } ++ ++ free (program); ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-142193-7.patch b/glibc-RHEL-142193-7.patch new file mode 100644 index 0000000..65c10f2 --- /dev/null +++ b/glibc-RHEL-142193-7.patch @@ -0,0 +1,171 @@ +commit 364426a59ee30ee3e528e5b5cae36b5dee045320 +Author: Florian Weimer +Date: Mon Feb 2 21:15:48 2026 +0100 + + elf: Add test case for LD_PROFILE/LD_PROFILE_OUTPUT interaction + + This verifies that LD_PROFILE is correctly ignored if LD_PROFILE_OUTPUT + is not set. + + The test was initially auto-generated, then heavily edited and re-edited + for brevity and clarity. The test uses glibc-specific interfaces + (including one that did not exist at all a couple of hours ago), so + this should be unproblematic. + +diff --git a/elf/Makefile b/elf/Makefile +index cf54ea084489b9cf..73c426ae9c1da05c 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -448,6 +448,7 @@ tests += \ + tst-initorder \ + tst-initorder2 \ + tst-latepthread \ ++ tst-ld_profile \ + tst-main1 \ + tst-next-ver \ + tst-nodelete-dlclose \ +diff --git a/elf/tst-ld_profile.c b/elf/tst-ld_profile.c +new file mode 100644 +index 0000000000000000..6c392f682b950b00 +--- /dev/null ++++ b/elf/tst-ld_profile.c +@@ -0,0 +1,139 @@ ++/* Test LD_PROFILE/LD_PROFILE_OUTPUT interaction (bug 33797). ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Expected profile file path (based on LD_PROFILE_OUTPUT and LIBC_SO). */ ++static char *profile_file_path; ++ ++/* Path to this test program for recursive invocation. */ ++static char *program; ++ ++/* LD_PROFILE_OUTPUT environment variable setting. */ ++static char *ld_profile_output_env; ++ ++/* Run the test program with the specified environment array. */ ++static struct support_capture_subprocess ++run_test_program (char *env[]) ++{ ++ /* Make sure the the potential output file does not exist. */ ++ unlink (profile_file_path); ++ ++/* Command line arguments for recursive invocation. This turns the ++ test program in a no-op (with LD_PROFILE output the only side effect). */ ++ static char *recurse_argv[] = ++ { ++ (char *) "tst-ld_profile", (char *) "recurse", NULL ++ }; ++ struct support_spawn_wrapped *w ++ = support_spawn_wrap (program, recurse_argv, env, 0); ++ struct support_capture_subprocess proc ++ = support_capture_subprogram (w->path, w->argv, w->envp); ++ support_spawn_wrapped_free (w); ++ ++ return proc; ++} ++ ++static void ++test_profile_without_output (void) ++{ ++ char *env[] = { (char *) "LD_PROFILE=" LIBC_SO, NULL }; ++ struct support_capture_subprocess proc = run_test_program (env); ++ const char *expected_warning = ++ "warning: LD_PROFILE ignored because LD_PROFILE_OUTPUT not specified\n"; ++ TEST_COMPARE_STRING (proc.err.buffer, expected_warning); ++ support_capture_subprocess_check (&proc, ++ "LD_PROFILE without LD_PROFILE_OUTPUT", ++ 0, sc_allow_stderr); ++ support_capture_subprocess_free (&proc); ++ ++ TEST_VERIFY (access (profile_file_path, F_OK) != 0); ++ /* Also check the old /var/tmp path. */ ++ TEST_VERIFY (access ("/var/tmp/" LIBC_SO ".profile", F_OK) != 0); ++} ++ ++static void ++test_profile_with_output (void) ++{ ++ char *env[] = { (char *) "LD_PROFILE=" LIBC_SO, ld_profile_output_env, NULL }; ++ struct support_capture_subprocess proc = run_test_program (env); ++ support_capture_subprocess_check (&proc, "LD_PROFILE with LD_PROFILE_OUTPUT", ++ 0, sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ ++ /* This asserts that the file was created. */ ++ TEST_COMPARE (unlink (profile_file_path), 0); ++} ++ ++static void ++test_output_without_profile (void) ++{ ++ char *env[] = { ld_profile_output_env, NULL }; ++ struct support_capture_subprocess proc = run_test_program (env); ++ support_capture_subprocess_check (&proc, ++ "LD_PROFILE_OUTPUT without LD_PROFILE", ++ 0, sc_allow_none); ++ support_capture_subprocess_free (&proc); ++ ++ TEST_VERIFY (access (profile_file_path, F_OK) != 0); ++} ++ ++static void ++prepare (int argc, char **argv) ++{ ++ /* Do nothing on recursive invocation. */ ++ if (argc >= 2 && strcmp (argv[1], "recurse") == 0) ++ exit (0); ++} ++ ++#define PREPARE prepare ++ ++static int ++do_test (void) ++{ ++ if (access ("/var/tmp/" LIBC_SO ".profile", F_OK) == 0) ++ FAIL_UNSUPPORTED ("/var/tmp/" LIBC_SO ".profile exists"); ++ ++ /* Temporary directory for the profile output. */ ++ char *profile_output_dir = support_create_temp_directory ("tst-ld_profile"); ++ profile_file_path = xasprintf ("%s/%s.profile", profile_output_dir, LIBC_SO); ++ ld_profile_output_env = xasprintf ("LD_PROFILE_OUTPUT=%s", ++ profile_output_dir); ++ program = xasprintf ("%s/elf/tst-ld_profile", support_objdir_root); ++ ++ test_profile_without_output (); ++ test_profile_with_output (); ++ test_output_without_profile (); ++ ++ free (program); ++ free (ld_profile_output_env); ++ free (profile_file_path); ++ free (profile_output_dir); ++ ++ return 0; ++} ++ ++#include