diff --git a/SOURCES/glibc-RHEL-1018.patch b/SOURCES/glibc-RHEL-1018.patch new file mode 100644 index 0000000..ffe3c50 --- /dev/null +++ b/SOURCES/glibc-RHEL-1018.patch @@ -0,0 +1,24 @@ +commit fc72b6d7d818ab2868920af956d1542d03342a4d +Author: Andreas Schwab +Date: Tue Aug 1 17:01:37 2023 +0200 + + iconv: restore verbosity with unrecognized encoding names (bug 30694) + + Commit 91927b7c76 ("Rewrite iconv option parsing [BZ #19519]") changed the + iconv program to call __gconv_open directly instead of the iconv_open + wrapper, but the former does not set errno. Update the caller to + interpret the return codes like iconv_open does. + +diff --git a/iconv/iconv_prog.c b/iconv/iconv_prog.c +index 08ea99d6adf6ea86..f199db6020284ad1 100644 +--- a/iconv/iconv_prog.c ++++ b/iconv/iconv_prog.c +@@ -201,7 +201,7 @@ main (int argc, char *argv[]) + + if (res != __GCONV_OK) + { +- if (errno == EINVAL) ++ if (res == __GCONV_NOCONV || res == __GCONV_NODB) + { + /* Try to be nice with the user and tell her which of the + two encoding names is wrong. This is possible because diff --git a/SOURCES/glibc-RHEL-140105.patch b/SOURCES/glibc-RHEL-140105.patch new file mode 100644 index 0000000..1b763ae --- /dev/null +++ b/SOURCES/glibc-RHEL-140105.patch @@ -0,0 +1,59 @@ +commit 559010e471acb3cb292615b71b248aba73e5c2fe +Author: Dragan Stanojević (Nevidljivi) +Date: Wed Feb 7 16:31:04 2024 +0100 + + localedata: hr_HR: change currency to EUR/€ + + Resolves: BZ # 29845 + +Conflicts: + localedata/locales/hr_HR + (locale definitions do not use UTF-8 downstream) + +diff --git a/localedata/locales/hr_HR b/localedata/locales/hr_HR +index 029a3794e2a17dcb..a13370461eede9c1 100644 +--- a/localedata/locales/hr_HR ++++ b/localedata/locales/hr_HR +@@ -24,8 +24,8 @@ tel "" + fax "" + language "Croatian" + territory "Croatia" +-revision "2.3" +-date "2016-04-16" ++revision "2.4" ++date "2022-12-03" + + category "i18n:2012";LC_IDENTIFICATION + category "i18n:2012";LC_CTYPE +@@ -171,8 +171,8 @@ nostr "ne" + END LC_MESSAGES + + LC_MONETARY +-int_curr_symbol "HRK " +-currency_symbol "kn" ++int_curr_symbol "EUR " ++currency_symbol "" + mon_decimal_point "," + mon_thousands_sep "." + mon_grouping 3;3 +diff --git a/stdlib/tst-strfmon_l.c b/stdlib/tst-strfmon_l.c +index 603d4b0c3d91818b..65313cd14b61dfe4 100644 +--- a/stdlib/tst-strfmon_l.c ++++ b/stdlib/tst-strfmon_l.c +@@ -181,12 +181,12 @@ static const struct locale_pair tests[] = + "hr_HR.UTF-8", + { + { +- "HRK 1.234.567,89", "1.234.567,89 kn", +- "HRK 1234567,89", "1234567,89 kn" ++ "EUR 1.234.567,89", "1.234.567,89 €", ++ "EUR 1234567,89", "1234567,89 €" + }, + { +- "-HRK 1.234.567,89", "-1.234.567,89 kn", +- "-HRK 1234567,89", "-1234567,89 kn" ++ "-EUR 1.234.567,89", "-1.234.567,89 €", ++ "-EUR 1234567,89", "-1234567,89 €" + } + } + }, diff --git a/SOURCES/glibc-RHEL-141072.patch b/SOURCES/glibc-RHEL-141072.patch new file mode 100644 index 0000000..496a62f --- /dev/null +++ b/SOURCES/glibc-RHEL-141072.patch @@ -0,0 +1,46 @@ +commit 63716823dbad9482e09972907ae98e9cb00f9b86 +Author: Sunil K Pandey +Date: Tue Dec 9 08:57:44 2025 -0800 + + nptl: Optimize trylock for high cache contention workloads (BZ #33704) + + Check lock availability before acquisition to reduce cache line + bouncing. Significantly improves trylock throughput on multi-core + systems under heavy contention. + + Tested on x86_64. + + Fixes BZ #33704. + + Co-authored-by: Alex M Wells + Reviewed-by: Wilco Dijkstra + +Conflicts: + nptl/pthread_mutex_trylock.c + Updated for minor context difference + +diff -Nrup a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c +--- a/nptl/pthread_mutex_trylock.c 2021-08-01 21:33:43.000000000 -0400 ++++ b/nptl/pthread_mutex_trylock.c 2026-02-06 13:29:01.508899356 -0500 +@@ -49,7 +49,8 @@ ___pthread_mutex_trylock (pthread_mutex_ + return 0; + } + +- if (lll_trylock (mutex->__data.__lock) == 0) ++ if (atomic_load_relaxed (&(mutex->__data.__lock)) == 0 ++ && lll_trylock (mutex->__data.__lock) == 0) + { + /* Record the ownership. */ + mutex->__data.__owner = id; +@@ -72,7 +73,10 @@ ___pthread_mutex_trylock (pthread_mutex_ + /*FALL THROUGH*/ + case PTHREAD_MUTEX_ADAPTIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: +- if (lll_trylock (mutex->__data.__lock) != 0) ++ /* Mutex type is already loaded, lock check overhead should ++ be minimal. */ ++ if (atomic_load_relaxed (&(mutex->__data.__lock)) != 0 ++ || lll_trylock (mutex->__data.__lock) != 0) + break; + + /* Record the ownership. */ diff --git a/SOURCES/glibc-RHEL-142196-1.patch b/SOURCES/glibc-RHEL-142196-1.patch new file mode 100644 index 0000000..fa52d55 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-1.patch @@ -0,0 +1,52 @@ +commit 4a133885a7c8ae7ebe34e36fcdb353f8e94c810f +Author: Adhemerval Zanella +Date: Mon Nov 6 17:25:45 2023 -0300 + + elf: Ignore LD_PROFILE for setuid binaries + + Loader does not ignore LD_PROFILE in secure-execution mode (different + than man-page states [1]), rather it uses a different path + (/var/profile) and ignore LD_PROFILE_OUTPUT. + + Allowing secure-execution profiling is already a non good security + boundary, since it enables different code paths and extra OS access by + the process. But by ignoring LD_PROFILE_OUTPUT, the resulting profile + file might also be acceded in a racy manner since the file name does not + use any process-specific information (such as pid, timing, etc.). + + Another side-effect is it forces lazy binding even on libraries that + might be with DF_BIND_NOW. + + [1] https://man7.org/linux/man-pages/man8/ld.so.8.html + Reviewed-by: Siddhesh Poyarekar + +Conflicts: + elf/Makefile + elf/tst-env-setuid.c + (test does not have LD_PROFILE support downstream) + +diff --git a/elf/rtld.c b/elf/rtld.c +index 1fbfc65a38e19110..44546e8e158e7b9c 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2761,10 +2761,6 @@ process_envvars (struct dl_main_state *state) + char *envline; + char *debug_output = NULL; + +- /* This is the default place for profiling data file. */ +- GLRO(dl_profile_output) +- = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0]; +- + while ((envline = _dl_next_ld_env_entry (&runp)) != NULL) + { + size_t len = 0; +@@ -2813,7 +2809,8 @@ process_envvars (struct dl_main_state *state) + } + + /* Which shared object shall be profiled. */ +- if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0') ++ if (!__libc_enable_secure ++ && memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0') + GLRO(dl_profile) = &envline[8]; + break; + diff --git a/SOURCES/glibc-RHEL-142196-2.patch b/SOURCES/glibc-RHEL-142196-2.patch new file mode 100644 index 0000000..f12aeb8 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-2.patch @@ -0,0 +1,243 @@ +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.c + elf/tst-tunables-enable_secure.c + sysdeps/x86/tst-hwcap-tunables.c + (files do 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/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/SOURCES/glibc-RHEL-142196-3.patch b/SOURCES/glibc-RHEL-142196-3.patch new file mode 100644 index 0000000..28567d2 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-3.patch @@ -0,0 +1,56 @@ +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/rtld.c + (different implementation of environment variable filtering + downstream) + elf/tst-env-setuid.c + (no LD_PROFILE test downstream) + +diff -Nrup a/elf/rtld.c b/elf/rtld.c +--- a/elf/rtld.c 2026-02-25 14:11:17.505620756 -0500 ++++ b/elf/rtld.c 2026-02-25 14:14:43.355916730 -0500 +@@ -2974,6 +2974,15 @@ process_envvars (struct dl_main_state *s + /* We use standard output if opening the file failed. */ + GLRO(dl_debug_fd) = STDOUT_FILENO; + } ++ ++ /* 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 HP_TIMING_INLINE +diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h +index 5ea8a4a259ef753c..0b84642f71ae9351 100644 +--- a/sysdeps/generic/unsecvars.h ++++ b/sysdeps/generic/unsecvars.h +@@ -21,6 +21,7 @@ + "LD_ORIGIN_PATH\0" \ + "LD_PRELOAD\0" \ + "LD_PROFILE\0" \ ++ "LD_PROFILE_OUTPUT\0" \ + "LD_SHOW_AUXV\0" \ + "LD_USE_LOAD_BIAS\0" \ + "LOCALDOMAIN\0" \ diff --git a/SOURCES/glibc-RHEL-142196-4.patch b/SOURCES/glibc-RHEL-142196-4.patch new file mode 100644 index 0000000..e1a8ab7 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-4.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/SOURCES/glibc-RHEL-142196-5.patch b/SOURCES/glibc-RHEL-142196-5.patch new file mode 100644 index 0000000..84b027a --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-5.patch @@ -0,0 +1,49 @@ +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 -Nrup a/support/Makefile b/support/Makefile +--- a/support/Makefile 2026-02-23 07:44:09.189237390 -0500 ++++ b/support/Makefile 2026-02-23 07:49:33.975521231 -0500 +@@ -252,6 +252,12 @@ CFLAGS-support_paths.c = \ + -DROOTSBINDIR_PATH=\"$(rootsbindir)\" \ + -DCOMPLOCALEDIR_PATH=\"$(complocaledir)\" + ++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 ++ + # In support_timespec_check_in_range we may be passed a very tight + # range for which we should produce a correct result for expected + # being withing the observed range. The code uses double internally +diff -Nrup a/support/support.h b/support/support.h +--- a/support/support.h 2026-02-23 07:44:09.127896391 -0500 ++++ b/support/support.h 2026-02-23 07:52:57.800614726 -0500 +@@ -144,6 +144,9 @@ extern const char support_install_rootsb + /* Corresponds to the install's compiled locale directory. */ + extern const char support_complocaledir_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 -Nrup a/support/support_paths.c b/support/support_paths.c +--- a/support/support_paths.c 2021-08-01 21:33:43.000000000 -0400 ++++ b/support/support_paths.c 2026-02-23 07:56:12.231942401 -0500 +@@ -92,3 +92,5 @@ const char support_complocaledir_prefix[ + #else + # error please -DCOMPLOCALEDIR_PATH=something in the Makefile + #endif ++ ++const bool support_hardcoded_paths_in_test = HARDCODED_PATHS_IN_TEST; diff --git a/SOURCES/glibc-RHEL-142196-6.patch b/SOURCES/glibc-RHEL-142196-6.patch new file mode 100644 index 0000000..68cdcff --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-6.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/SOURCES/glibc-RHEL-142196-7.patch b/SOURCES/glibc-RHEL-142196-7.patch new file mode 100644 index 0000000..7997560 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-7.patch @@ -0,0 +1,505 @@ +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 + +Conflicts: + support/Makefile + (Remove duplicate entry for support_stack_alloc) + +diff -Nrup a/support/Makefile b/support/Makefile +--- a/support/Makefile 2026-02-23 11:21:06.705964233 -0500 ++++ b/support/Makefile 2026-02-23 11:46:54.560737380 -0500 +@@ -90,7 +90,7 @@ libsupport-routines = \ + support_shared_allocate \ + support_small_stack_thread_attribute \ + support_socket_so_timestamp_time64 \ +- support_stack_alloc \ ++ support_spawn_wrap \ + support_stack_alloc \ + support_stat_nanoseconds \ + support_subprocess \ +@@ -258,6 +258,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 ++ + # In support_timespec_check_in_range we may be passed a very tight + # range for which we should produce a correct result for expected + # being withing the observed range. The code uses double internally +@@ -343,6 +348,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 -Nrup a/support/subprocess.h b/support/subprocess.h +--- a/support/subprocess.h 2021-08-01 21:33:43.000000000 -0400 ++++ b/support/subprocess.h 2026-02-23 11:22:26.578465391 -0500 +@@ -51,4 +51,36 @@ int support_process_wait (struct support + 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 -Nrup a/support/support_spawn_wrap.c b/support/support_spawn_wrap.c +--- a/support/support_spawn_wrap.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/support/support_spawn_wrap.c 2026-02-23 11:22:26.578713214 -0500 +@@ -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 -Nrup a/support/tst-support_spawn_wrap.c b/support/tst-support_spawn_wrap.c +--- a/support/tst-support_spawn_wrap.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/support/tst-support_spawn_wrap.c 2026-02-23 11:22:26.578942243 -0500 +@@ -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/SOURCES/glibc-RHEL-142196-8.patch b/SOURCES/glibc-RHEL-142196-8.patch new file mode 100644 index 0000000..a999fc8 --- /dev/null +++ b/SOURCES/glibc-RHEL-142196-8.patch @@ -0,0 +1,168 @@ +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 -Nrup a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2026-02-23 12:26:54.652168428 -0500 ++++ b/elf/Makefile 2026-02-23 12:30:15.356267040 -0500 +@@ -442,6 +442,7 @@ tests += \ + tst-initorder \ + tst-initorder2 \ + tst-latepthread \ ++ tst-ld_profile \ + tst-main1 \ + tst-nodelete2 \ + tst-nodelete-dlclose \ +diff -Nrup a/elf/tst-ld_profile.c b/elf/tst-ld_profile.c +--- a/elf/tst-ld_profile.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-ld_profile.c 2026-02-23 12:27:49.770154596 -0500 +@@ -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 diff --git a/SOURCES/glibc-RHEL-142209.patch b/SOURCES/glibc-RHEL-142209.patch new file mode 100644 index 0000000..0b0cc99 --- /dev/null +++ b/SOURCES/glibc-RHEL-142209.patch @@ -0,0 +1,52 @@ +commit 55e85c1e48c2aae71c0b5907fd22a3e9b978b6e8 +Author: H.J. Lu +Date: Fri Jul 18 17:03:04 2025 -0700 + + io/tst-stat.c: Use a temporary directory for symlink test + + Call support_create_temp_directory to create a temporary directory for + symlink test, instead of a fixed file in the glibc source tree, to avoid + the race condition when there are more than one glibc tests running at the + same time with the same glibc source tree. This fixes BZ #33178. + + Signed-off-by: H.J. Lu + Reviewed-by: Andreas K. Huettel + +diff --git a/io/tst-stat.c b/io/tst-stat.c +index 269c62d29e7e93fa..af31b4b39d3d65a8 100644 +--- a/io/tst-stat.c ++++ b/io/tst-stat.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + static void + stat_check (int fd, const char *path, struct stat *st) +@@ -78,7 +79,8 @@ static int + do_test (void) + { + char *path; +- const char *linkame = "tst-fstat.linkname"; ++ char *tempdir = support_create_temp_directory ("tst-stat-"); ++ char *linkname = xasprintf ("%s/tst-fstat.linkname", tempdir); + int fd = create_temp_file ("tst-fstat.", &path); + TEST_VERIFY_EXIT (fd >= 0); + support_write_file_string (path, "abc"); +@@ -122,9 +124,12 @@ do_test (void) + } + } + +- TEST_COMPARE (symlink ("tst-fstat.target", linkame), 0); +- add_temp_file (linkame); +- fstatat_link (linkame, &st); ++ TEST_COMPARE (symlink ("tst-fstat.target", linkname), 0); ++ add_temp_file (linkname); ++ fstatat_link (linkname, &st); ++ ++ free (linkname); ++ free (tempdir); + + return 0; + } diff --git a/SOURCES/glibc-RHEL-145156-1.patch b/SOURCES/glibc-RHEL-145156-1.patch new file mode 100644 index 0000000..d591eea --- /dev/null +++ b/SOURCES/glibc-RHEL-145156-1.patch @@ -0,0 +1,206 @@ +commit 0981c03c2752b5f12ede24e6e696d5a29f7c6396 +Author: Frédéric Bérat +Date: Wed Apr 29 15:53:51 2026 +0200 + + libio: Fix gconv module reference counter overflow in swscanf + + The swscanf family of functions creates a wide-oriented FILE stream + on the stack. Initialization of this stream invokes `_IO_fwide`, which + clones the global locale's gconv transformation steps via + `__wcsmbs_clone_conv`. This increments the reference counter (`__counter`) + of the gconv module. + + Because the FILE stream is stack-allocated, `fclose` cannot be called, + and so `__gconv_release_step` is never invoked. The counter leaks, + eventually hitting the 32-bit integer overflow limit and aborting the + process. + + To resolve this, we introduce `_IO_wstrfile_fclose_stack`, a dedicated + cleanup function for stack-allocated FILE streams. This function invokes + `_IO_FINISH` and correctly releases the gconv steps via + `__gconv_release_step` without attempting to `free` the FILE pointer. + This cleanup function is then hooked into all variants of swscanf right + before they return. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + sysdeps/ieee754/ldbl-opt/nldbl-compat.c + (isoc23 variants not present downstream) + sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c + (not present downstream) + sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c + (not present downstream) + wcsmbs/isoc23_swscanf.c + (not present downstream) + wcsmbs/isoc23_vswscanf.c + (not present downstream) + +diff --git a/libio/iofwide.c b/libio/iofwide.c +index a7f29fa0b9693cc3..972f154365e88bb3 100644 +--- a/libio/iofwide.c ++++ b/libio/iofwide.c +@@ -257,3 +257,18 @@ __libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep, + + return result; + } ++ ++void ++_IO_wstrfile_fclose_stack (FILE *fp) ++{ ++ _IO_FINISH (fp); ++ if (fp->_mode > 0) ++ { ++ struct _IO_codecvt *cc = fp->_codecvt; ++ ++ __libc_lock_lock (__gconv_lock); ++ __gconv_release_step (cc->__cd_in.step); ++ __gconv_release_step (cc->__cd_out.step); ++ __libc_lock_unlock (__gconv_lock); ++ } ++} +diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c +index 35bddc0daeca917f..284c61615ebddd47 100644 +--- a/libio/iovswscanf.c ++++ b/libio/iovswscanf.c +@@ -38,6 +38,8 @@ __vswscanf (const wchar_t *string, const wchar_t *format, va_list args) + _IO_strfile sf; + struct _IO_wide_data wd; + FILE *f = _IO_strfile_readw (&sf, &wd, string); +- return __vfwscanf_internal (f, format, args, 0); ++ int done = __vfwscanf_internal (f, format, args, 0); ++ _IO_wstrfile_fclose_stack (f); ++ return done; + } + ldbl_weak_alias (__vswscanf, vswscanf) +diff --git a/libio/libioP.h b/libio/libioP.h +index 50570f89de5a7010..76fd7853dc087bf9 100644 +--- a/libio/libioP.h ++++ b/libio/libioP.h +@@ -594,6 +594,7 @@ extern FILE* _IO_new_file_fopen (FILE *, const char *, const char *, + int); + extern void _IO_no_init (FILE *, int, int, struct _IO_wide_data *, + const struct _IO_jump_t *) __THROW; ++extern void _IO_wstrfile_fclose_stack (FILE *) attribute_hidden; + extern void _IO_new_file_init_internal (struct _IO_FILE_plus *) + __THROW attribute_hidden; + extern FILE* _IO_new_file_setbuf (FILE *, char *, ssize_t); +diff --git a/libio/swscanf.c b/libio/swscanf.c +index 88a19144342cdc38..cb3795599c6f70c6 100644 +--- a/libio/swscanf.c ++++ b/libio/swscanf.c +@@ -37,7 +37,7 @@ __swscanf (const wchar_t *s, const wchar_t *format, ...) + va_start (arg, format); + done = __vfwscanf_internal (f, format, arg, 0); + va_end (arg); +- ++ _IO_wstrfile_fclose_stack (f); + return done; + } + ldbl_strong_alias (__swscanf, swscanf) +diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c +index ef8c1317dcd7f5e5..96bf2ac3559186e7 100644 +--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c ++++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c +@@ -34,7 +34,7 @@ ___ieee128_isoc99_swscanf (const wchar_t *string, const wchar_t *format, ...) + va_start (ap, format); + done = __vfwscanf_internal (fp, format, ap, mode_flags); + va_end (ap); +- ++ _IO_wstrfile_fclose_stack (fp); + return done; + } + strong_alias (___ieee128_isoc99_swscanf, __isoc99_swscanfieee128) +diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c +index f77bb64638a4717f..8fa10ee2e22e238f 100644 +--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c ++++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c +@@ -27,6 +27,8 @@ ___ieee128_isoc99_vswscanf (wchar_t *string, const wchar_t *format, va_list ap) + struct _IO_wide_data wd; + FILE *fp = _IO_strfile_readw (&sf, &wd, string); + int mode_flags = SCANF_ISOC99_A | SCANF_LDBL_USES_FLOAT128; +- return __vfwscanf_internal (fp, format, ap, mode_flags); ++ int done = __vfwscanf_internal (fp, format, ap, mode_flags); ++ _IO_wstrfile_fclose_stack (fp); ++ return done; + } + strong_alias (___ieee128_isoc99_vswscanf, __isoc99_vswscanfieee128) +diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c +index 04ee5419200298b1..509eaa663d0a9a33 100644 +--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c ++++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c +@@ -34,7 +34,7 @@ ___ieee128_swscanf (const wchar_t *string, const wchar_t *format, ...) + done = __vfwscanf_internal (fp, format, ap, + SCANF_LDBL_USES_FLOAT128); + va_end (ap); +- ++ _IO_wstrfile_fclose_stack (fp); + return done; + } + strong_alias (___ieee128_swscanf, __swscanfieee128) +diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c +index 7aebc5f1c12939b6..18aa6deaacdde682 100644 +--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c ++++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c +@@ -27,6 +27,8 @@ ___ieee128_vswscanf (const wchar_t *string, const wchar_t *format, + _IO_strfile sf; + struct _IO_wide_data wd; + FILE *fp = _IO_strfile_readw (&sf, &wd, string); +- return __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128); ++ int done = __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128); ++ _IO_wstrfile_fclose_stack (fp); ++ return done; + } + strong_alias (___ieee128_vswscanf, __vswscanfieee128) +diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c +index a6c5c49ecb151bf5..81d53a4cf30acb89 100644 +--- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c ++++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c +@@ -388,7 +388,9 @@ __nldbl_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap) + struct _IO_wide_data wd; + FILE *f = _IO_strfile_readw (&sf, &wd, s); + +- return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL); ++ int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL); ++ _IO_wstrfile_fclose_stack (f); ++ return ret; + } + libc_hidden_def (__nldbl_vswscanf) + +@@ -952,7 +954,9 @@ __nldbl___isoc99_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap) + struct _IO_wide_data wd; + FILE *f = _IO_strfile_readw (&sf, &wd, s); + +- return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A); ++ int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A); ++ _IO_wstrfile_fclose_stack (f); ++ return ret; + } + libc_hidden_def (__nldbl___isoc99_vswscanf) + +diff --git a/wcsmbs/isoc99_swscanf.c b/wcsmbs/isoc99_swscanf.c +index bb6d8f2035d8e4a4..3fda1f7be695b31e 100644 +--- a/wcsmbs/isoc99_swscanf.c ++++ b/wcsmbs/isoc99_swscanf.c +@@ -32,6 +32,6 @@ __isoc99_swscanf (const wchar_t *s, const wchar_t *format, ...) + va_start (arg, format); + done = __vfwscanf_internal (f, format, arg, SCANF_ISOC99_A); + va_end (arg); +- ++ _IO_wstrfile_fclose_stack (f); + return done; + } +diff --git a/wcsmbs/isoc99_vswscanf.c b/wcsmbs/isoc99_vswscanf.c +index 3cd0a28c21ffa36f..1df90438416d3bb7 100644 +--- a/wcsmbs/isoc99_vswscanf.c ++++ b/wcsmbs/isoc99_vswscanf.c +@@ -33,6 +33,8 @@ __isoc99_vswscanf (const wchar_t *string, const wchar_t *format, va_list args) + _IO_strfile sf; + struct _IO_wide_data wd; + FILE *f = _IO_strfile_readw (&sf, &wd, string); +- return __vfwscanf_internal (f, format, args, SCANF_ISOC99_A); ++ int done = __vfwscanf_internal (f, format, args, SCANF_ISOC99_A); ++ _IO_wstrfile_fclose_stack (f); ++ return done; + } + libc_hidden_def (__isoc99_vswscanf) diff --git a/SOURCES/glibc-RHEL-145156-2.patch b/SOURCES/glibc-RHEL-145156-2.patch new file mode 100644 index 0000000..01b7d9c --- /dev/null +++ b/SOURCES/glibc-RHEL-145156-2.patch @@ -0,0 +1,57 @@ +libio: Fix gconv module reference counter overflow in vswprintf + +The vswprintf family of functions (swprintf, vswprintf and their +fortified/ldbl-compat wrappers) creates a wide-oriented FILE stream +on the stack via _IO_no_init with _IO_wstrn_jumps, then calls +_IO_fwide which clones the gconv transformation steps via +__wcsmbs_clone_conv, incrementing the __counter reference counter. + +Because the FILE is stack-allocated, fclose is never called, and the +counter is never decremented. In a long-running process calling +swprintf repeatedly with a non-builtin locale (e.g. en_US.UTF-8), +the counter eventually overflows, triggering a fatal abort. + +This is the same class of bug fixed upstream for swscanf in commit +0981c03c2752b5f12ede24e6e696d5a29f7c6396. Upstream resolved the +swprintf path architecturally by reworking the printf subsystem to +use struct __wprintf_buffer instead of FILE * (commit 118816de3383), +eliminating the _IO_fwide call entirely. That rework is too large +to backport to glibc 2.34. + +Instead, apply the same pattern as the swscanf fix: call +_IO_wstrfile_fclose_stack after __vfwprintf_internal returns to +release the gconv steps before the stack FILE goes out of scope. +All ldbl-compat wrappers (ieee128, nldbl) call __vswprintf_internal, +so they are covered by this single change. + +Note: _IO_wstrfile_fclose_stack calls _IO_FINISH which invokes +_IO_wstr_finish, and that NULLs out _IO_buf_base. Therefore the +cleanup must happen after the overflow check (which compares +_IO_buf_base against overflow_buf) and after string termination +(which dereferences _IO_write_ptr). + +diff --git a/libio/vswprintf.c b/libio/vswprintf.c +index b7036f1480733ac6..accbb8516f860680 100644 +--- a/libio/vswprintf.c ++++ b/libio/vswprintf.c +@@ -111,13 +111,17 @@ __vswprintf_internal (wchar_t *string, size_t maxlen, const wchar_t *format, + ret = __vfwprintf_internal ((FILE *) &sf.f._sbf, format, args, mode_flags); + + if (sf.f._sbf._f._wide_data->_IO_buf_base == sf.overflow_buf) +- /* ISO C99 requires swprintf/vswprintf to return an error if the +- output does not fit in the provided buffer. */ +- return -1; ++ { ++ /* ISO C99 requires swprintf/vswprintf to return an error if the ++ output does not fit in the provided buffer. */ ++ _IO_wstrfile_fclose_stack ((FILE *) &sf.f._sbf); ++ return -1; ++ } + + /* Terminate the string. */ + *sf.f._sbf._f._wide_data->_IO_write_ptr = '\0'; + ++ _IO_wstrfile_fclose_stack ((FILE *) &sf.f._sbf); + return ret; + } + diff --git a/SOURCES/glibc-RHEL-145156-3.patch b/SOURCES/glibc-RHEL-145156-3.patch new file mode 100644 index 0000000..78b117e --- /dev/null +++ b/SOURCES/glibc-RHEL-145156-3.patch @@ -0,0 +1,123 @@ +commit 1cc165baed20b6589ef2f2c8c0e0415139bbb151 +Author: Frédéric Bérat +Date: Wed Apr 29 13:26:38 2026 +0200 + + test: Add gconv refcount leak test for swscanf + + Add a new internal test, `tst-wcsmbs-clone-overflow`, to verify correct + gconv module reference counting. The Makefile is updated to include this + test in the `tests-internal` list and ensure it runs with generated locales. + + This test specifically checks that the `__counter` for `gconv_fcts->towc` + does not leak references when `swscanf` is used with a stack-allocated + wide character stream. It ensures that `_IO_wstrfile_fclose_stack` + properly decrements the module reference counter, preventing a module + from staying loaded indefinitely due to unreleased references. + + Assisted-by: LLM + Reviewed-by: Adhemerval Zanella + +Conflicts: + wcsmbs/Makefile + (test-c8rtomb, test-mbrtoc8, tst-wscanf-to_inpunct not present + downstream) + wcsmbs/tst-wcsmbs-clone-overflow.c + (struct lc_ctype_data not present downstream; use + loc->private.ctype directly instead) + +diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile +index 0e5fe909a83c7a13..f74b06d9749eaa02 100644 +--- a/wcsmbs/Makefile ++++ b/wcsmbs/Makefile +@@ -59,6 +59,12 @@ tests := tst-wcstof wcsmbs-tst1 tst-wcsnlen tst-btowc tst-mbrtowc \ + # This test runs for a long time. + xtests += test-wcsncmp-nonarray + ++tests-internal += \ ++ tst-wcsmbs-clone-overflow ++ ++tests-static += \ ++ tst-wcsmbs-clone-overflow ++ + + include ../Rules + +@@ -77,6 +83,7 @@ $(objpfx)tst-wcstol-locale.out: $(gen-locales) + $(objpfx)tst-wcstod-nan-locale.out: $(gen-locales) + $(objpfx)tst-c16-surrogate.out: $(gen-locales) + $(objpfx)tst-c32-state.out: $(gen-locales) ++$(objpfx)tst-wcsmbs-clone-overflow.out: $(gen-locales) + endif + + $(objpfx)tst-wcstod-round: $(libm) +diff --git a/wcsmbs/tst-wcsmbs-clone-overflow.c b/wcsmbs/tst-wcsmbs-clone-overflow.c +new file mode 100644 +index 0000000000000000..412e1c54085c68d5 +--- /dev/null ++++ b/wcsmbs/tst-wcsmbs-clone-overflow.c +@@ -0,0 +1,65 @@ ++/* Test for gconv module reference counter leak. ++ 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 ++ ++/* Internal headers for accessing the gconv structures. */ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ if (setlocale (LC_ALL, "de_DE.ISO-8859-1") == NULL) ++ FAIL_EXIT1 ("setlocale failed, check if de_DE.ISO-8859-1 is generated"); ++ ++ wchar_t buf[32] = L"123"; ++ int j; ++ ++ /* First iteration initializes the gconv functions internally. */ ++ if (swscanf (buf, L"%d", &j) < 1) ++ FAIL_EXIT1 ("swscanf failed"); ++ ++ /* Retrieve the current gconv_fcts from the LC_CTYPE locale data. */ ++ struct __locale_data *loc = _NL_CURRENT_DATA (LC_CTYPE); ++ const struct gconv_fcts *fcts = loc->private.ctype; ++ ++ TEST_VERIFY_EXIT (fcts != NULL); ++ TEST_VERIFY_EXIT (fcts->towc != NULL); ++ ++ /* Capture the reference counter. */ ++ int initial_counter = fcts->towc->__counter; ++ ++ /* Perform a second iteration of swscanf. If the stack-allocated FILE ++ leaks the gconv reference, the counter will increment. */ ++ if (swscanf (buf, L"%d", &j) < 1) ++ FAIL_EXIT1 ("swscanf failed"); ++ ++ /* The counter should be unchanged, as _IO_wstrfile_fclose_stack should ++ have decremented it correctly. */ ++ TEST_COMPARE (fcts->towc->__counter, initial_counter); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-145156-4.patch b/SOURCES/glibc-RHEL-145156-4.patch new file mode 100644 index 0000000..62dec63 --- /dev/null +++ b/SOURCES/glibc-RHEL-145156-4.patch @@ -0,0 +1,146 @@ +commit 9ef37798fa7dc2b10926742e3f3bd304a17a9ad1 +Author: Frédéric Bérat +Date: Tue May 26 13:29:57 2026 +0200 + + test: Fix and stabilize tst-wcsmbs-clone-overflow test + + The test tst-wcsmbs-clone-overflow was initially added to tests-static. + However, this causes the test to be unstable because gconv modules + dynamically load libc.so. Any discrepancy between the statically linked + version and the dynamically loaded one can lead to a crash. + + By removing the test from tests-static, it relies on dynamic linking, + safely bypassing the dlopen crash. Since the test is now dynamically + linked, it cannot use the internal thread-local symbol + _NL_CURRENT_DATA(LC_CTYPE) because _nl_current_LC_CTYPE is hidden in + libc.so, leading to undefined references. Thus, the test now uses + newlocale and uselocale, safely extracting the locale data from the + returned locale_t object. + + Furthermore, using newlocale requires the gconv-modules configuration to + be built and available so that the ISO8859-1.so module can be + dynamically loaded. Otherwise, glibc falls back to the built-in C locale + conversions, leaving __shlib_handle as NULL and silently bypassing the + reference counter increment. + A new Makefile fragment, gen-gconv-modules.mk, is introduced to ensure + the gconv-modules are built before the test runs, and an explicit check + for __shlib_handle != NULL is added to the test. + + Reviewed-by: Carlos O'Donell + +Conflicts: + wcsmbs/Makefile + (test-c8rtomb, test-mbrtoc8, tst-wscanf-to_inpunct not present + downstream) + wcsmbs/tst-wcsmbs-clone-overflow.c + (struct lc_ctype_data not present downstream; use + loc->private.ctype directly instead) + +diff --git a/gen-gconv-modules.mk b/gen-gconv-modules.mk +new file mode 100644 +index 0000000000000000..046721a7a7bf460c +--- /dev/null ++++ b/gen-gconv-modules.mk +@@ -0,0 +1,6 @@ ++# defines target $(gen-gconv-modules) that ensures gconv-modules are available ++ ++gen-gconv-modules := $(common-objpfx)iconvdata/gconv-modules ++ ++$(gen-gconv-modules): ++ $(MAKE) -C ../iconvdata subdir=iconvdata $@ +diff --git a/localedata/Makefile b/localedata/Makefile +index 56de42e9262decd3..0cc02c430d96d804 100644 +--- a/localedata/Makefile ++++ b/localedata/Makefile +@@ -204,7 +204,7 @@ install-others := $(addprefix $(inst_i18ndir)/, \ + $(locales)) + endif + +-tests: $(objdir)/iconvdata/gconv-modules ++tests: $(gen-gconv-modules) + + tests-static += tst-langinfo-newlocale-static tst-langinfo-setlocale-static + +@@ -315,6 +315,7 @@ LOCALES := \ + $(NULL) + + include ../gen-locales.mk ++include ../gen-gconv-modules.mk + + $(objpfx)tst-iconv-math-trans.out: $(gen-locales) + endif +@@ -486,6 +487,3 @@ $(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out + + bug-setlocale1-ENV-only = LOCPATH=$(objpfx) LC_CTYPE=de_DE.UTF-8 + bug-setlocale1-static-ENV-only = $(bug-setlocale1-ENV-only) +- +-$(objdir)/iconvdata/gconv-modules: +- $(MAKE) -C ../iconvdata subdir=iconvdata $@ +diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile +index f74b06d9749eaa02..bb03d92576700a99 100644 +--- a/wcsmbs/Makefile ++++ b/wcsmbs/Makefile +@@ -62,16 +62,13 @@ xtests += test-wcsncmp-nonarray + tests-internal += \ + tst-wcsmbs-clone-overflow + +-tests-static += \ +- tst-wcsmbs-clone-overflow +- +- + include ../Rules + + ifeq ($(run-built-tests),yes) + LOCALES := de_DE.ISO-8859-1 de_DE.UTF-8 en_US.ANSI_X3.4-1968 hr_HR.ISO-8859-2 \ + ja_JP.EUC-JP zh_TW.EUC-TW tr_TR.UTF-8 tr_TR.ISO-8859-9 + include ../gen-locales.mk ++include ../gen-gconv-modules.mk + + $(objpfx)tst-btowc.out: $(gen-locales) + $(objpfx)tst-c16c32-1.out: $(gen-locales) +@@ -83,7 +80,7 @@ $(objpfx)tst-wcstol-locale.out: $(gen-locales) + $(objpfx)tst-wcstod-nan-locale.out: $(gen-locales) + $(objpfx)tst-c16-surrogate.out: $(gen-locales) + $(objpfx)tst-c32-state.out: $(gen-locales) +-$(objpfx)tst-wcsmbs-clone-overflow.out: $(gen-locales) ++$(objpfx)tst-wcsmbs-clone-overflow.out: $(gen-locales) $(gen-gconv-modules) + endif + + $(objpfx)tst-wcstod-round: $(libm) +diff --git a/wcsmbs/tst-wcsmbs-clone-overflow.c b/wcsmbs/tst-wcsmbs-clone-overflow.c +index 412e1c54085c68d5..6d8b643f48aa7775 100644 +--- a/wcsmbs/tst-wcsmbs-clone-overflow.c ++++ b/wcsmbs/tst-wcsmbs-clone-overflow.c +@@ -30,8 +30,11 @@ + static int + do_test (void) + { +- if (setlocale (LC_ALL, "de_DE.ISO-8859-1") == NULL) +- FAIL_EXIT1 ("setlocale failed, check if de_DE.ISO-8859-1 is generated"); ++ locale_t loc_obj = newlocale (LC_ALL_MASK, "de_DE.ISO-8859-1", NULL); ++ if (loc_obj == NULL) ++ FAIL_EXIT1 ("newlocale failed, check if de_DE.ISO-8859-1 is generated"); ++ ++ uselocale (loc_obj); + + wchar_t buf[32] = L"123"; + int j; +@@ -41,7 +44,7 @@ do_test (void) + FAIL_EXIT1 ("swscanf failed"); + + /* Retrieve the current gconv_fcts from the LC_CTYPE locale data. */ +- struct __locale_data *loc = _NL_CURRENT_DATA (LC_CTYPE); ++ struct __locale_data *loc = loc_obj->__locales[LC_CTYPE]; + const struct gconv_fcts *fcts = loc->private.ctype; + + TEST_VERIFY_EXIT (fcts != NULL); +@@ -50,6 +53,9 @@ do_test (void) + /* Capture the reference counter. */ + int initial_counter = fcts->towc->__counter; + ++ if (fcts->towc->__shlib_handle == NULL) ++ FAIL_EXIT1 ("__shlib_handle is NULL!"); ++ + /* Perform a second iteration of swscanf. If the stack-allocated FILE + leaks the gconv reference, the counter will increment. */ + if (swscanf (buf, L"%d", &j) < 1) diff --git a/SOURCES/glibc-RHEL-145156-5.patch b/SOURCES/glibc-RHEL-145156-5.patch new file mode 100644 index 0000000..41d91fd --- /dev/null +++ b/SOURCES/glibc-RHEL-145156-5.patch @@ -0,0 +1,111 @@ +test: Add gconv refcount leak test for swprintf + +Add a new internal test, tst-wcsmbs-clone-overflow-swprintf, to verify +correct gconv module reference counting for the swprintf path. + +This is the swprintf counterpart to tst-wcsmbs-clone-overflow (which +tests swscanf). The vswprintf/swprintf family creates a wide-oriented +stack-allocated FILE stream via _IO_no_init and _IO_fwide, which clones +the gconv transformation steps. This test verifies that the __counter +for gconv_fcts->towc does not leak references after swprintf returns, +confirming that _IO_wstrfile_fclose_stack properly releases the gconv +steps. + +diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile +index bb03d92576700a99..252630457146de35 100644 +--- a/wcsmbs/Makefile ++++ b/wcsmbs/Makefile +@@ -60,7 +60,8 @@ tests := tst-wcstof wcsmbs-tst1 tst-wcsnlen tst-btowc tst-mbrtowc \ + xtests += test-wcsncmp-nonarray + + tests-internal += \ +- tst-wcsmbs-clone-overflow ++ tst-wcsmbs-clone-overflow \ ++ tst-wcsmbs-clone-overflow-swprintf + + include ../Rules + +@@ -81,6 +82,7 @@ $(objpfx)tst-wcstod-nan-locale.out: $(gen-locales) + $(objpfx)tst-c16-surrogate.out: $(gen-locales) + $(objpfx)tst-c32-state.out: $(gen-locales) + $(objpfx)tst-wcsmbs-clone-overflow.out: $(gen-locales) $(gen-gconv-modules) ++$(objpfx)tst-wcsmbs-clone-overflow-swprintf.out: $(gen-locales) $(gen-gconv-modules) + endif + + $(objpfx)tst-wcstod-round: $(libm) +diff --git a/wcsmbs/tst-wcsmbs-clone-overflow-swprintf.c b/wcsmbs/tst-wcsmbs-clone-overflow-swprintf.c +new file mode 100644 +index 0000000000000000..daccb08e6352a943 +--- /dev/null ++++ b/wcsmbs/tst-wcsmbs-clone-overflow-swprintf.c +@@ -0,0 +1,70 @@ ++/* Test for gconv module reference counter leak in swprintf. ++ 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 ++ ++/* Internal headers for accessing the gconv structures. */ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ locale_t loc_obj = newlocale (LC_ALL_MASK, "de_DE.ISO-8859-1", NULL); ++ if (loc_obj == NULL) ++ FAIL_EXIT1 ("newlocale failed, check if de_DE.ISO-8859-1 is generated"); ++ ++ uselocale (loc_obj); ++ ++ wchar_t buf[32]; ++ ++ /* First iteration initializes the gconv functions internally. */ ++ if (swprintf (buf, sizeof (buf) / sizeof (buf[0]), L"%d", 123) < 0) ++ FAIL_EXIT1 ("swprintf failed"); ++ ++ /* Retrieve the current gconv_fcts from the LC_CTYPE locale data. */ ++ struct __locale_data *loc = loc_obj->__locales[LC_CTYPE]; ++ const struct gconv_fcts *fcts = loc->private.ctype; ++ ++ TEST_VERIFY_EXIT (fcts != NULL); ++ TEST_VERIFY_EXIT (fcts->towc != NULL); ++ ++ /* Capture the reference counter. */ ++ int initial_counter = fcts->towc->__counter; ++ ++ if (fcts->towc->__shlib_handle == NULL) ++ FAIL_EXIT1 ("__shlib_handle is NULL!"); ++ ++ /* Perform a second iteration of swprintf. If the stack-allocated FILE ++ leaks the gconv reference, the counter will increment. */ ++ if (swprintf (buf, sizeof (buf) / sizeof (buf[0]), L"%d", 456) < 0) ++ FAIL_EXIT1 ("swprintf failed"); ++ ++ /* The counter should be unchanged, as _IO_wstrfile_fclose_stack should ++ have decremented it correctly. */ ++ TEST_COMPARE (fcts->towc->__counter, initial_counter); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-150269-1.patch b/SOURCES/glibc-RHEL-150269-1.patch new file mode 100644 index 0000000..0eda8a7 --- /dev/null +++ b/SOURCES/glibc-RHEL-150269-1.patch @@ -0,0 +1,25 @@ +commit 0d83b349fa7340475406b2fe933c7467e4584091 +Author: Andreas Schwab +Date: Wed Mar 15 11:44:24 2023 +0100 + + getlogin_r: fix missing fallback if loginuid is unset (bug 30235) + + When /proc/self/loginuid is not set, we should still fall back to using + the traditional utmp lookup, instead of failing right away. + +diff --git a/sysdeps/unix/sysv/linux/getlogin_r.c b/sysdeps/unix/sysv/linux/getlogin_r.c +index dc0ca388b20d42c3..ff60fbbc726d7e7a 100644 +--- a/sysdeps/unix/sysv/linux/getlogin_r.c ++++ b/sysdeps/unix/sysv/linux/getlogin_r.c +@@ -59,10 +59,7 @@ __getlogin_r_loginuid (char *name, size_t namesize) + value of, (uid_t) -1, so check if that value is set and return early to + avoid making unneeded nss lookups. */ + if (uid == (uid_t) -1) +- { +- __set_errno (ENXIO); +- return ENXIO; +- } ++ return -1; + + struct passwd pwd; + struct passwd *tpwd; diff --git a/SOURCES/glibc-RHEL-150269-2.patch b/SOURCES/glibc-RHEL-150269-2.patch new file mode 100644 index 0000000..3925565 --- /dev/null +++ b/SOURCES/glibc-RHEL-150269-2.patch @@ -0,0 +1,112 @@ +commit 7bb859f4198d0be19c31a9937eae4f6c2c9a079e +Author: Florian Weimer +Date: Fri Feb 13 09:02:07 2026 +0100 + + nss: Introduce dedicated struct nss_database_for_fork type + + The initialized field in struct nss_database_data is rather confusing + because it is not used by the regular NSS code, only by the fork + state synchronization code. Introduce a separate type and place + the initialized field there. + + Reviewed-by: Sam James + +diff --git a/nss/nss_database.c b/nss/nss_database.c +index e807e9d84ca03680..293e8457d70c00ba 100644 +--- a/nss/nss_database.c ++++ b/nss/nss_database.c +@@ -56,7 +56,6 @@ global_state_allocate (void *closure) + { + result->data.nsswitch_conf.size = -1; /* Force reload. */ + memset (result->data.services, 0, sizeof (result->data.services)); +- result->data.initialized = true; + result->data.reload_disabled = false; + __libc_lock_init (result->lock); + result->root_ino = 0; +@@ -451,8 +450,8 @@ nss_database_check_reload_and_get (struct nss_database_state *local, + /* Avoid overwriting the global configuration until we have loaded + everything successfully. Otherwise, if the file change + information changes back to what is in the global configuration, +- the lookups would use the partially-written configuration. */ +- struct nss_database_data staging = { .initialized = true, }; ++ the lookups would use the partially-written configuration. */ ++ struct nss_database_data staging = { }; + + bool ok = nss_database_reload (&staging, &initial); + +@@ -503,7 +502,7 @@ __nss_database_freeres (void) + } + + void +-__nss_database_fork_prepare_parent (struct nss_database_data *data) ++__nss_database_fork_prepare_parent (struct nss_database_for_fork *data) + { + /* Do not use allocate_once to trigger loading unnecessarily. */ + struct nss_database_state *local = atomic_load_acquire (&global_database_state); +@@ -515,20 +514,21 @@ __nss_database_fork_prepare_parent (struct nss_database_data *data) + because it avoids acquiring the lock during the actual + fork. */ + __libc_lock_lock (local->lock); +- *data = local->data; ++ data->data = local->data; + __libc_lock_unlock (local->lock); ++ data->initialized = true; + } + } + + void +-__nss_database_fork_subprocess (struct nss_database_data *data) ++__nss_database_fork_subprocess (struct nss_database_for_fork *data) + { + struct nss_database_state *local = atomic_load_acquire (&global_database_state); + if (data->initialized) + { + /* Restore the state at the point of the fork. */ + assert (local != NULL); +- local->data = *data; ++ local->data = data->data; + __libc_lock_init (local->lock); + } + else if (local != NULL) +diff --git a/nss/nss_database.h b/nss/nss_database.h +index d4b23b5295110c1c..349a793a6ef011e4 100644 +--- a/nss/nss_database.h ++++ b/nss/nss_database.h +@@ -73,15 +73,21 @@ struct nss_database_data + struct file_change_detection nsswitch_conf; + nss_action_list services[NSS_DATABASE_COUNT]; + int reload_disabled; /* Actually bool; int for atomic access. */ +- bool initialized; ++}; ++ ++/* Use to store a consistent state snapshot across fork. */ ++struct nss_database_for_fork ++{ ++ bool initialized; /* Set to true if the data field below is initialized. */ ++ struct nss_database_data data; + }; + + /* Called by fork in the parent process, before forking. */ +-void __nss_database_fork_prepare_parent (struct nss_database_data *data) ++void __nss_database_fork_prepare_parent (struct nss_database_for_fork *) + attribute_hidden; + + /* Called by fork in the new subprocess, after forking. */ +-void __nss_database_fork_subprocess (struct nss_database_data *data) ++void __nss_database_fork_subprocess (struct nss_database_for_fork *) + attribute_hidden; + + #endif /* _NSS_DATABASE_H */ +diff --git a/posix/fork.c b/posix/fork.c +index 5a5baf63aa971b43..b996f4c63acc4e4b 100644 +--- a/posix/fork.c ++++ b/posix/fork.c +@@ -50,7 +50,7 @@ __libc_fork (void) + + lastrun = __run_prefork_handlers (multiple_threads); + +- struct nss_database_data nss_database_data; ++ struct nss_database_for_fork nss_database_data; + + /* If we are not running multiple threads, we do not have to + preserve lock state. If fork runs from a signal handler, only diff --git a/SOURCES/glibc-RHEL-150269-3.patch b/SOURCES/glibc-RHEL-150269-3.patch new file mode 100644 index 0000000..7c99f3d --- /dev/null +++ b/SOURCES/glibc-RHEL-150269-3.patch @@ -0,0 +1,71 @@ +commit 28660f4b45afa8921c2faebaec2846f95f670ba0 +Author: Florian Weimer +Date: Fri Feb 13 09:02:07 2026 +0100 + + Linux: In getlogin_r, use utmp fallback only for specific errors + + Most importantly, if getwpuid_r fails, it does not make sense to retry + via utmp because the user ID obtained from there is less reliable than + the one from /proc/self/loginuid. + + Reviewed-by: Sam James + +diff --git a/sysdeps/unix/sysv/linux/getlogin_r.c b/sysdeps/unix/sysv/linux/getlogin_r.c +index ff60fbbc726d7e7a..85e54b3879ff986a 100644 +--- a/sysdeps/unix/sysv/linux/getlogin_r.c ++++ b/sysdeps/unix/sysv/linux/getlogin_r.c +@@ -37,7 +37,12 @@ __getlogin_r_loginuid (char *name, size_t namesize) + { + int fd = __open_nocancel ("/proc/self/loginuid", O_RDONLY); + if (fd == -1) +- return -1; ++ { ++ if (errno == ENOENT) ++ /* Trigger utmp fallback. */ ++ return -1; ++ return errno; ++ } + + /* We are reading a 32-bit number. 12 bytes are enough for the text + representation. If not, something is wrong. */ +@@ -45,6 +50,8 @@ __getlogin_r_loginuid (char *name, size_t namesize) + ssize_t n = TEMP_FAILURE_RETRY (__read_nocancel (fd, uidbuf, + sizeof (uidbuf))); + __close_nocancel_nostatus (fd); ++ if (n < 0) ++ return errno; + + uid_t uid; + char *endp; +@@ -53,12 +60,13 @@ __getlogin_r_loginuid (char *name, size_t namesize) + || (uidbuf[n] = '\0', + uid = strtoul (uidbuf, &endp, 10), + endp == uidbuf || *endp != '\0')) +- return -1; ++ return EINVAL; + + /* If there is no login uid, linux sets /proc/self/loginid to the sentinel + value of, (uid_t) -1, so check if that value is set and return early to + avoid making unneeded nss lookups. */ + if (uid == (uid_t) -1) ++ /* Trigger utmp fallback. */ + return -1; + + struct passwd pwd; +@@ -78,9 +86,14 @@ __getlogin_r_loginuid (char *name, size_t namesize) + } + } + +- if (res != 0 || tpwd == NULL) ++ if (res != 0) ++ { ++ result = res; ++ goto out; ++ } ++ if (tpwd == NULL) + { +- result = -1; ++ result = ENOENT; + goto out; + } + diff --git a/SOURCES/glibc-RHEL-150269-4.patch b/SOURCES/glibc-RHEL-150269-4.patch new file mode 100644 index 0000000..f811d42 --- /dev/null +++ b/SOURCES/glibc-RHEL-150269-4.patch @@ -0,0 +1,405 @@ +commit 5b713b49443eb6a4e54e50e2f0147105f86dab02 +Author: Florian Weimer +Date: Fri Feb 13 09:02:07 2026 +0100 + + nss: Missing checks in __nss_configure_lookup, __nss_database_get (bug 28940) + + This avoids a null pointer dereference in the + nss_database_check_reload_and_get function, and assertion failures. + + Reviewed-by: Sam James + +Conflicts: + nss/Makefile + (fixup context) + +diff --git a/nss/Makefile b/nss/Makefile +index dbdba5bf815136ef..4d128da0a366301f 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -62,6 +62,7 @@ tests := \ + test-digits-dots \ + test-netdb \ + tst-nss-getpwent \ ++ tst-nss-malloc-failure-getlogin_r \ + tst-nss-test1 \ + tst-nss-test2 \ + tst-nss-test4 \ +diff --git a/nss/nss_database.c b/nss/nss_database.c +index 293e8457d70c00ba..251e47fdcf1ecf54 100644 +--- a/nss/nss_database.c ++++ b/nss/nss_database.c +@@ -250,9 +250,12 @@ __nss_configure_lookup (const char *dbname, const char *service_line) + + /* Force any load/cache/read whatever to happen, so we can override + it. */ +- __nss_database_get (db, &result); ++ if (!__nss_database_get (db, &result)) ++ return -1; + + local = nss_database_state_get (); ++ if (local == NULL) ++ return -1; + + result = __nss_action_parse (service_line); + if (result == NULL) +@@ -477,6 +480,8 @@ bool + __nss_database_get (enum nss_database db, nss_action_list *actions) + { + struct nss_database_state *local = nss_database_state_get (); ++ if (local == NULL) ++ return false; + return nss_database_check_reload_and_get (local, actions, db); + } + libc_hidden_def (__nss_database_get) +diff --git a/nss/tst-nss-malloc-failure-getlogin_r.c b/nss/tst-nss-malloc-failure-getlogin_r.c +new file mode 100644 +index 0000000000000000..0e2985ad57b07fce +--- /dev/null ++++ b/nss/tst-nss-malloc-failure-getlogin_r.c +@@ -0,0 +1,345 @@ ++/* Test NSS/getlogin_r with injected allocation failures (bug 28940). ++ 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 ++#include ++ ++/* This test calls getpwuid_r via getlogin_r (on Linux). ++ ++ This test uses the NSS system configuration to exercise that code ++ path. It means that it can fail (crash) if malloc failure is not ++ handled by NSS modules for the passwd database. */ ++ ++/* Data structure allocated via MAP_SHARED, so that writes from the ++ subprocess are visible. */ ++struct shared_data ++{ ++ /* Number of tracked allocations performed so far. */ ++ volatile unsigned int allocation_count; ++ ++ /* If this number is reached, one allocation fails. */ ++ volatile unsigned int failing_allocation; ++ ++ /* The number of allocations performed during initialization ++ (before the actual getlogin_r call). */ ++ volatile unsigned int init_allocation_count; ++ ++ /* Error code of an expected getlogin_r failure. */ ++ volatile int expected_failure; ++ ++ /* The subprocess stores the expected name here. */ ++ char name[100]; ++}; ++ ++/* Allocation count in shared mapping. */ ++static struct shared_data *shared; ++ ++/* Returns true if a failure should be injected for this allocation. */ ++static bool ++fail_this_allocation (void) ++{ ++ if (shared != NULL) ++ { ++ unsigned int count = shared->allocation_count; ++ shared->allocation_count = count + 1; ++ return count == shared->failing_allocation; ++ } ++ else ++ return false; ++} ++ ++/* Failure-injecting wrappers for allocation functions used by glibc. */ ++ ++void * ++malloc (size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (malloc) __libc_malloc; ++ return __libc_malloc (size); ++} ++ ++void * ++calloc (size_t a, size_t b) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (calloc) __libc_calloc; ++ return __libc_calloc (a, b); ++} ++ ++void * ++realloc (void *ptr, size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (realloc) __libc_realloc; ++ return __libc_realloc (ptr, size); ++} ++ ++/* No-op subprocess to verify that support_isolate_in_subprocess does ++ not perform any heap allocations. */ ++static void ++no_op (void *ignored) ++{ ++} ++ ++/* Perform a getlogin_r call in a subprocess, to obtain the number of ++ allocations used and the expected result of a successful call. */ ++static void ++initialize (void *configure_lookup) ++{ ++ shared->init_allocation_count = 0; ++ if (configure_lookup != NULL) ++ { ++ TEST_COMPARE (__nss_configure_lookup ("passwd", configure_lookup), 0); ++ shared->init_allocation_count = shared->allocation_count; ++ } ++ ++ shared->name[0] = '\0'; ++ int ret = getlogin_r (shared->name, sizeof (shared->name)); ++ if (ret != 0) ++ { ++ printf ("info: getlogin_r failed: %s (%d)\n", ++ strerrorname_np (ret), ret); ++ shared->expected_failure = ret; ++ } ++ else ++ { ++ shared->expected_failure = 0; ++ if (shared->name[0] == '\0') ++ FAIL ("error: getlogin_r succeeded without result\n"); ++ else ++ printf ("info: getlogin_r: \"%s\"\n", shared->name); ++ } ++} ++ ++/* Perform getlogin_r in a subprocess with fault injection. */ ++static void ++test_in_subprocess (void *configure_lookup) ++{ ++ if (configure_lookup != NULL ++ && __nss_configure_lookup ("passwd", configure_lookup) < 0) ++ { ++ printf ("info: __nss_configure_lookup failed: %s (%d)\n", ++ strerrorname_np (errno), errno); ++ TEST_COMPARE (errno, ENOMEM); ++ TEST_VERIFY (shared->allocation_count <= shared->init_allocation_count); ++ return; ++ } ++ ++ unsigned int inject_at = shared->failing_allocation; ++ char name[sizeof (shared->name)] = "name not set"; ++ int ret = getlogin_r (name, sizeof (name)); ++ shared->failing_allocation = ~0U; ++ ++ if (ret == 0) ++ { ++ TEST_COMPARE (shared->expected_failure, 0); ++ TEST_COMPARE_STRING (name, shared->name); ++ } ++ else ++ { ++ printf ("info: allocation %u failure results in error %s (%d)\n", ++ inject_at, strerrorname_np (ret), ret); ++ ++ if (ret != ENOMEM) ++ { ++ if (shared->expected_failure != 0) ++ TEST_COMPARE (ret, shared->expected_failure); ++ else if (configure_lookup == NULL) ++ /* The ENOENT failure can happen due to an issue related ++ to bug 22041: dlopen failure does not result in ENOMEM. */ ++ TEST_COMPARE (ret, ENOENT); ++ else ++ FAIL ("unexpected getlogin_r error"); ++ } ++ } ++ ++ if (shared->expected_failure == 0) ++ { ++ /* The second call should succeed. */ ++ puts ("info: about to perform second getlogin_r call"); ++ ret = getlogin_r (name, sizeof (name)); ++ if (configure_lookup == NULL) ++ { ++ /* This check can fail due to bug 22041 if the malloc error ++ injection causes a failure internally in dlopen. */ ++ if (ret != 0) ++ { ++ printf ("warning: second getlogin_r call failed with %s (%d)\n", ++ strerrorname_np (ret), ret); ++ TEST_COMPARE (ret, ENOENT); ++ } ++ } ++ else ++ /* If __nss_configure_lookup has been called, the error caching ++ bug does not happen because nss_files is built-in, and the ++ second getlogin_r is expected to succeed. */ ++ TEST_COMPARE (ret, 0); ++ if (ret == 0) ++ TEST_COMPARE_STRING (name, shared->name); ++ } ++} ++ ++/* Set by the --failing-allocation command line option. Together with ++ --direct, this can be used to trigger an allocation failure in the ++ original process, which may help with debugging. */ ++static int option_failing_allocation = -1; ++ ++/* Set by --override, to be used with --failing-allocation. Turns on ++ the __nss_configure_lookup call for passwd/files, which is disabled ++ by default. */ ++static int option_override = 0; ++ ++static int ++do_test (void) ++{ ++ char files[] = "files"; ++ ++ if (option_failing_allocation >= 0) ++ { ++ /* The test was invoked with --failing-allocation. Perform just ++ one test, using the original nsswitch.conf. This is a ++ condensed version of the probing/testing loop below. */ ++ printf ("info: testing with failing allocation %d\n", ++ option_failing_allocation); ++ shared = support_shared_allocate (sizeof (*shared)); ++ shared->failing_allocation = ~0U; ++ char *configure_lookup = option_override ? files : NULL; ++ support_isolate_in_subprocess (initialize, configure_lookup); ++ shared->allocation_count = 0; ++ shared->failing_allocation = option_failing_allocation; ++ test_in_subprocess (configure_lookup); /* No subprocess. */ ++ support_shared_free (shared); ++ shared = NULL; ++ return 0; ++ } ++ ++ bool any_success = false; ++ ++ for (int do_configure_lookup = 0; do_configure_lookup < 2; ++ ++do_configure_lookup) ++ { ++ if (do_configure_lookup) ++ puts ("info: testing with nsswitch.conf override"); ++ else ++ puts ("info: testing with original nsswitch.conf"); ++ ++ char *configure_lookup = do_configure_lookup ? files : NULL; ++ ++ shared = support_shared_allocate (sizeof (*shared)); ++ ++ /* Disable fault injection. */ ++ shared->failing_allocation = ~0U; ++ ++ support_isolate_in_subprocess (no_op, NULL); ++ TEST_COMPARE (shared->allocation_count, 0); ++ ++ support_isolate_in_subprocess (initialize, configure_lookup); ++ ++ if (shared->name[0] != '\0') ++ any_success = true; ++ ++ /* The number of allocations in the successful case. Once the ++ number of expected allocations is exceeded, injecting further ++ failures does not make a difference (assuming that the number ++ of malloc calls is deterministic). */ ++ unsigned int maximum_allocation_count = shared->allocation_count; ++ printf ("info: initial getlogin_r performed %u allocations\n", ++ maximum_allocation_count); ++ ++ for (unsigned int inject_at = 0; inject_at <= maximum_allocation_count; ++ ++inject_at) ++ { ++ printf ("info: running fault injection at allocation %u\n", ++ inject_at); ++ shared->allocation_count = 0; ++ shared->failing_allocation = inject_at; ++ support_isolate_in_subprocess (test_in_subprocess, configure_lookup); ++ } ++ ++ support_shared_free (shared); ++ shared = NULL; ++ } ++ ++ { ++ FILE *fp = fopen (_PATH_NSSWITCH_CONF, "r"); ++ if (fp == NULL) ++ printf ("info: no %s file\n", _PATH_NSSWITCH_CONF); ++ else ++ { ++ printf ("info: %s contents follows\n", _PATH_NSSWITCH_CONF); ++ int last_ch = '\n'; ++ while (true) ++ { ++ int ch = fgetc (fp); ++ if (ch == EOF) ++ break; ++ putchar (ch); ++ last_ch = ch; ++ } ++ if (last_ch != '\n') ++ putchar ('\n'); ++ printf ("(end of %s contents)\n", _PATH_NSSWITCH_CONF); ++ xfclose (fp); ++ } ++ } ++ ++ support_record_failure_barrier (); ++ ++ if (!any_success) ++ FAIL_UNSUPPORTED ("no successful getlogin_r calls"); ++ ++ return 0; ++} ++ ++static void ++cmdline_process (int c) ++{ ++ if (c == 'F') ++ option_failing_allocation = atoi (optarg); ++} ++ ++#define CMDLINE_OPTIONS \ ++ { "failing-allocation", required_argument, NULL, 'F' }, \ ++ { "override", no_argument, &option_override, 1 }, ++ ++#define CMDLINE_PROCESS cmdline_process ++ ++#include diff --git a/SOURCES/glibc-RHEL-153056-1.patch b/SOURCES/glibc-RHEL-153056-1.patch new file mode 100644 index 0000000..bf93cf7 --- /dev/null +++ b/SOURCES/glibc-RHEL-153056-1.patch @@ -0,0 +1,249 @@ +commit 8ca2fe7e96c0ccf04d32d7002d7a6d9edcb9f8ee +Author: Sergey Kolosov +Date: Fri Oct 10 17:15:27 2025 +0200 + + resolv: Add tests for getaddrinfo returning EAI_AGAIN [BZ #16849] + + This patch adds two tests that verify correct behavior of getaddrinfo + when DNS resolution fails with a temporary error. Both tests ensure + that getaddrinfo returns EAI_AGAIN in cases where no valid address can + be resolved due to network or resolver failure. + + * tst-getaddrinfo-eai-again.c + Runs inside the glibc test-container without any DNS server + configured. The test performs queries using AF_INET, AF_INET6, + and AF_UNSPEC and verifies that getaddrinfo returns EAI_AGAIN + when resolution fails. + + * tst-getaddrinfo-eai-again-timeout.c + Runs outside of the container but uses the resolv_test framework + to simulate network failures. The test covers two failure modes: + - No response from the server (resolv_response_drop) + - Zero-length reply from the server + In both cases, getaddrinfo is expected to return EAI_AGAIN. + + Reviewed-by: Florian Weimer + +diff --git a/resolv/Makefile b/resolv/Makefile +index f3f5c260d0b7471f..cb1ff182dfdd4572 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -80,7 +80,10 @@ routines_no_fortify += \ + # routines_no_fortify + + tests = tst-aton tst-leaks tst-inet_ntop +-tests-container = tst-leaks2 ++tests-container += \ ++ tst-getaddrinfo-eai-again \ ++ tst-leaks2 \ ++ # tests-container + + tests-internal += tst-inet_aton_exact + +@@ -136,6 +139,7 @@ tests-static += tst-ns_rr_cursor + # These tests need libdl. + ifeq (yes,$(build-shared)) + tests += \ ++ tst-getaddrinfo-eai-again-timeout \ + tst-resolv-ai_idn \ + tst-resolv-ai_idn-latin1 \ + tst-resolv-ai_idn-nolibidn2 \ +@@ -268,6 +272,8 @@ $(objpfx)mtrace-tst-resolv-res_ninit.out: $(objpfx)tst-resolv-res_ninit.out + + $(objpfx)tst-bug18665-tcp: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-bug18665: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-getaddrinfo-eai-again-timeout: \ ++ $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-ai_idn: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-ai_idn-latin1: \ + $(objpfx)libresolv.so $(shared-thread-library) +diff --git a/resolv/tst-getaddrinfo-eai-again-timeout.c b/resolv/tst-getaddrinfo-eai-again-timeout.c +new file mode 100644 +index 0000000000000000..ec4a6563b7b5ae51 +--- /dev/null ++++ b/resolv/tst-getaddrinfo-eai-again-timeout.c +@@ -0,0 +1,122 @@ ++/* Test for BZ #16849. Verify that getaddrinfo correctly returns ++ EAI_AGAIN when DNS resolution fails due to timeout or malformed ++ responses. ++ ++ This test uses two simulated failure modes: ++ - The DNS server does not respond at all (resolv_response_drop). ++ - The DNS server responds with a zero-length packet. ++ ++ Copyright (C) 2025 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 ++ ++/* Track whether the callbacks were actually invoked. */ ++static volatile bool response_called_drop = false; ++static volatile bool response_called_zero_len = false; ++ ++/* Simulate a DNS server that sends a zero-length response. */ ++static void ++response_zero_len (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ response_called_zero_len = true; ++ /* Do nothing — zero-length reply. */ ++} ++ ++/* Simulate a DNS server that drops the request. */ ++static void ++response_drop (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ response_called_drop = true; ++ resolv_response_drop (b); ++} ++ ++/* Query getaddrinfo for multiple families and expect EAI_AGAIN. */ ++static void ++query_host (const char *host_name) ++{ ++ int family[] = { AF_INET, AF_INET6, AF_UNSPEC }; ++ const char *family_names[] = { "AF_INET", "AF_INET6", "AF_UNSPEC" }; ++ ++ for (int i = 0; i < 3; i++) ++ { ++ struct addrinfo hints = ++ { ++ .ai_socktype = 0, ++ .ai_protocol = 0, ++ .ai_family = family[i], ++ .ai_flags = 0, ++ }; ++ struct addrinfo *result; ++ int res = getaddrinfo (host_name, NULL, &hints, &result); ++ if (res != EAI_AGAIN) ++ FAIL_EXIT1 ("getaddrinfo (%s, %s) returned %s, expected EAI_AGAIN", ++ host_name, family_names[i], gai_strerror (res)); ++ } ++} ++ ++/* Simulate DNS server dropping all queries. */ ++static void ++test_drop (void) ++{ ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response_drop, ++ }); ++ /* Reduce default timeout to make the test run faster. */ ++ _res.retrans = 1; ++ _res.retry = 1; ++ query_host ("site.example"); ++ resolv_test_end (aux); ++} ++ ++/* Simulate DNS server sending zero-length responses. */ ++static void ++test_zero_len_packet (void) ++{ ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response_zero_len, ++ }); ++ query_host ("site.example"); ++ resolv_test_end (aux); ++} ++ ++static int ++do_test (void) ++{ ++ test_drop (); ++ test_zero_len_packet (); ++ ++ if (!response_called_drop) ++ FAIL_EXIT1 ("response_drop callback was not called"); ++ if (!response_called_zero_len) ++ FAIL_EXIT1 ("response_zero_len callback was not called"); ++ return 0; ++} ++ ++#include +diff --git a/resolv/tst-getaddrinfo-eai-again.c b/resolv/tst-getaddrinfo-eai-again.c +new file mode 100644 +index 0000000000000000..21daa6c1682d1156 +--- /dev/null ++++ b/resolv/tst-getaddrinfo-eai-again.c +@@ -0,0 +1,56 @@ ++/* Test for BZ #16849. Verify that getaddrinfo correctly returns ++ EAI_AGAIN error code if DNS query fails due to a network failure. ++ ++ Copyright (C) 2025 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 ++ ++/* Query getaddrinfo with various address families and verify that ++ it returns EAI_AGAIN when DNS resolution fails. */ ++static void ++query_host (const char *host_name) ++{ ++ int family[] = { AF_INET, AF_INET6, AF_UNSPEC }; ++ const char *family_names[] = { "AF_INET", "AF_INET6", "AF_UNSPEC" }; ++ ++ for (int i = 0; i < 3; i++) ++ { ++ struct addrinfo hints = ++ { ++ .ai_socktype = 0, ++ .ai_protocol = 0, ++ .ai_family = family[i], ++ .ai_flags = 0, ++ }; ++ struct addrinfo *result; ++ int res = getaddrinfo (host_name, NULL, &hints, &result); ++ if (res != EAI_AGAIN) ++ FAIL_EXIT1 ("getaddrinfo (%s, %s) returned %s, expected EAI_AGAIN", ++ host_name, family_names[i], gai_strerror (res)); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ query_host ("site.example"); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-153056-2.patch b/SOURCES/glibc-RHEL-153056-2.patch new file mode 100644 index 0000000..df43536 --- /dev/null +++ b/SOURCES/glibc-RHEL-153056-2.patch @@ -0,0 +1,258 @@ +commit c995686e2cbe2a3ab2a11877a61c14a2e1fc35cb +Author: Florian Weimer +Date: Tue Mar 3 18:48:47 2026 +0100 + + support: no_override_resolv_conf_search flag for resolver test framework + + It is required to test "search ." in /etc/resolv.conf files. The + default is to override the search path isolate from unexpected + settings in the test execution environment. + + Reviewed-by: Carlos O'Donell + +Conflicts: + resolv/Makefile + (tst-resolv-invalid-cname dependencies were added + in a different place on the 2.34 upstream branch) + +diff --git a/resolv/Makefile b/resolv/Makefile +index cb1ff182dfdd4572..116ab6a1574eb1ff 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -83,6 +83,7 @@ tests = tst-aton tst-leaks tst-inet_ntop + tests-container += \ + tst-getaddrinfo-eai-again \ + tst-leaks2 \ ++ tst-resolv-no-search \ + # tests-container + + tests-internal += tst-inet_aton_exact +@@ -294,10 +295,11 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \ + $(shared-thread-library) +-$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library) +-$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \ + $(shared-thread-library) ++$(objpfx)tst-resolv-no-search: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library) +diff --git a/resolv/tst-resolv-no-search.c b/resolv/tst-resolv-no-search.c +new file mode 100644 +index 0000000000000000..29701d4772507507 +--- /dev/null ++++ b/resolv/tst-resolv-no-search.c +@@ -0,0 +1,174 @@ ++/* Test using "search ." in /etc/resolv.conf. ++ 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 ++ ++/* Check that plain res_init loads the configuration as expected. */ ++static void ++test_res_init (void *ignored) ++{ ++ res_init (); ++ TEST_COMPARE_STRING (_res.dnsrch[0], "."); ++ TEST_COMPARE_STRING (_res.dnsrch[1], NULL); ++} ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_VERIFY_EXIT (qclass == C_IN); ++ TEST_COMPARE (ctx->server_index, 0); ++ ++ if (strncmp (qname, "does-not-exist", strlen ("does-not-exist")) == 0) ++ { ++ resolv_response_init (b, (struct resolv_response_flags) ++ { .rcode = ns_r_nxdomain }); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ return; ++ } ++ ++ resolv_response_init (b, (struct resolv_response_flags) { }); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ resolv_response_open_record (b, qname, qclass, qtype, 0); ++ switch (qtype) ++ { ++ case T_A: ++ { ++ char ipv4[4] = {192, 0, 2, 17}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ break; ++ case T_AAAA: ++ { ++ char ipv6[16] ++ = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ break; ++ } ++ resolv_response_close_record (b); ++} ++ ++static void ++check_h (const char *name, int family, const char *expected) ++{ ++ if (family == AF_INET) ++ { ++ char *query = xasprintf ("gethostbyname (\"%s\")", name); ++ check_hostent (query, gethostbyname (name), expected); ++ free (query); ++ } ++ { ++ char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family); ++ check_hostent (query, gethostbyname2 (name, family), expected); ++ free (query); ++ } ++} ++ ++static void ++check_ai (const char *name, int family, const char *expected) ++{ ++ struct addrinfo hints = { .ai_family = family, .ai_socktype = SOCK_STREAM, }; ++ struct addrinfo *ai; ++ char *query = xasprintf ("%s:80 [%d]", name, hints.ai_family); ++ int ret = getaddrinfo (name, "80", &hints, &ai); ++ check_addrinfo (query, ai, ret, expected); ++ if (ret == 0) ++ freeaddrinfo (ai); ++ free (query); ++} ++ ++static int ++do_test (void) ++{ ++ support_isolate_in_subprocess (test_res_init, NULL); ++ ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response, ++ .no_override_resolv_conf_search = true, ++ }); ++ ++ check_h ("www.example", AF_INET, ++ "name: www.example\n" ++ "address: 192.0.2.17\n"); ++ check_h ("www.example", AF_INET6, ++ "name: www.example\n" ++ "address: 2001:db8::1\n"); ++ check_ai ("www.example", AF_UNSPEC, ++ "address: STREAM/TCP 192.0.2.17 80\n" ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ check_ai ("www.example", AF_INET, ++ "address: STREAM/TCP 192.0.2.17 80\n"); ++ check_ai ("www.example", AF_INET6, ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ check_h ("does-not-exist.example", AF_INET, ++ "error: HOST_NOT_FOUND\n"); ++ check_h ("does-not-exist.example", AF_INET6, ++ "error: HOST_NOT_FOUND\n"); ++ check_ai ("does-not-exist.example", AF_UNSPEC, ++ "error: Name or service not known\n"); ++ check_ai ("does-not-exist.example", AF_INET, ++ "error: Name or service not known\n"); ++ check_ai ("does-not-exist.example", AF_INET6, ++ "error: Name or service not known\n"); ++ ++ /* With trailing dot. */ ++ check_h ("www.example.", AF_INET, ++ "name: www.example\n" ++ "address: 192.0.2.17\n"); ++ check_h ("www.example.", AF_INET6, ++ "name: www.example\n" ++ "address: 2001:db8::1\n"); ++ check_ai ("www.example.", AF_UNSPEC, ++ "address: STREAM/TCP 192.0.2.17 80\n" ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ check_ai ("www.example.", AF_INET, ++ "address: STREAM/TCP 192.0.2.17 80\n"); ++ check_ai ("www.example.", AF_INET6, ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ check_h ("does-not-exist.example.", AF_INET, ++ "error: HOST_NOT_FOUND\n"); ++ check_h ("does-not-exist.example.", AF_INET6, ++ "error: HOST_NOT_FOUND\n"); ++ check_ai ("does-not-exist.example.", AF_UNSPEC, ++ "error: Name or service not known\n"); ++ check_ai ("does-not-exist.example.", AF_INET, ++ "error: Name or service not known\n"); ++ check_ai ("does-not-exist.example.", AF_INET6, ++ "error: Name or service not known\n"); ++ ++ resolv_test_end (aux); ++ ++ return 0; ++} ++ ++#include +diff --git a/resolv/tst-resolv-no-search.root/etc/resolv.conf b/resolv/tst-resolv-no-search.root/etc/resolv.conf +new file mode 100644 +index 0000000000000000..5ace648869a31edb +--- /dev/null ++++ b/resolv/tst-resolv-no-search.root/etc/resolv.conf +@@ -0,0 +1 @@ ++search . +diff --git a/support/resolv_test.c b/support/resolv_test.c +index 73d20572f3b843c7..7272334b78fc85f1 100644 +--- a/support/resolv_test.c ++++ b/support/resolv_test.c +@@ -1098,6 +1098,9 @@ resolv_test_init (void) + static void + set_search_path (struct resolv_redirect_config config) + { ++ if (config.no_override_resolv_conf_search) ++ return; ++ + memset (_res.defdname, 0, sizeof (_res.defdname)); + memset (_res.dnsrch, 0, sizeof (_res.dnsrch)); + +diff --git a/support/resolv_test.h b/support/resolv_test.h +index ddf967449e398710..e0a17ebf23c34880 100644 +--- a/support/resolv_test.h ++++ b/support/resolv_test.h +@@ -96,6 +96,9 @@ struct resolv_redirect_config + domain name as well. */ + const char *search[7]; + ++ /* If true, do not override the search path loaded from /etc/resolv.conf. */ ++ bool no_override_resolv_conf_search; ++ + /* Number of servers to activate in resolv. 0 means the default, + resolv_max_test_servers. */ + int nscount; diff --git a/SOURCES/glibc-RHEL-153056-3.patch b/SOURCES/glibc-RHEL-153056-3.patch new file mode 100644 index 0000000..ed5efd8 --- /dev/null +++ b/SOURCES/glibc-RHEL-153056-3.patch @@ -0,0 +1,98 @@ +commit dc9ca785a5fe2059a9b04ab336520d463d9a715b +Author: Carlos Peón Costa +Date: Tue Mar 3 18:48:47 2026 +0100 + + resolv: Avoid duplicate query if search list contains '.' (bug 33804) + + Co-authored-by: Florian Weimer + Signed-off-by: Florian Weimer + Reviewed-by: Carlos O'Donell + +diff --git a/resolv/res_query.c b/resolv/res_query.c +index 1d2c81737bc889c9..bae7b3c23d778a7d 100644 +--- a/resolv/res_query.c ++++ b/resolv/res_query.c +@@ -358,7 +358,7 @@ __res_context_search (struct resolv_context *ctx, + char tmp[NS_MAXDNAME]; + u_int dots; + int trailing_dot, ret, saved_herrno; +- int got_nodata = 0, got_servfail = 0, root_on_list = 0; ++ int got_nodata = 0, got_servfail = 0; + int tried_as_is = 0; + int searched = 0; + +@@ -437,8 +437,11 @@ __res_context_search (struct resolv_context *ctx, + domain. */ + if (dname[0] == '.') + dname++; +- if (dname[0] == '\0') +- root_on_list++; ++ if (dname[0] == '\0') { ++ if (tried_as_is) ++ continue; ++ tried_as_is++; ++ } + + ret = __res_context_querydomain + (ctx, name, dname, class, type, +@@ -510,7 +513,7 @@ __res_context_search (struct resolv_context *ctx, + * unless RES_NOTLDQUERY is set and there were no dots. + */ + if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) +- && !(tried_as_is || root_on_list)) { ++ && !tried_as_is) { + ret = __res_context_querydomain + (ctx, name, NULL, class, type, + answer, anslen, answerp, answerp2, nanswerp2, +diff --git a/resolv/tst-resolv-no-search.c b/resolv/tst-resolv-no-search.c +index 29701d4772507507..7d78d4044cf50e48 100644 +--- a/resolv/tst-resolv-no-search.c ++++ b/resolv/tst-resolv-no-search.c +@@ -27,6 +27,11 @@ + #include + #include + ++/* Used to check for duplicated queries (bug 33804). POSIX does not ++ explicitly say that socket calls (as used in the resolver tests) ++ provide synchronization. */ ++static _Atomic unsigned int query_count; ++ + /* Check that plain res_init loads the configuration as expected. */ + static void + test_res_init (void *ignored) +@@ -43,6 +48,7 @@ response (const struct resolv_response_context *ctx, + { + TEST_VERIFY_EXIT (qclass == C_IN); + TEST_COMPARE (ctx->server_index, 0); ++ ++query_count; + + if (strncmp (qname, "does-not-exist", strlen ("does-not-exist")) == 0) + { +@@ -82,12 +88,16 @@ check_h (const char *name, int family, const char *expected) + if (family == AF_INET) + { + char *query = xasprintf ("gethostbyname (\"%s\")", name); ++ query_count = 0; + check_hostent (query, gethostbyname (name), expected); ++ TEST_COMPARE (query_count, 1); + free (query); + } + { + char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family); ++ query_count = 0; + check_hostent (query, gethostbyname2 (name, family), expected); ++ TEST_COMPARE (query_count, 1); + free (query); + } + } +@@ -98,8 +108,10 @@ check_ai (const char *name, int family, const char *expected) + struct addrinfo hints = { .ai_family = family, .ai_socktype = SOCK_STREAM, }; + struct addrinfo *ai; + char *query = xasprintf ("%s:80 [%d]", name, hints.ai_family); ++ query_count = 0; + int ret = getaddrinfo (name, "80", &hints, &ai); + check_addrinfo (query, ai, ret, expected); ++ TEST_COMPARE (query_count, family == AF_UNSPEC ? 2 : 1); + if (ret == 0) + freeaddrinfo (ai); + free (query); diff --git a/SOURCES/glibc-RHEL-162901-1.patch b/SOURCES/glibc-RHEL-162901-1.patch new file mode 100644 index 0000000..39624a7 --- /dev/null +++ b/SOURCES/glibc-RHEL-162901-1.patch @@ -0,0 +1,369 @@ +commit 5729e0e9af590807df66a3db688008f9547bce9f +Author: Adhemerval Zanella +Date: Fri Feb 10 14:09:10 2023 -0300 + + iconv: Remove _STRING_ARCH_unaligned usage for get/set macros + + And use a packed structure instead. The compiler generates optimized + unaligned code if the architecture supports it. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Wilco Dijkstra + +diff --git a/iconv/gconv_int.h b/iconv/gconv_int.h +index 499bc6bfa54fe37d..360924cc0394d86b 100644 +--- a/iconv/gconv_int.h ++++ b/iconv/gconv_int.h +@@ -27,6 +27,34 @@ + + __BEGIN_DECLS + ++/* We have to provide support for machines which are not able to handled ++ unaligned memory accesses. Some of the character encodings have ++ representations with a fixed width of 2 or 4 bytes. */ ++#define get16(addr) \ ++({ \ ++ const struct { uint16_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r; \ ++}) ++#define get32(addr) \ ++({ \ ++ const struct { uint32_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r; \ ++}) ++ ++#define put16(addr, val) \ ++do { \ ++ struct { uint16_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r = val; \ ++} while (0) ++#define put32(addr, val) \ ++do { \ ++ struct { uint32_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r = val; \ ++} while (0) + + /* Structure for alias definition. Simply two strings. */ + struct gconv_alias +diff --git a/iconv/loop.c b/iconv/loop.c +index 7193e8f20104bf84..851caa9d42d45a47 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -58,75 +58,10 @@ + #include + #include + +-/* We have to provide support for machines which are not able to handled +- unaligned memory accesses. Some of the character encodings have +- representations with a fixed width of 2 or 4 bytes. But if we cannot +- access unaligned memory we still have to read byte-wise. */ + #undef FCTNAME2 + #if _STRING_ARCH_unaligned || !defined DEFINE_UNALIGNED +-/* We can handle unaligned memory access. */ +-# define get16(addr) *((const uint16_t *) (addr)) +-# define get32(addr) *((const uint32_t *) (addr)) +- +-/* We need no special support for writing values either. */ +-# define put16(addr, val) *((uint16_t *) (addr)) = (val) +-# define put32(addr, val) *((uint32_t *) (addr)) = (val) +- + # define FCTNAME2(name) name + #else +-/* Distinguish between big endian and little endian. */ +-# if __BYTE_ORDER == __LITTLE_ENDIAN +-# define get16(addr) \ +- (((const unsigned char *) (addr))[1] << 8 \ +- | ((const unsigned char *) (addr))[0]) +-# define get32(addr) \ +- (((((const unsigned char *) (addr))[3] << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[0]) +- +-# define put16(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- ((unsigned char *) (addr))[1] = __val >> 8; \ +- (void) 0; }) +-# define put32(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[3] = __val; \ +- (void) 0; }) +-# else +-# define get16(addr) \ +- (((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) +-# define get32(addr) \ +- (((((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[3]) +- +-# define put16(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[1] = __val; \ +- ((unsigned char *) (addr))[0] = __val >> 8; \ +- (void) 0; }) +-# define put32(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[3] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[0] = __val; \ +- (void) 0; }) +-# endif +- + # define FCTNAME2(name) name##_unaligned + #endif + #define FCTNAME(name) FCTNAME2(name) +@@ -352,10 +287,6 @@ FCTNAME (LOOPFCT) (struct __gconv_step *step, + #if !defined DEFINE_UNALIGNED && !_STRING_ARCH_unaligned \ + && MIN_NEEDED_INPUT != 1 && MAX_NEEDED_INPUT % MIN_NEEDED_INPUT == 0 \ + && MIN_NEEDED_OUTPUT != 1 && MAX_NEEDED_OUTPUT % MIN_NEEDED_OUTPUT == 0 +-# undef get16 +-# undef get32 +-# undef put16 +-# undef put32 + # undef unaligned + + # define DEFINE_UNALIGNED +@@ -537,8 +468,4 @@ gconv_btowc (struct __gconv_step *step, unsigned char c) + #undef LOOP_NEED_STATE + #undef LOOP_NEED_FLAGS + #undef LOOP_NEED_DATA +-#undef get16 +-#undef get32 +-#undef put16 +-#undef put32 + #undef unaligned +diff --git a/iconv/skeleton.c b/iconv/skeleton.c +index 0ddc16ad1d7e0184..6992701808079ec2 100644 +--- a/iconv/skeleton.c ++++ b/iconv/skeleton.c +@@ -205,73 +205,6 @@ + #endif + + +-/* Define macros which can access unaligned buffers. These macros are +- supposed to be used only in code outside the inner loops. For the inner +- loops we have other definitions which allow optimized access. */ +-#if _STRING_ARCH_unaligned +-/* We can handle unaligned memory access. */ +-# define get16u(addr) *((const uint16_t *) (addr)) +-# define get32u(addr) *((const uint32_t *) (addr)) +- +-/* We need no special support for writing values either. */ +-# define put16u(addr, val) *((uint16_t *) (addr)) = (val) +-# define put32u(addr, val) *((uint32_t *) (addr)) = (val) +-#else +-/* Distinguish between big endian and little endian. */ +-# if __BYTE_ORDER == __LITTLE_ENDIAN +-# define get16u(addr) \ +- (((const unsigned char *) (addr))[1] << 8 \ +- | ((const unsigned char *) (addr))[0]) +-# define get32u(addr) \ +- (((((const unsigned char *) (addr))[3] << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[0]) +- +-# define put16u(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- ((unsigned char *) (addr))[1] = __val >> 8; \ +- (void) 0; }) +-# define put32u(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[3] = __val; \ +- (void) 0; }) +-# else +-# define get16u(addr) \ +- (((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) +-# define get32u(addr) \ +- (((((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[3]) +- +-# define put16u(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[1] = __val; \ +- ((unsigned char *) (addr))[0] = __val >> 8; \ +- (void) 0; }) +-# define put32u(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[3] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[0] = __val; \ +- (void) 0; }) +-# endif +-#endif +- +- + /* For conversions from a fixed width character set to another fixed width + character set we can define RESET_INPUT_BUFFER in a very fast way. */ + #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE +diff --git a/iconvdata/iso-2022-jp-3.c b/iconvdata/iso-2022-jp-3.c +index 220d8d1cf174fdd7..97e735c755ab8093 100644 +--- a/iconvdata/iso-2022-jp-3.c ++++ b/iconvdata/iso-2022-jp-3.c +@@ -93,7 +93,7 @@ enum + if (__glibc_likely (outbuf + 4 <= outend)) \ + { \ + /* Write out the last character. */ \ +- put32u (outbuf, ch); \ ++ put32 (outbuf, ch); \ + outbuf += 4; \ + data->__statep->__count &= 7; \ + data->__statep->__count |= ASCII_set; \ +diff --git a/iconvdata/unicode.c b/iconvdata/unicode.c +index 525abe48b94118e5..ee19b2041c7d4fc5 100644 +--- a/iconvdata/unicode.c ++++ b/iconvdata/unicode.c +@@ -52,10 +52,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get16u (inptr) == BOM) \ ++ if (get16 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 2; \ +- else if (get16u (inptr) == BOM_OE) \ ++ else if (get16 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 2; \ +@@ -68,7 +68,7 @@ + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM); \ ++ put16 (outbuf, BOM); \ + outbuf += 2; \ + } \ + swap = data->__flags & __GCONV_SWAP; +diff --git a/iconvdata/utf-16.c b/iconvdata/utf-16.c +index 63cf43a09b4abf5d..87c59e9ecf1602ac 100644 +--- a/iconvdata/utf-16.c ++++ b/iconvdata/utf-16.c +@@ -56,10 +56,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get16u (inptr) == BOM) \ ++ if (get16 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 2; \ +- else if (get16u (inptr) == BOM_OE) \ ++ else if (get16 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 2; \ +@@ -71,7 +71,7 @@ + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM); \ ++ put16 (outbuf, BOM); \ + outbuf += 2; \ + } \ + } \ +diff --git a/iconvdata/utf-32.c b/iconvdata/utf-32.c +index 4fbd7bc18fa2d0c9..060f77230b0101fe 100644 +--- a/iconvdata/utf-32.c ++++ b/iconvdata/utf-32.c +@@ -52,10 +52,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get32u (inptr) == BOM) \ ++ if (get32 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 4; \ +- else if (get32u (inptr) == BOM_OE) \ ++ else if (get32 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 4; \ +@@ -69,7 +69,7 @@ + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM); \ ++ put32 (outbuf, BOM); \ + outbuf += 4; \ + } \ + else if (__builtin_expect (data->__invocation_counter == 0, 0) \ +diff --git a/sysdeps/s390/utf16-utf32-z9.c b/sysdeps/s390/utf16-utf32-z9.c +index 93895f9db8004f72..7d3de99f440a8ab4 100644 +--- a/sysdeps/s390/utf16-utf32-z9.c ++++ b/sysdeps/s390/utf16-utf32-z9.c +@@ -177,7 +177,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM_UTF16); \ ++ put16 (outbuf, BOM_UTF16); \ + outbuf += 2; \ + } \ + else \ +@@ -186,7 +186,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM_UTF32); \ ++ put32 (outbuf, BOM_UTF32); \ + outbuf += 4; \ + } \ + } +diff --git a/sysdeps/s390/utf8-utf16-z9.c b/sysdeps/s390/utf8-utf16-z9.c +index 1ff16b5bec4c10fd..615ebaac13a4c0a7 100644 +--- a/sysdeps/s390/utf8-utf16-z9.c ++++ b/sysdeps/s390/utf8-utf16-z9.c +@@ -217,7 +217,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM_UTF16); \ ++ put16 (outbuf, BOM_UTF16); \ + outbuf += 2; \ + } + +diff --git a/sysdeps/s390/utf8-utf32-z9.c b/sysdeps/s390/utf8-utf32-z9.c +index f1f4d3c89e6469c1..364558c0e2bed517 100644 +--- a/sysdeps/s390/utf8-utf32-z9.c ++++ b/sysdeps/s390/utf8-utf32-z9.c +@@ -217,7 +217,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM); \ ++ put32 (outbuf, BOM); \ + outbuf += 4; \ + } + diff --git a/SOURCES/glibc-RHEL-162901-2.patch b/SOURCES/glibc-RHEL-162901-2.patch new file mode 100644 index 0000000..b31887c --- /dev/null +++ b/SOURCES/glibc-RHEL-162901-2.patch @@ -0,0 +1,694 @@ +commit 3e20ddade31d9c392d8ccf7ec902172f4bb01c2b +Author: Adhemerval Zanella +Date: Fri Feb 10 16:37:36 2023 -0300 + + iconv: Remove _STRING_ARCH_unaligned usage + + Use put/get macros __builtin_bswap32 instead. It allows to remove + the unaligned routines, the compiler will generate unaligned access + if the ABI allows it. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Wilco Dijkstra + +diff --git a/iconv/gconv_simple.c b/iconv/gconv_simple.c +index 882d3642e445b107..d368284411c79282 100644 +--- a/iconv/gconv_simple.c ++++ b/iconv/gconv_simple.c +@@ -87,69 +87,22 @@ internal_ucs4_loop (struct __gconv_step *step, + #if __BYTE_ORDER == __LITTLE_ENDIAN + /* Sigh, we have to do some real work. */ + size_t cnt; +- uint32_t *outptr32 = (uint32_t *) outptr; +- +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) +- *outptr32++ = bswap_32 (*(const uint32_t *) inptr); +- +- *inptrp = inptr; +- *outptrp = (unsigned char *) outptr32; +-#elif __BYTE_ORDER == __BIG_ENDIAN +- /* Simply copy the data. */ +- *inptrp = inptr + n_convert * 4; +- *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-#else +-# error "This endianness is not supported." +-#endif +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*outptrp + 4 > outend) +- result = __GCONV_FULL_OUTPUT; +- else +- result = __GCONV_INCOMPLETE_INPUT; +- +- return result; +-} +- +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-internal_ucs4_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, +- const unsigned char *outend, +- size_t *irreversible) +-{ +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- size_t n_convert = MIN (inend - inptr, outend - outptr) / 4; +- int result; +- +-# if __BYTE_ORDER == __LITTLE_ENDIAN +- /* Sigh, we have to do some real work. */ +- size_t cnt; + + for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) + { +- outptr[0] = inptr[3]; +- outptr[1] = inptr[2]; +- outptr[2] = inptr[1]; +- outptr[3] = inptr[0]; ++ uint32_t val = get32 (inptr); ++ put32 (outptr, __builtin_bswap32 (val)); + } + + *inptrp = inptr; + *outptrp = outptr; +-# elif __BYTE_ORDER == __BIG_ENDIAN ++#elif __BYTE_ORDER == __BIG_ENDIAN + /* Simply copy the data. */ + *inptrp = inptr + n_convert * 4; + *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-# else +-# error "This endianess is not supported." +-# endif ++#else ++# error "This endianness is not supported." ++#endif + + /* Determine the status. */ + if (*inptrp == inend) +@@ -161,7 +114,6 @@ internal_ucs4_loop_unaligned (struct __gconv_step *step, + + return result; + } +-#endif + + + static inline int +@@ -243,12 +195,9 @@ ucs4_internal_loop (struct __gconv_step *step, + + for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4) + { +- uint32_t inval; +- ++ uint32_t inval = get32 (inptr); + #if __BYTE_ORDER == __LITTLE_ENDIAN +- inval = bswap_32 (*(const uint32_t *) inptr); +-#else +- inval = *(const uint32_t *) inptr; ++ inval = __builtin_bswap32 (inval); + #endif + + if (__glibc_unlikely (inval > 0x7fffffff)) +@@ -273,7 +222,7 @@ ucs4_internal_loop (struct __gconv_step *step, + return __gconv_mark_illegal_input (step_data); + } + +- *((uint32_t *) outptr) = inval; ++ put32 (outptr, inval); + outptr += sizeof (uint32_t); + } + +@@ -291,75 +240,6 @@ ucs4_internal_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-ucs4_internal_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, +- const unsigned char *outend, +- size_t *irreversible) +-{ +- int flags = step_data->__flags; +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- int result; +- +- for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4) +- { +- if (__glibc_unlikely (inptr[0] > 0x80)) +- { +- /* The value is too large. We don't try transliteration here since +- this is not an error because of the lack of possibilities to +- represent the result. This is a genuine bug in the input since +- UCS4 does not allow such values. */ +- if (irreversible == NULL) +- /* We are transliterating, don't try to correct anything. */ +- return __GCONV_ILLEGAL_INPUT; +- +- if (flags & __GCONV_IGNORE_ERRORS) +- { +- /* Just ignore this character. */ +- ++*irreversible; +- continue; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- return __GCONV_ILLEGAL_INPUT; +- } +- +-# if __BYTE_ORDER == __LITTLE_ENDIAN +- outptr[3] = inptr[0]; +- outptr[2] = inptr[1]; +- outptr[1] = inptr[2]; +- outptr[0] = inptr[3]; +-# else +- outptr[0] = inptr[0]; +- outptr[1] = inptr[1]; +- outptr[2] = inptr[2]; +- outptr[3] = inptr[3]; +-# endif +- outptr += 4; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*outptrp + 4 > outend) +- result = __GCONV_FULL_OUTPUT; +- else +- result = __GCONV_INCOMPLETE_INPUT; +- +- return result; +-} +-#endif +- + + static inline int + __attribute ((always_inline)) +@@ -454,11 +334,12 @@ internal_ucs4le_loop (struct __gconv_step *step, + #if __BYTE_ORDER == __BIG_ENDIAN + /* Sigh, we have to do some real work. */ + size_t cnt; +- uint32_t *outptr32 = (uint32_t *) outptr; + +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) +- *outptr32++ = bswap_32 (*(const uint32_t *) inptr); +- outptr = (unsigned char *) outptr32; ++ for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) ++ { ++ uint32_t val = get32 (inptr); ++ put32 (outptr, __builtin_bswap32 (val)); ++ } + + *inptrp = inptr; + *outptrp = outptr; +@@ -481,59 +362,6 @@ internal_ucs4le_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-internal_ucs4le_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, +- const unsigned char *outend, +- size_t *irreversible) +-{ +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- size_t n_convert = MIN (inend - inptr, outend - outptr) / 4; +- int result; +- +-# if __BYTE_ORDER == __BIG_ENDIAN +- /* Sigh, we have to do some real work. */ +- size_t cnt; +- +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) +- { +- outptr[0] = inptr[3]; +- outptr[1] = inptr[2]; +- outptr[2] = inptr[1]; +- outptr[3] = inptr[0]; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +-# elif __BYTE_ORDER == __LITTLE_ENDIAN +- /* Simply copy the data. */ +- *inptrp = inptr + n_convert * 4; +- *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-# else +-# error "This endianess is not supported." +-# endif +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*inptrp + 4 > inend) +- result = __GCONV_INCOMPLETE_INPUT; +- else +- { +- assert (*outptrp + 4 > outend); +- result = __GCONV_FULL_OUTPUT; +- } +- +- return result; +-} +-#endif +- + + static inline int + __attribute ((always_inline)) +@@ -613,12 +441,9 @@ ucs4le_internal_loop (struct __gconv_step *step, + + for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4) + { +- uint32_t inval; +- ++ uint32_t inval = get32 (inptr); + #if __BYTE_ORDER == __BIG_ENDIAN +- inval = bswap_32 (*(const uint32_t *) inptr); +-#else +- inval = *(const uint32_t *) inptr; ++ inval = __builtin_bswap32 (inval); + #endif + + if (__glibc_unlikely (inval > 0x7fffffff)) +@@ -643,7 +468,7 @@ ucs4le_internal_loop (struct __gconv_step *step, + return __gconv_mark_illegal_input (step_data); + } + +- *((uint32_t *) outptr) = inval; ++ put32 (outptr, inval); + outptr += sizeof (uint32_t); + } + +@@ -664,79 +489,6 @@ ucs4le_internal_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-ucs4le_internal_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, +- const unsigned char *outend, +- size_t *irreversible) +-{ +- int flags = step_data->__flags; +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- int result; +- +- for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4) +- { +- if (__glibc_unlikely (inptr[3] > 0x80)) +- { +- /* The value is too large. We don't try transliteration here since +- this is not an error because of the lack of possibilities to +- represent the result. This is a genuine bug in the input since +- UCS4 does not allow such values. */ +- if (irreversible == NULL) +- /* We are transliterating, don't try to correct anything. */ +- return __GCONV_ILLEGAL_INPUT; +- +- if (flags & __GCONV_IGNORE_ERRORS) +- { +- /* Just ignore this character. */ +- ++*irreversible; +- continue; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- return __GCONV_ILLEGAL_INPUT; +- } +- +-# if __BYTE_ORDER == __BIG_ENDIAN +- outptr[3] = inptr[0]; +- outptr[2] = inptr[1]; +- outptr[1] = inptr[2]; +- outptr[0] = inptr[3]; +-# else +- outptr[0] = inptr[0]; +- outptr[1] = inptr[1]; +- outptr[2] = inptr[2]; +- outptr[3] = inptr[3]; +-# endif +- +- outptr += 4; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*inptrp + 4 > inend) +- result = __GCONV_INCOMPLETE_INPUT; +- else +- { +- assert (*outptrp + 4 > outend); +- result = __GCONV_FULL_OUTPUT; +- } +- +- return result; +-} +-#endif +- + + static inline int + __attribute ((always_inline)) +diff --git a/iconv/loop.c b/iconv/loop.c +index 851caa9d42d45a47..696caa212aaec4bd 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -59,12 +59,7 @@ + #include + + #undef FCTNAME2 +-#if _STRING_ARCH_unaligned || !defined DEFINE_UNALIGNED +-# define FCTNAME2(name) name +-#else +-# define FCTNAME2(name) name##_unaligned +-#endif +-#define FCTNAME(name) FCTNAME2(name) ++#define FCTNAME(name) name + + + /* We need at least one byte for the next round. */ +@@ -282,20 +277,9 @@ FCTNAME (LOOPFCT) (struct __gconv_step *step, + } + + +-/* Include the file a second time to define the function to handle +- unaligned access. */ +-#if !defined DEFINE_UNALIGNED && !_STRING_ARCH_unaligned \ +- && MIN_NEEDED_INPUT != 1 && MAX_NEEDED_INPUT % MIN_NEEDED_INPUT == 0 \ +- && MIN_NEEDED_OUTPUT != 1 && MAX_NEEDED_OUTPUT % MIN_NEEDED_OUTPUT == 0 +-# undef unaligned +- +-# define DEFINE_UNALIGNED +-# include "loop.c" +-# undef DEFINE_UNALIGNED +-#else +-# if MAX_NEEDED_INPUT > 1 +-# define SINGLE(fct) SINGLE2 (fct) +-# define SINGLE2(fct) fct##_single ++#if MAX_NEEDED_INPUT > 1 ++# define SINGLE(fct) SINGLE2 (fct) ++# define SINGLE2(fct) fct##_single + static inline int + __attribute ((always_inline)) + SINGLE(LOOPFCT) (struct __gconv_step *step, +@@ -305,37 +289,37 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + size_t *irreversible EXTRA_LOOP_DECLS) + { + mbstate_t *state = step_data->__statep; +-# ifdef LOOP_NEED_FLAGS ++# ifdef LOOP_NEED_FLAGS + int flags = step_data->__flags; +-# endif +-# ifdef LOOP_NEED_DATA ++# endif ++# ifdef LOOP_NEED_DATA + void *data = step->__data; +-# endif ++# endif + int result = __GCONV_OK; + unsigned char bytebuf[MAX_NEEDED_INPUT]; + const unsigned char *inptr = *inptrp; + unsigned char *outptr = *outptrp; + size_t inlen; + +-# ifdef INIT_PARAMS ++# ifdef INIT_PARAMS + INIT_PARAMS; +-# endif ++# endif + +-# ifdef UNPACK_BYTES ++# ifdef UNPACK_BYTES + UNPACK_BYTES +-# else ++# else + /* Add the bytes from the state to the input buffer. */ + assert ((state->__count & 7) <= sizeof (state->__value)); + for (inlen = 0; inlen < (size_t) (state->__count & 7); ++inlen) + bytebuf[inlen] = state->__value.__wchb[inlen]; +-# endif ++# endif + + /* Are there enough bytes in the input buffer? */ + if (MIN_NEEDED_INPUT > 1 + && __builtin_expect (inptr + (MIN_NEEDED_INPUT - inlen) > inend, 0)) + { + *inptrp = inend; +-# ifdef STORE_REST ++# ifdef STORE_REST + + /* Building with -O3 GCC emits a `array subscript is above array + bounds' warning. GCC BZ #64739 has been opened for this. */ +@@ -350,14 +334,14 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + inend = &bytebuf[inlen]; + + STORE_REST +-# else ++# else + /* We don't have enough input for another complete input + character. */ + size_t inlen_after = inlen + (inend - inptr); + assert (inlen_after <= sizeof (state->__value.__wchb)); + for (; inlen < inlen_after; inlen++) + state->__value.__wchb[inlen] = *inptr++; +-# endif ++# endif + + return __GCONV_INCOMPLETE_INPUT; + } +@@ -403,11 +387,11 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + result = __GCONV_OK; + + /* Clear the state buffer. */ +-# ifdef CLEAR_STATE ++# ifdef CLEAR_STATE + CLEAR_STATE; +-# else ++# else + state->__count &= ~7; +-# endif ++# endif + } + else if (result == __GCONV_INCOMPLETE_INPUT) + { +@@ -416,11 +400,11 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + assert (inend != &bytebuf[MAX_NEEDED_INPUT]); + + *inptrp += inend - bytebuf - (state->__count & 7); +-# ifdef STORE_REST ++# ifdef STORE_REST + inptrp = &inptr; + + STORE_REST +-# else ++# else + /* We don't have enough input for another complete input + character. */ + assert (inend - inptr > (state->__count & ~7)); +@@ -429,14 +413,13 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + for (inlen = 0; inlen < inend - inptr; inlen++) + state->__value.__wchb[inlen] = inptr[inlen]; + inptr = inend; +-# endif ++# endif + } + + return result; + } +-# undef SINGLE +-# undef SINGLE2 +-# endif ++# undef SINGLE ++# undef SINGLE2 + + + # ifdef ONEBYTE_BODY +@@ -468,4 +451,3 @@ gconv_btowc (struct __gconv_step *step, unsigned char c) + #undef LOOP_NEED_STATE + #undef LOOP_NEED_FLAGS + #undef LOOP_NEED_DATA +-#undef unaligned +diff --git a/iconv/skeleton.c b/iconv/skeleton.c +index 6992701808079ec2..e7933ae333a5eae5 100644 +--- a/iconv/skeleton.c ++++ b/iconv/skeleton.c +@@ -451,33 +451,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + size_t lirreversible = 0; + size_t *lirreversiblep = irreversible ? &lirreversible : NULL; + +- /* The following assumes that encodings, which have a variable length +- what might unalign a buffer even though it is an aligned in the +- beginning, either don't have the minimal number of bytes as a divisor +- of the maximum length or have a minimum length of 1. This is true +- for all known and supported encodings. +- We use && instead of || to combine the subexpression for the FROM +- encoding and for the TO encoding, because usually one of them is +- INTERNAL, for which the subexpression evaluates to 1, but INTERNAL +- buffers are always aligned correctly. */ +-#define POSSIBLY_UNALIGNED \ +- (!_STRING_ARCH_unaligned \ +- && (((FROM_LOOP_MIN_NEEDED_FROM != 1 \ +- && FROM_LOOP_MAX_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_FROM == 0) \ +- && (FROM_LOOP_MIN_NEEDED_TO != 1 \ +- && FROM_LOOP_MAX_NEEDED_TO % FROM_LOOP_MIN_NEEDED_TO == 0)) \ +- || ((TO_LOOP_MIN_NEEDED_FROM != 1 \ +- && TO_LOOP_MAX_NEEDED_FROM % TO_LOOP_MIN_NEEDED_FROM == 0) \ +- && (TO_LOOP_MIN_NEEDED_TO != 1 \ +- && TO_LOOP_MAX_NEEDED_TO % TO_LOOP_MIN_NEEDED_TO == 0)))) +-#if POSSIBLY_UNALIGNED +- int unaligned; +-# define GEN_unaligned(name) GEN_unaligned2 (name) +-# define GEN_unaligned2(name) name##_unaligned +-#else +-# define unaligned 0 +-#endif +- + #ifdef PREPARE_LOOP + PREPARE_LOOP + #endif +@@ -517,18 +490,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + } + #endif + +-#if POSSIBLY_UNALIGNED +- unaligned = +- ((FROM_DIRECTION +- && ((uintptr_t) inptr % FROM_LOOP_MIN_NEEDED_FROM != 0 +- || ((data->__flags & __GCONV_IS_LAST) +- && (uintptr_t) outbuf % FROM_LOOP_MIN_NEEDED_TO != 0))) +- || (!FROM_DIRECTION +- && (((data->__flags & __GCONV_IS_LAST) +- && (uintptr_t) outbuf % TO_LOOP_MIN_NEEDED_TO != 0) +- || (uintptr_t) inptr % TO_LOOP_MIN_NEEDED_FROM != 0))); +-#endif +- + while (1) + { + /* Remember the start value for this round. */ +@@ -546,34 +507,14 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + SAVE_RESET_STATE (1); + #endif + +- if (__glibc_likely (!unaligned)) +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend, +- lirreversiblep EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend, +- lirreversiblep EXTRA_LOOP_ARGS); +- } +-#if POSSIBLY_UNALIGNED ++ if (FROM_DIRECTION) ++ /* Run the conversion loop. */ ++ status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend, ++ lirreversiblep EXTRA_LOOP_ARGS); + else +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- status = GEN_unaligned (FROM_LOOP) (step, data, inptrp, inend, +- &outbuf, outend, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- status = GEN_unaligned (TO_LOOP) (step, data, inptrp, inend, +- &outbuf, outend, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-#endif ++ /* Run the conversion loop. */ ++ status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend, ++ lirreversiblep EXTRA_LOOP_ARGS); + + /* If we were called as part of an error handling module we + don't do anything else here. */ +@@ -638,41 +579,18 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + SAVE_RESET_STATE (0); + #endif + +- if (__glibc_likely (!unaligned)) +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- nstatus = FROM_LOOP (step, data, inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- nstatus = TO_LOOP (step, data, inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-#if POSSIBLY_UNALIGNED ++ if (FROM_DIRECTION) ++ /* Run the conversion loop. */ ++ nstatus = FROM_LOOP (step, data, inptrp, inend, ++ &outbuf, outerr, ++ lirreversiblep ++ EXTRA_LOOP_ARGS); + else +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- nstatus = GEN_unaligned (FROM_LOOP) (step, data, +- inptrp, inend, +- &outbuf, +- outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- nstatus = GEN_unaligned (TO_LOOP) (step, data, +- inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-#endif ++ /* Run the conversion loop. */ ++ nstatus = TO_LOOP (step, data, inptrp, inend, ++ &outbuf, outerr, ++ lirreversiblep ++ EXTRA_LOOP_ARGS); + + /* We must run out of output buffer space in this + rerun. */ diff --git a/SOURCES/glibc-RHEL-162901-3.patch b/SOURCES/glibc-RHEL-162901-3.patch new file mode 100644 index 0000000..9294f79 --- /dev/null +++ b/SOURCES/glibc-RHEL-162901-3.patch @@ -0,0 +1,326 @@ +commit d6f08d1cf027f4eb2ba289a6cc66853722d4badc +Author: Florian Weimer +Date: Thu Apr 16 19:13:43 2026 +0200 + + Use pending character state in IBM1390, IBM1399 character sets (CVE-2026-4046) + + Follow the example in iso-2022-jp-3.c and use the __count state + variable to store the pending character. This avoids restarting + the conversion if the output buffer ends between two 4-byte UCS-4 + code points, so that the assert reported in the bug can no longer + happen. + + Even though the fix is applied to ibm1364.c, the change is only + effective for the two HAS_COMBINED codecs for IBM1390, IBM1399. + + The test case was mostly auto-generated using + claude-4.6-opus-high-thinking, and composer-2-fast shows up in the + log as well. During review, gpt-5.4-xhigh flagged that the original + version of the test case was not exercising the new character + flush logic. + + This fixes bug 33980. + + Assisted-by: LLM + Reviewed-by: Carlos O'Donell + +Conflicts: + iconvdata/Makefile + (fixup context) + +diff --git a/iconvdata/Makefile b/iconvdata/Makefile +index 25bd004e7f92a994..798f9be86d376aeb 100644 +--- a/iconvdata/Makefile ++++ b/iconvdata/Makefile +@@ -75,7 +75,8 @@ ifeq (yes,$(build-shared)) + tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \ + tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \ + bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \ +- bug-iconv13 bug-iconv14 bug-iconv15 tst-iconv-iso-2022-cn-ext ++ bug-iconv13 bug-iconv14 bug-iconv15 \ ++ tst-iconv-iso-2022-cn-ext tst-bug33980 + ifeq ($(have-thread-library),yes) + tests += bug-iconv3 + endif +@@ -332,6 +333,8 @@ $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \ + $(addprefix $(objpfx),$(modules.so)) + $(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \ + $(addprefix $(objpfx),$(modules.so)) ++$(objpfx)tst-bug33980.out: $(addprefix $(objpfx), $(gconv-modules)) \ ++ $(addprefix $(objpfx),$(modules.so)) + + $(objpfx)iconv-test.out: run-iconv-test.sh \ + $(addprefix $(objpfx), $(gconv-modules)) \ +diff --git a/iconvdata/ibm1364.c b/iconvdata/ibm1364.c +index 18f1aa4f6da4b021..c3e9b7c881eaa1d9 100644 +--- a/iconvdata/ibm1364.c ++++ b/iconvdata/ibm1364.c +@@ -68,12 +68,29 @@ + + /* Since this is a stateful encoding we have to provide code which resets + the output state to the initial state. This has to be done during the +- flushing. */ ++ flushing. For the to-internal direction (FROM_DIRECTION is true), ++ there may be a pending character that needs flushing. */ + #define EMIT_SHIFT_TO_INIT \ + if ((data->__statep->__count & ~7) != sb) \ + { \ + if (FROM_DIRECTION) \ +- data->__statep->__count &= 7; \ ++ { \ ++ uint32_t ch = data->__statep->__count >> 7; \ ++ if (__glibc_unlikely (ch != 0)) \ ++ { \ ++ if (__glibc_unlikely (outend - outbuf < 4)) \ ++ status = __GCONV_FULL_OUTPUT; \ ++ else \ ++ { \ ++ put32 (outbuf, ch); \ ++ outbuf += 4; \ ++ /* Clear character and db bit. */ \ ++ data->__statep->__count &= 7; \ ++ } \ ++ } \ ++ else \ ++ data->__statep->__count &= 7; \ ++ } \ + else \ + { \ + /* We are not in the initial state. To switch back we have \ +@@ -100,11 +117,13 @@ + *curcsp = save_curcs + + +-/* Current codeset type. */ ++/* Current codeset type. The bit is stored in the __count variable of ++ the conversion state. If the db bit is set, bit 7 and above store ++ a pending UCS-4 code point if non-zero. */ + enum + { +- sb = 0, +- db = 64 ++ sb = 0, /* Single byte mode. */ ++ db = 64 /* Double byte mode. */ + }; + + +@@ -120,21 +139,29 @@ enum + } \ + else \ + { \ +- /* This is a combined character. Make sure we have room. */ \ +- if (__glibc_unlikely (outptr + 8 > outend)) \ +- { \ +- result = __GCONV_FULL_OUTPUT; \ +- break; \ +- } \ +- \ + const struct divide *cmbp \ + = &DB_TO_UCS4_COMB[ch - __TO_UCS4_COMBINED_MIN]; \ + assert (cmbp->res1 != 0 && cmbp->res2 != 0); \ + \ + put32 (outptr, cmbp->res1); \ + outptr += 4; \ +- put32 (outptr, cmbp->res2); \ +- outptr += 4; \ ++ \ ++ /* See whether we have room for the second character. */ \ ++ if (outend - outptr >= 4) \ ++ { \ ++ put32 (outptr, cmbp->res2); \ ++ outptr += 4; \ ++ } \ ++ else \ ++ { \ ++ /* Otherwise store only the first character now, and \ ++ put the second one into the queue. */ \ ++ curcs |= cmbp->res2 << 7; \ ++ inptr += 2; \ ++ /* Tell the caller why we terminate the loop. */ \ ++ result = __GCONV_FULL_OUTPUT; \ ++ break; \ ++ } \ + } \ + } + #else +@@ -154,7 +181,20 @@ enum + #define LOOPFCT FROM_LOOP + #define BODY \ + { \ +- uint32_t ch = *inptr; \ ++ uint32_t ch; \ ++ \ ++ ch = curcs >> 7; \ ++ if (__glibc_unlikely (ch != 0)) \ ++ { \ ++ put32 (outptr, ch); \ ++ outptr += 4; \ ++ /* Remove the pending character, but preserve state bits. */ \ ++ curcs &= (1 << 7) - 1; \ ++ continue; \ ++ } \ ++ \ ++ /* Otherwise read the next input byte. */ \ ++ ch = *inptr; \ + \ + if (__builtin_expect (ch, 0) == SO) \ + { \ +diff --git a/iconvdata/tst-bug33980.c b/iconvdata/tst-bug33980.c +new file mode 100644 +index 0000000000000000..c9693e0efebe4eae +--- /dev/null ++++ b/iconvdata/tst-bug33980.c +@@ -0,0 +1,153 @@ ++/* Test for bug 33980: combining characters in IBM1390/IBM1399. ++ 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 ++ ++/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes ++ starting at OUTBUF. OUTBUF should be right before an unmapped page ++ so that writing past the end will fault. Skip SHIFT bytes at the ++ start of the input and output, to exercise different buffer ++ alignment. TRUNCATE indicates skipped bytes at the end of ++ input (0 and 1 a valid). */ ++static void ++test_one (const char *encoding, unsigned int shift, unsigned int truncate, ++ char *outbuf, size_t outbufsize) ++{ ++ /* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two ++ Unicode code points when translated. */ ++ static char input[] = ++ { ++ /* 8 letters X. */ ++ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, ++ /* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */ ++ 0x0e, 0xec, 0xb5, 0x0f ++ }; ++ ++ /* Expected output after UTF-8 conversion. */ ++ static char expected[] = ++ { ++ 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', ++ /* U+304B (HIRAGANA LETTER KA). */ ++ 0xe3, 0x81, 0x8b, ++ /* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */ ++ 0xe3, 0x82, 0x9a ++ }; ++ ++ iconv_t cd = iconv_open ("UTF-8", encoding); ++ TEST_VERIFY_EXIT (cd != (iconv_t) -1); ++ ++ char result_storage[64]; ++ struct alloc_buffer result_buf ++ = alloc_buffer_create (result_storage, sizeof (result_storage)); ++ ++ char *inptr = &input[shift]; ++ size_t inleft = sizeof (input) - shift - truncate; ++ ++ while (inleft > 0) ++ { ++ char *outptr = outbuf; ++ size_t outleft = outbufsize; ++ size_t inleft_before = inleft; ++ ++ size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft); ++ size_t produced = outptr - outbuf; ++ alloc_buffer_copy_bytes (&result_buf, outbuf, produced); ++ ++ if (ret == (size_t) -1 && errno == E2BIG) ++ { ++ if (produced == 0 && inleft == inleft_before) ++ { ++ /* Output buffer too small to make progress. This is ++ expected for very small output buffer sizes. */ ++ TEST_VERIFY_EXIT (outbufsize < 3); ++ break; ++ } ++ continue; ++ } ++ if (ret == (size_t) -1) ++ FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize); ++ break; ++ } ++ ++ /* Flush any pending state (e.g. a buffered combined character). ++ With outbufsize < 3, we could not store the first character, so ++ the second character did not become pending, and there is nothing ++ to flush. */ ++ { ++ char *outptr = outbuf; ++ size_t outleft = outbufsize; ++ ++ size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft); ++ TEST_VERIFY_EXIT (ret == 0); ++ size_t produced = outptr - outbuf; ++ alloc_buffer_copy_bytes (&result_buf, outbuf, produced); ++ ++ /* Second flush does not provide more data. */ ++ outptr = outbuf; ++ outleft = outbufsize; ++ ret = iconv (cd, NULL, NULL, &outptr, &outleft); ++ TEST_VERIFY_EXIT (ret == 0); ++ TEST_VERIFY (outptr == outbuf); ++ } ++ ++ TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf)); ++ size_t result_used ++ = sizeof (result_storage) - alloc_buffer_size (&result_buf); ++ ++ if (outbufsize >= 3) ++ { ++ TEST_COMPARE (inleft, 0); ++ TEST_COMPARE (result_used, sizeof (expected) - shift); ++ TEST_COMPARE_BLOB (result_storage, result_used, ++ &expected[shift], sizeof (expected) - shift); ++ } ++ else ++ /* If the buffer is too small, only the leading X could be converted. */ ++ TEST_COMPARE (result_used, 8 - shift); ++ ++ TEST_VERIFY_EXIT (iconv_close (cd) == 0); ++} ++ ++static int ++do_test (void) ++{ ++ struct support_next_to_fault ntf ++ = support_next_to_fault_allocate (8); ++ ++ for (int shift = 0; shift <= 8; ++shift) ++ for (int truncate = 0; truncate < 2; ++truncate) ++ for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++) ++ { ++ char *outbuf = ntf.buffer + ntf.length - outbufsize; ++ test_one ("IBM1390", shift, truncate, outbuf, outbufsize); ++ test_one ("IBM1399", shift, truncate, outbuf, outbufsize); ++ } ++ ++ support_next_to_fault_free (&ntf); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-168096.patch b/SOURCES/glibc-RHEL-168096.patch new file mode 100644 index 0000000..cd80ed7 --- /dev/null +++ b/SOURCES/glibc-RHEL-168096.patch @@ -0,0 +1,184 @@ +commit d8997716a1ca22cf038eac86ed286830ba9818cc +Author: DJ Delorie +Date: Wed Apr 1 17:52:25 2026 -0400 + + nss: fix __get_default_domain logic + + Fix logic bug in __nss_get_default_domain that prevents + proper initialization. + + Because this function is not exposed, the test case must link + against the object directly. + + Bug origin commit: 64d1e08ea822bf47cb2796ad0f727136227f983c + + Co-authored-by: Florian Weimer + Reviewed-by: Frédéric Bérat + +Conflicts: + nss/Makefile + (fixup context) + +diff --git a/nss/Makefile b/nss/Makefile +index 4d128da0a366301f..4d22ef2661fb9b27 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -61,6 +61,7 @@ tests := \ + bug17079 \ + test-digits-dots \ + test-netdb \ ++ tst-default-domain \ + tst-nss-getpwent \ + tst-nss-malloc-failure-getlogin_r \ + tst-nss-test1 \ +@@ -226,6 +227,8 @@ endif + $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)libnss_files.so + $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)libnss_files.so + ++$(objpfx)tst-default-domain: $(objpfx)nisdomain.os ++ + tst-nss-gai-hv2-canonname-ENV = \ + MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so +diff --git a/nss/nss_compat/nisdomain.c b/nss/nss_compat/nisdomain.c +index 838fd95820a47767..899b323f63d90076 100644 +--- a/nss/nss_compat/nisdomain.c ++++ b/nss/nss_compat/nisdomain.c +@@ -36,7 +36,7 @@ __nss_get_default_domain (char **outdomain) + + __libc_lock_lock (domainname_lock); + +- if (domainname[0] != '\0') ++ if (domainname[0] == '\0') + { + if (getdomainname (domainname, MAXDOMAINNAMELEN) < 0) + result = errno; +diff --git a/nss/tst-default-domain.c b/nss/tst-default-domain.c +new file mode 100644 +index 0000000000000000..2fa9153d8802d4c3 +--- /dev/null ++++ b/nss/tst-default-domain.c +@@ -0,0 +1,123 @@ ++/* Basic test of __nss_get_default_domain ++ 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 "nss_compat/nisdomain.h" ++ ++#include ++#include ++#include ++#include ++ ++char unset_domain[] = "unset_domain"; ++char new_domain[] = "new_domain"; ++ ++/* This function checks the __nss_get_default_domain() function in ++ nss_compat/nssdomain.c. Because this is an internal function to ++ libnss_compat.so, the Makefile will link that object to this test ++ case directly. */ ++ ++static int ++do_test (void) ++{ ++ char *domain_name; ++ char buf[1024]; ++ ++ /* We need to be in a network namespace so we can change the domain ++ name without interfering with the host system. */ ++ support_become_root (); ++ support_enter_network_namespace (); ++ if (!support_in_uts_namespace ()) ++ return EXIT_UNSUPPORTED; ++ ++ /* First pass: set an empty domain and make sure it's returned ++ correctly. This should not be cached. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname ("", 0) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, ""); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ /* Make sure the correct domain name is returned. */ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, ""); ++ ++ /* Second pass: set a non-empty domain and make sure it's returned ++ correctly. This works because the empty domain is not ++ cached. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname (new_domain, strlen (new_domain)) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, new_domain); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ /* Make sure the correct domain name is returned. */ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ /* The function caches the name, so check it twice. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ /* Third pass: set an empty domain again but expect the cached ++ value. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname ("", 0) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, ""); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-168851.patch b/SOURCES/glibc-RHEL-168851.patch new file mode 100644 index 0000000..9e41b3f --- /dev/null +++ b/SOURCES/glibc-RHEL-168851.patch @@ -0,0 +1,234 @@ +commit 5663ab0b833df187b15e7bb4b18173e22beb8bd1 +Author: Carlos O'Donell +Date: Fri Mar 20 16:43:33 2026 -0400 + + resolv: Count records correctly (CVE-2026-4437) + + The answer section boundary was previously ignored, and the code in + getanswer_ptr would iterate past the last resource record, but not + beyond the end of the returned data. This could lead to subsequent data + being interpreted as answer records, thus violating the DNS + specification. Such resource records could be maliciously crafted and + hidden from other tooling, but processed by the glibc stub resolver and + acted upon by the application. While we trust the data returned by the + configured recursive resolvers, we should not trust its format and + should validate it as required. It is a security issue to incorrectly + process the DNS protocol. + + A regression test is added for response section crossing. + + No regressions on x86_64-linux-gnu. + + Reviewed-by: Collin Funk + (cherry picked from commit 9f5f18aab40ec6b61fa49a007615e6077e9a979b) + +diff --git a/resolv/Makefile b/resolv/Makefile +index 116ab6a1574eb1ff..649c1254c30d40c6 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -108,6 +108,7 @@ tests += \ + tst-resolv-basic \ + tst-resolv-binary \ + tst-resolv-byaddr \ ++ tst-resolv-dns-section \ + tst-resolv-edns \ + tst-resolv-invalid-cname \ + tst-resolv-network \ +@@ -119,6 +120,7 @@ tests += \ + tst-resolv-semi-failure \ + tst-resolv-short-response \ + tst-resolv-trailing \ ++ # tests + + # This test calls __res_context_send directly, which is not exported + # from libresolv. +@@ -288,6 +290,8 @@ $(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-dns-section: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so +diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +index 3d261b6810bba5c9..54d6e2822d3454ca 100644 +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -821,7 +821,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen, + /* expected_name may be updated to point into this buffer. */ + unsigned char name_buffer[NS_MAXCDNAME]; + +- while (ancount > 0) ++ for (; ancount > 0; --ancount) + { + struct ns_rr_wire rr; + if (!__ns_rr_cursor_next (&c, &rr)) +diff --git a/resolv/tst-resolv-dns-section.c b/resolv/tst-resolv-dns-section.c +new file mode 100644 +index 0000000000000000..1171baef51e3cc36 +--- /dev/null ++++ b/resolv/tst-resolv-dns-section.c +@@ -0,0 +1,162 @@ ++/* Test handling of invalid section transitions (bug 34014). ++ Copyright (C) 2022-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 ++ ++/* Name of test, and the second section type. */ ++struct item { ++ const char *test; ++ int ns_section; ++}; ++ ++static const struct item test_items[] = ++ { ++ { "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar }, ++ { "Test crossing from ns_s_an to ns_s_an.", ns_s_ns }, ++ ++ { NULL, 0 }, ++ }; ++ ++/* The response is designed to contain the following: ++ - An Answer section with one T_PTR record that is skipped. ++ - A second section with a semantically invalid T_PTR record. ++ The original defect is that the response parsing would cross ++ section boundaries and handle the additional section T_PTR ++ as if it were an answer. A conforming implementation would ++ stop as soon as it reaches the end of the section. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count; ++ char *tail = NULL; ++ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%ms", &count, &tail) == 2) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* We have a bounded number of possible tests. */ ++ TEST_VERIFY (count >= 0); ++ TEST_VERIFY (count <= 15); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record, but the wrong name (skipped). */ ++ resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, "test.ptr.example.net"); ++ resolv_response_close_record (b); ++ ++ /* Add a second section to test section boundary crossing. */ ++ resolv_response_section (b, test_items[count].ns_section); ++ /* Semantically incorrect, but hide a T_PTR entry. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ resolv_response_add_name (b, "wrong.ptr.example.net"); ++ resolv_response_close_record (b); ++} ++ ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY (count < array_length (test_items)); ++ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ TEST_VERIFY (answer == NULL); ++ TEST_VERIFY (h_errno == NO_RECOVERY); ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; test_items[i].test != NULL; i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-168852.patch b/SOURCES/glibc-RHEL-168852.patch new file mode 100644 index 0000000..159e4d5 --- /dev/null +++ b/SOURCES/glibc-RHEL-168852.patch @@ -0,0 +1,318 @@ +commit 9344c796f7a4ac8f2c59de63f3e1e936b51e817d +Author: Carlos O'Donell +Date: Fri Mar 20 17:14:33 2026 -0400 + + resolv: Check hostname for validity (CVE-2026-4438) + + The processed hostname in getanswer_ptr should be correctly checked to + avoid invalid characters from being allowed, including shell + metacharacters. It is a security issue to fail to check the returned + hostname for validity. + + A regression test is added for invalid metacharacters and other cases + of invalid or valid characters. + + No regressions on x86_64-linux-gnu. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit e10977481f4db4b2a3ce34fa4c3a1e26651ae312) + +Conflicts: + resolv/Makefile + (fixup context) + +diff --git a/resolv/Makefile b/resolv/Makefile +index 649c1254c30d40c6..aa25c71f1eea9c28 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -111,6 +111,7 @@ tests += \ + tst-resolv-dns-section \ + tst-resolv-edns \ + tst-resolv-invalid-cname \ ++ tst-resolv-invalid-ptr \ + tst-resolv-network \ + tst-resolv-noaaaa \ + tst-resolv-noaaaa-vc \ +@@ -302,6 +303,8 @@ $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \ + $(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-no-search: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-invalid-ptr: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) +diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +index 54d6e2822d3454ca..2016131b24a55145 100644 +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -867,7 +867,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen, + char hname[MAXHOSTNAMELEN + 1]; + if (__ns_name_unpack (c.begin, c.end, rr.rdata, + name_buffer, sizeof (name_buffer)) < 0 +- || !__res_binary_hnok (expected_name) ++ || !__res_binary_hnok (name_buffer) + || __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0) + { + *h_errnop = NO_RECOVERY; +diff --git a/resolv/tst-resolv-invalid-ptr.c b/resolv/tst-resolv-invalid-ptr.c +new file mode 100644 +index 0000000000000000..0c802ab96780efb0 +--- /dev/null ++++ b/resolv/tst-resolv-invalid-ptr.c +@@ -0,0 +1,255 @@ ++/* Test handling of invalid T_PTR results (bug 34015). ++ Copyright (C) 2022-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 ++ ++/* Name of test, the answer, the expected error return, and if we ++ expect the call to fail. */ ++struct item { ++ const char *test; ++ const char *answer; ++ int expected; ++ bool fail; ++}; ++ ++static const struct item test_items[] = ++ { ++ /* Test for invalid characters. */ ++ { "Invalid use of \"|\"", ++ "test.|.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"&\"", ++ "test.&.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \";\"", ++ "test.;.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"<\"", ++ "test.<.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \">\"", ++ "test.>.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"(\"", ++ "test.(.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \")\"", ++ "test.).ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"$\"", ++ "test.$.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"`\"", ++ "test.`.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\\"", ++ "test.\\.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\'\"", ++ "test.'.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\"\"", ++ "test.\".ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \" \"", ++ "test. .ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\t\"", ++ "test.\t.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\n\"", ++ "test.\n.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\r\"", ++ "test.\r.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"*\"", ++ "test.*.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"?\"", ++ "test.?.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"[\"", ++ "test.[.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"]\"", ++ "test.].ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \",\"", ++ "test.,.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"~\"", ++ "test.~.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \":\"", ++ "test.:.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"!\"", ++ "test.!.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"@\"", ++ "test.@.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"#\"", ++ "test.#.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"%\"", ++ "test.%%.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"^\"", ++ "test.^.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */ ++ { "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)", ++ "ÁÂÃ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (4-byte, U+0750-U+0752)", ++ "ݐݑݒ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (6-byte, U+0904-U+0906)", ++ "ऄअआ.test.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for "-" which may be valid depending on position. */ ++ { "Invalid leading \"-\"", ++ "-test.ptr.example", NO_RECOVERY, true }, ++ { "Valid trailing \"-\"", ++ "test-.ptr.example", 0, false }, ++ { "Valid mid-label use of \"-\"", ++ "te-st.ptr.example", 0, false }, ++ ++ /* Test for "_" which is always valid in any position. */ ++ { "Valid leading use of \"_\"", ++ "_test.ptr.example", 0, false }, ++ { "Valid mid-label use of \"_\"", ++ "te_st.ptr.example", 0, false }, ++ { "Valid trailing use of \"_\"", ++ "test_.ptr.example", 0, false }, ++ ++ /* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */ ++ { "Valid \"[A-Z]\"", ++ "test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false }, ++ { "Valid \"[a-z]\"", ++ "test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false }, ++ { "Valid \"[0-9]\"", ++ "test.0123456789.ptr.example", 0, false }, ++ { "Valid mixed use of \"[A-Za-z0-9_-]\"", ++ "test.012abcABZ_-.ptr.example", 0, false }, ++ }; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count, count1; ++ char *tail = NULL; ++ ++ /* The test implementation can handle up to 255 tests. */ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ count |= count1 << 4; ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* Cross check. Count has a fixed bound (soft limit). */ ++ TEST_VERIFY (count >= 0 && count <= 255); ++ ++ /* We have a fixed number of tests (hard limit). */ ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, test_items[count].answer); ++ resolv_response_close_record (b); ++} ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ /* Generate an address to query for each test. */ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = (char) count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ ++ /* Verify h_errno is as expected. */ ++ TEST_COMPARE (h_errno, test_items[count].expected); ++ if (h_errno != test_items[count].expected) ++ /* And print more information if it's not. */ ++ printf ("INFO: %s\n", test_items[count].test); ++ ++ if (test_items[count].fail) ++ { ++ /* We expected a failure so verify answer is NULL. */ ++ TEST_VERIFY (answer == NULL); ++ /* If it's not NULL we should print out what we received. */ ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++ } ++ else ++ /* We don't expect a failure so answer must be valid. */ ++ TEST_COMPARE_STRING (answer->h_name, test_items[count].answer); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; i < array_length (test_items); i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-172710-1.patch b/SOURCES/glibc-RHEL-172710-1.patch new file mode 100644 index 0000000..1bd6427 --- /dev/null +++ b/SOURCES/glibc-RHEL-172710-1.patch @@ -0,0 +1,126 @@ +commit 839898777226a3ed88c0859f25ffe712519b4ead +Author: Rocket Ma +Date: Fri Apr 17 23:48:41 2026 -0700 + + stdio-common: Fix buffer overflow in scanf %mc [BZ #34008] + + * stdio-common/vfscanf-internal.c: When enlarging allocated buffer with + format %mc or %mC, glibc allocates one byte less, leading to + user-controlled one byte overflow. This commit fixes BZ #34008, or + CVE-2026-5450. + + Reviewed-by: Carlos O'Donell + Signed-off-by: Rocket Ma + Reviewed-by: H.J. Lu + +Conflicts: + stdio-common/Makefile + (usual test differences) + +diff -Nrup a/stdio-common/Makefile b/stdio-common/Makefile +--- a/stdio-common/Makefile 2026-06-16 08:24:01.348860456 -0400 ++++ b/stdio-common/Makefile 2026-06-16 08:18:22.791126117 -0400 +@@ -290,6 +290,7 @@ tests := \ + tst-vfprintf-mbs-prec \ + tst-vfprintf-user-type \ + tst-vfprintf-width-prec-alloc \ ++ tst-vfscanf-bz34008 \ + tst-wc-printf \ + tstdiomisc \ + tstgetln \ +@@ -441,6 +442,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$( + tst-vfprintf-width-prec-ENV = \ + MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so ++tst-vfscanf-bz34008-ENV = \ ++ MALLOC_CHECK_=3 \ ++ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so + tst-printf-bz25691-ENV = \ + MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so +diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c +new file mode 100644 +index 0000000000..48371c8a3d +--- /dev/null ++++ b/stdio-common/tst-vfscanf-bz34008.c +@@ -0,0 +1,48 @@ ++/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008) ++ Copyright (C) 2026 The GNU Toolchain Authors. ++ 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 "malloc/mcheck.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WIDTH 0x410 ++#define SCANFSTR "%1040mc" ++static int ++do_test (void) ++{ ++ mcheck_pedantic (NULL); ++ char *input = malloc (WIDTH + 1); ++ TEST_VERIFY (input != NULL); ++ memset (input, 'A', WIDTH); ++ input[WIDTH] = '\0'; ++ ++ char *buf = NULL; ++ TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1); ++ TEST_VERIFY (buf != NULL); ++ ++ free (buf); ++ free (input); ++ return 0; ++} ++ ++#include +diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c +index 59fc8208aa..3d11ac261e 100644 +--- a/stdio-common/vfscanf-internal.c ++++ b/stdio-common/vfscanf-internal.c +@@ -855,8 +855,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + { + /* Enlarge the buffer. */ + size_t newsize +- = strsize +- + (strsize >= width ? width - 1 : strsize); ++ = strsize + (strsize >= width ? width : strsize); + + str = (char *) realloc (*strptr, newsize); + if (str == NULL) +@@ -929,7 +928,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + && wstr == (wchar_t *) *strptr + strsize) + { + size_t newsize +- = strsize + (strsize > width ? width - 1 : strsize); ++ = strsize + (strsize >= width ? width : strsize); + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + newsize * sizeof (wchar_t)); +@@ -984,7 +983,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + && wstr == (wchar_t *) *strptr + strsize) + { + size_t newsize +- = strsize + (strsize > width ? width - 1 : strsize); ++ = strsize + (strsize >= width ? width : strsize); + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + newsize * sizeof (wchar_t)); diff --git a/SOURCES/glibc-RHEL-172710-2.patch b/SOURCES/glibc-RHEL-172710-2.patch new file mode 100644 index 0000000..a334a8c --- /dev/null +++ b/SOURCES/glibc-RHEL-172710-2.patch @@ -0,0 +1,80 @@ +commit b866ef29773b22a1343ff9084374775114350b78 +Author: Maciej W. Rozycki +Date: Wed May 27 12:57:10 2026 -0400 + + support: Implement 'xfmemopen' for seamless 'fmemopen' use + + Add 'xfmemopen' wrapper for seamless 'fmemopen' use in tests, following + 'xfopen', 'xfclose', etc., and providing a standardized error reporting + facility. + + Reviewed-by: Florian Weimer + (cherry picked from commit fe709cc24578ecfd2ff5b07e10e3829fcb55075b) + + Reviewed-by: Carlos O'Donell + +Conflicts: + support/Makefile + (usual test differences) + support/xstdio.h + (adjust for downstream context difference) + +diff -Nrup a/support/Makefile b/support/Makefile +--- a/support/Makefile 2026-06-16 10:34:13.241874132 -0400 ++++ b/support/Makefile 2026-06-16 10:37:06.085172353 -0400 +@@ -135,6 +135,7 @@ libsupport-routines = \ + xfchmod \ + xfclose \ + xfdopendir \ ++ xfmemopen \ + xfopen \ + xfork \ + xfread \ +diff --git a/support/xfmemopen.c b/support/xfmemopen.c +new file mode 100644 +index 0000000000..f1dbc72c67 +--- /dev/null ++++ b/support/xfmemopen.c +@@ -0,0 +1,31 @@ ++/* fmemopen with error checking. ++ Copyright (C) 2025 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 ++ ++FILE * ++xfmemopen (void *mem, size_t len, const char *mode) ++{ ++ FILE *fp = fmemopen (mem, len, mode); ++ if (fp == NULL) ++ FAIL_EXIT1 ("fmemopen (mode \"%s\"): %m", mode); ++ return fp; ++} +diff -Nrup a/support/xstdio.h b/support/xstdio.h +--- a/support/xstdio.h 2026-06-16 10:34:10.450856660 -0400 ++++ b/support/xstdio.h 2026-06-16 10:39:05.792825780 -0400 +@@ -26,6 +26,7 @@ __BEGIN_DECLS + + FILE *xfopen (const char *path, const char *mode); + void xfclose (FILE *); ++FILE *xfmemopen (void *mem, size_t len, const char *mode); + void xfread (void *ptr, size_t size, size_t nmemb, FILE *stream); + + /* Read a line from FP, using getline. *BUFFER must be NULL, or a diff --git a/SOURCES/glibc-RHEL-172710-3.patch b/SOURCES/glibc-RHEL-172710-3.patch new file mode 100644 index 0000000..6bfe8c5 --- /dev/null +++ b/SOURCES/glibc-RHEL-172710-3.patch @@ -0,0 +1,457 @@ +commit 97926e9017f3faeaacce9337f1288460f5e6ec7d +Author: Maciej W. Rozycki +Date: Wed May 27 12:57:10 2026 -0400 + + stdio-common: Reject insufficient character data in scanf [BZ #12701] + + Reject invalid formatted scanf character data with the 'c' conversion + where there is not enough input available to satisfy the field width + requested. It is required by ISO C that this conversion matches a + sequence of characters of exactly the number specified by the field + width and it is also already documented as such in our own manual: + + "It reads precisely the next N characters, and fails if it cannot get + that many." + + Currently a matching success is instead incorrectly produced where the + EOF condition is encountered before the required number of characters + has been retrieved, and the characters actually obtained are stored in + the buffer provided. + + Add test cases accordingly and remove placeholders from 'c' conversion + input data for the existing scanf tests. + + Reviewed-by: Adhemerval Zanella + + [This is a modified version of commit 2b16c76609, which tests for the + old behavior and only includes the test cases, for older branches + and downstream backports - DJ] + + Reviewed-by: Carlos O'Donell + +Conflicts: + localedata/Makefile + stdio-common/Makefile + (usual test differences) + +diff -Nrup a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2026-06-16 09:15:19.295653341 -0400 ++++ b/localedata/Makefile 2026-06-16 09:11:51.111414458 -0400 +@@ -160,6 +160,7 @@ tests = \ + bug-iconv-trans \ + bug-setlocale1 \ + bug-usesetlocale \ ++ tst-bz12701-lc \ + tst-c-utf8-consistency \ + tst-digits \ + tst-iconv-math-trans \ +diff --git a/localedata/tst-bz12701-lc.c b/localedata/tst-bz12701-lc.c +new file mode 100644 +index 0000000000..23c2ab7d2a +--- /dev/null ++++ b/localedata/tst-bz12701-lc.c +@@ -0,0 +1,218 @@ ++/* Verify scanf field width handling with the 'lc' conversion (BZ #12701). ++ Copyright (C) 2025-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 ++ ++/* Compare character-wise the initial part of the wide character object ++ pointed to by WS corresponding to wide characters obtained by the ++ conversion of first N bytes of the multibyte character object pointed ++ to by S. */ ++ ++static int ++tst_bz12701_lc_memcmp (const wchar_t *ds, const char *s, size_t n) ++{ ++ size_t nc = mbsnrtowcs (NULL, &s, n, 0, NULL); ++ ++ struct support_next_to_fault ntf; ++ ntf = support_next_to_fault_allocate (nc * sizeof (wchar_t)); ++ wchar_t *ss = (wchar_t *) ntf.buffer; ++ ++ mbsnrtowcs (ss, &s, n, nc, NULL); ++ int r = wmemcmp (ds, ss, nc); ++ ++ support_next_to_fault_free (&ntf); ++ ++ return r; ++} ++ ++/* Verify various aspects of field width handling, including the data ++ obtained, the number of bytes consumed, and the stream position. */ ++ ++static int ++do_test (void) ++{ ++ if (setlocale (LC_ALL, "pl_PL.UTF-8") == NULL) ++ FAIL_EXIT1 ("setlocale (LC_ALL, \"pl_PL.UTF-8\")"); ++ ++ /* Part of a tongue-twister in Polish, which says: ++ "On a rainy morning cuckoos and warblers, rather than starting ++ on earthworms, stuffed themselves fasted with the flesh of cress." */ ++ static const char s[126] = "Dżdżystym rankiem gżegżółki i piegże, " ++ "zamiast wziąć się za dżdżownice, " ++ "nażarły się na czczo miąższu rzeżuchy"; ++ ++ const char *sp = s; ++ size_t nc; ++ TEST_VERIFY_EXIT ((nc = mbsnrtowcs (NULL, &sp, sizeof (s), 0, NULL)) == 108); ++ ++ struct support_next_to_fault ntfo, ntfi; ++ ntfo = support_next_to_fault_allocate (nc * sizeof (wchar_t)); ++ ntfi = support_next_to_fault_allocate (sizeof (s)); ++ wchar_t *e = (wchar_t *) ntfo.buffer + nc; ++ char *b = ntfi.buffer; ++ ++ wchar_t *c; ++ FILE *f; ++ int ic; ++ int n; ++ int i; ++ ++ memcpy (ntfi.buffer, s, sizeof (s)); ++ ++ ic = i = 0; ++ f = xfmemopen (b, sizeof (s), "r"); ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ /* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */ ++ DIAG_PUSH_NEEDS_COMMENT; ++ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat"); ++ TEST_VERIFY_EXIT (fscanf (f, "%0lc%n", c, &n) == 1); ++ DIAG_POP_NEEDS_COMMENT; ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 1; ++ i += n; ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 2); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 1; ++ i += n; ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%1lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 1; ++ i += n; ++ ++ c = e - 2; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 3); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 2; ++ i += n; ++ ++ c = e - 4; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%4lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 4); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 4; ++ i += n; ++ ++ c = e - 8; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%8lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 8); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 8; ++ i += n; ++ ++ c = e - 16; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%16lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 20); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 16; ++ i += n; ++ ++ c = e - 32; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%32lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 38); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 32; ++ i += n; ++ ++ c = e - (nc - ic); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_COMPARE (fscanf (f, "%64lc%n", c, &n), 1); ++ TEST_COMPARE (n , 49); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, sizeof (s) - i) == 0); ++ ++ TEST_VERIFY_EXIT (ftell (f) == sizeof (s)); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ ic = i = 0; ++ f = xfmemopen (b, 3, "r"); ++ ++ c = e - 2; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 3); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 2; ++ i += n; ++ ++ c = e - (nc - ic); ++ TEST_VERIFY_EXIT (feof (f) == 0); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == EOF); ++ TEST_VERIFY_EXIT (n == 3); ++ ++ TEST_VERIFY_EXIT (ftell (f) == 3); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ ic = i = 0; ++ f = xfmemopen (b, 3, "r"); ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); ++ ic += 1; ++ i += n; ++ ++ c = e - (nc - ic); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 2); ++ TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, 3 - i) == 0); ++ ++ TEST_VERIFY_EXIT (ftell (f) == 3); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ support_next_to_fault_free (&ntfi); ++ support_next_to_fault_free (&ntfo); ++ ++ return 0; ++} ++ ++#include +diff -Nrup a/stdio-common/Makefile b/stdio-common/Makefile +--- a/stdio-common/Makefile 2026-06-16 09:15:19.320447732 -0400 ++++ b/stdio-common/Makefile 2026-06-16 09:14:09.814624522 -0400 +@@ -217,6 +217,7 @@ tests := \ + tllformat \ + tst-bz11319 \ + tst-bz11319-fortify2 \ ++ tst-bz12701-c \ + tst-cookie \ + tst-fclose-devzero \ + tst-fclose-offset \ +diff --git a/stdio-common/tst-bz12701-c.c b/stdio-common/tst-bz12701-c.c +new file mode 100644 +index 0000000000..4f3616fbfd +--- /dev/null ++++ b/stdio-common/tst-bz12701-c.c +@@ -0,0 +1,169 @@ ++/* Verify scanf field width handling with the 'c' conversion (BZ #12701). ++ Copyright (C) 2025-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 ++ ++/* Verify various aspects of field width handling, including the data ++ obtained, the number of bytes consumed, and the stream position. */ ++ ++static int ++do_test (void) ++{ ++ static const char s[43] = "The quick brown fox jumps over the lazy dog"; ++ struct support_next_to_fault ntfo, ntfi; ++ ntfo = support_next_to_fault_allocate (sizeof (s)); ++ ntfi = support_next_to_fault_allocate (sizeof (s)); ++ char *e = ntfo.buffer + sizeof (s); ++ char *b = ntfi.buffer; ++ ++ char *c; ++ FILE *f; ++ int n; ++ int i; ++ ++ memcpy (ntfi.buffer, s, sizeof (s)); ++ ++ i = 0; ++ f = xfmemopen (b, sizeof (s), "r"); ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ /* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */ ++ DIAG_PUSH_NEEDS_COMMENT; ++ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat"); ++ TEST_VERIFY_EXIT (fscanf (f, "%0c%n", c, &n) == 1); ++ DIAG_POP_NEEDS_COMMENT; ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%1c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 2; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 2); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 4; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%4c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 4); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 8; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%8c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 8); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 16; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%16c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 16); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - (sizeof (s) - i); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%32c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 10); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, sizeof (s) - i) == 0); ++ ++ TEST_VERIFY_EXIT (ftell (f) == sizeof (s)); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ i = 0; ++ f = xfmemopen (b, 3, "r"); ++ ++ c = e - 1; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - 2; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 2); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - (3 - i); ++ TEST_VERIFY_EXIT (feof (f) == 0); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == EOF); ++ TEST_VERIFY_EXIT (n == 2); ++ ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ i = 0; ++ f = xfmemopen (b, 3, "r"); ++ ++ c = e - 2; ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 2); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); ++ i += n; ++ ++ c = e - (3 - i); ++ TEST_VERIFY_EXIT (ftell (f) == i); ++ TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); ++ TEST_VERIFY_EXIT (n == 1); ++ TEST_VERIFY_EXIT (memcmp (c, s + i, 3 - i) == 0); ++ ++ TEST_VERIFY_EXIT (ftell (f) == 3); ++ TEST_VERIFY_EXIT (feof (f) != 0); ++ ++ xfclose (f); ++ ++ support_next_to_fault_free (&ntfi); ++ support_next_to_fault_free (&ntfo); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-172710-4.patch b/SOURCES/glibc-RHEL-172710-4.patch new file mode 100644 index 0000000..12a03cd --- /dev/null +++ b/SOURCES/glibc-RHEL-172710-4.patch @@ -0,0 +1,193 @@ +commit 6cebb0b80fd783e442a8ad27c3f52cde52a9cac7 +Author: DJ Delorie +Date: Wed May 27 12:57:10 2026 -0400 + + stdio-common: Allow partially-filled %mc buffers [BZ #12701] + + This is a backwards-compatible alternative to the main solution to + the %mc part of 12701. The allocated buffer is expanded to the + requested size and NUL padded, but truncated reads are allowed. + + Reviewed-by: Carlos O'Donell + +Conflicts: + localedata/Makefile + stdio-common/Makefile + (usual test differences) + +diff -Nrup a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2026-06-16 10:00:37.802495527 -0400 ++++ b/localedata/Makefile 2026-06-16 09:59:09.766677507 -0400 +@@ -161,6 +161,7 @@ tests = \ + bug-setlocale1 \ + bug-usesetlocale \ + tst-bz12701-lc \ ++ tst-bz12701-lc2 \ + tst-c-utf8-consistency \ + tst-digits \ + tst-iconv-math-trans \ +diff --git a/localedata/tst-bz12701-lc2.c b/localedata/tst-bz12701-lc2.c +new file mode 100644 +index 0000000000..b24e86df0b +--- /dev/null ++++ b/localedata/tst-bz12701-lc2.c +@@ -0,0 +1,47 @@ ++/* Verify scanf memory handling with the 'c' conversion (BZ #12701). ++ 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 ++ ++static int ++do_test (void) ++{ ++ wchar_t *c = NULL; ++ int i; ++ ++ TEST_VERIFY (sscanf ("1234", "%30mlc", &c) == 1); ++ ++ TEST_VERIFY (c != NULL); ++ TEST_COMPARE_BLOB (c, 5 * sizeof (wchar_t), ++ L"1234\0", 5 * sizeof (wchar_t)); ++ for (i = 5; i < 30; i ++) ++ TEST_VERIFY (c[i] == L'\0'); ++ ++ TEST_VERIFY (malloc_usable_size (c) >= 30 * sizeof(wchar_t)); ++ ++ return 0; ++} ++ ++#include +diff -Nrup a/stdio-common/Makefile b/stdio-common/Makefile +--- a/stdio-common/Makefile 2026-06-16 10:00:37.803143281 -0400 ++++ b/stdio-common/Makefile 2026-06-16 09:59:54.974421920 -0400 +@@ -218,6 +218,7 @@ tests := \ + tst-bz11319 \ + tst-bz11319-fortify2 \ + tst-bz12701-c \ ++ tst-bz12701-c2 \ + tst-cookie \ + tst-fclose-devzero \ + tst-fclose-offset \ +diff --git a/stdio-common/tst-bz12701-c2.c b/stdio-common/tst-bz12701-c2.c +new file mode 100644 +index 0000000000..5f9ca7c592 +--- /dev/null ++++ b/stdio-common/tst-bz12701-c2.c +@@ -0,0 +1,46 @@ ++/* Verify scanf memory handling with the 'c' conversion (BZ #12701). ++ 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 ++ ++static int ++do_test (void) ++{ ++ char *c = NULL; ++ int i; ++ ++ TEST_VERIFY (sscanf ("1234", "%30mc", &c) == 1); ++ ++ TEST_VERIFY (c != NULL); ++ TEST_COMPARE_BLOB (c, 5, "1234\0", 5); ++ for (i = 5; i < 30; i ++) ++ TEST_VERIFY (c[i] == '\0'); ++ ++ TEST_VERIFY (malloc_usable_size (c) >= 30); ++ ++ return 0; ++} ++ ++#include +diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c +index 17b5565d0f..90a1886951 100644 +--- a/stdio-common/vfscanf-internal.c ++++ b/stdio-common/vfscanf-internal.c +@@ -780,9 +780,9 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + conv_error (); \ + } while (0) + #ifdef COMPILE_WSCANF +- STRING_ARG (str, char, 100); ++ STRING_ARG (str, char, (width > 0 ? width : 1)); + #else +- STRING_ARG (str, char, (width > 1024 ? 1024 : width)); ++ STRING_ARG (str, char, (width > 0 ? width : 1)); + #endif + + c = inchar (); +@@ -891,6 +891,11 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + + if (!(flags & SUPPRESS)) + { ++ /* If the buffer isn't completely filled, pad it with NULs. */ ++ if (flags & MALLOC) ++ while (width-- > 0) ++ *str++ = '\0'; ++ + if ((flags & MALLOC) && str - *strptr != strsize) + { + char *cp = (char *) realloc (*strptr, str - *strptr); +@@ -908,7 +913,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + if (width == -1) + width = 1; + +- STRING_ARG (wstr, wchar_t, (width > 1024 ? 1024 : width)); ++ STRING_ARG (wstr, wchar_t, (width > 0 ? width : 1)); + + c = inchar (); + if (__glibc_unlikely (c == EOF)) +@@ -1044,6 +1049,11 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, + + if (!(flags & SUPPRESS)) + { ++ /* If the buffer isn't completely filled, pad it with NULs. */ ++ if (flags & MALLOC) ++ while (width-- > 0) ++ *wstr++ = L'\0'; ++ + if ((flags & MALLOC) && wstr - (wchar_t *) *strptr != strsize) + { + wchar_t *cp = (wchar_t *) realloc (*strptr, diff --git a/SOURCES/glibc-RHEL-178279.patch b/SOURCES/glibc-RHEL-178279.patch new file mode 100644 index 0000000..ded65f1 --- /dev/null +++ b/SOURCES/glibc-RHEL-178279.patch @@ -0,0 +1,117 @@ +commit e9325bd7d04aacc45cf39505e279b1ca9de22c08 +Author: Florian Weimer +Date: Tue Jun 9 07:28:02 2026 +0200 + + iconv: Suppress intermediate errors with //TRANSLIT (bug 34236) + + When tentatively converting characters on behalf of + __gconv_transliterate, do not create a persistent error. Just + produce a local error, and rely on __gconv_transliterate to + produce the error if all transliteration options are exhausted. + + This fixes transliteration of “½” to ASCII, which cannot use the + “ 1⁄2 ” alternative. Eventually, the “ 1/2 ” alternative is chosen, + but the error sticks. Therefore, iconv exited with status 1 before + this change. + + Adjust iconv/tst-iconv_prog.sh to test both C and en_US.UTF-8 locales. + This requires changing the way the ICONV template is defined, so that + run_program_env is evaluated multiple times. + + Fixes commit 9a4b0eaf726f5404c6683d5c7c5e86f61c3f3fbc ("iconv: do not + report error exit with transliteration [BZ #32448]"), + commit 6cbf845fcdc76131d0e674cee454fe738b69c69d ("iconv: Preserve + iconv -c error exit on invalid inputs (bug 32046)"), and bug 34236. + + Reviewed-by: Aurelien Jarno + +Conflicts: + iconv/Makefile + (adjust for slight context differences) + iconv/tst-iconv_prog.sh + (adjust for slight context differences) + +diff -Nrup a/iconv/Makefile b/iconv/Makefile +--- a/iconv/Makefile 2026-07-10 15:33:08.645711776 -0400 ++++ b/iconv/Makefile 2026-07-10 15:35:00.893038141 -0400 +@@ -130,7 +130,8 @@ $(objpfx)test-iconvconfig.out: $(objpfx) + rm -f $$tmp) > $@; \ + $(evaluate-test) + +-$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog ++$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog \ ++ $(gen-locales) + $(BASH) $< $(common-objdir) '$(test-wrapper-env)' \ + '$(run-program-env)' > $@; \ + $(evaluate-test) +diff --git a/iconv/loop.c b/iconv/loop.c +index 27dea1f5d6..81841d0d2d 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -144,8 +144,10 @@ + if (irreversible == NULL) \ + { \ + /* This means we are in call from __gconv_transliterate. In this \ +- case we are not doing any error recovery ourselves. */ \ +- result = __gconv_mark_illegal_input (step_data); \ ++ case we are not doing any error recovery ourselves. Do not create \ ++ a persistent error state. If __gconv_transliterate exhausts all \ ++ alternatives, it will call __gconv_mark_illegal_input itself. */ \ ++ result = __GCONV_ILLEGAL_INPUT; \ + break; \ + } \ + \ +diff -Nrup a/iconv/tst-iconv_prog.sh b/iconv/tst-iconv_prog.sh +--- a/iconv/tst-iconv_prog.sh 2026-07-15 11:10:33.321344932 -0400 ++++ b/iconv/tst-iconv_prog.sh 2026-07-15 11:16:10.328740778 -0400 +@@ -27,10 +27,10 @@ LIBPATH=$codir:$codir/iconvdata + + # How the start the iconv(1) program. $from is not defined/expanded yet. + ICONV=' ++$test_wrapper_env $run_program_env + $codir/elf/ld.so --library-path $LIBPATH --inhibit-rpath ${from}.so + $codir/iconv/iconv_prog + ' +-ICONV="$test_wrapper_env $run_program_env $ICONV" + + # List of known hangs; + # Gathered by running an exhaustive 2 byte input search against glibc-2.28 +@@ -216,6 +216,7 @@ testarray=( + "\x00\x00;;INVALID;UTF-8;1" + "\x00\x00;;UTF-8;INVALID;1" + "\xc3\xa9;;UTF-8;ASCII//TRANSLIT;0" ++"X\xc2\xbdY;;UTF-8;ASCII//TRANSLIT;0" + ) + + # Requires $twobyte input, $c flag, $from, and $to to be set; sets $ret +@@ -275,12 +276,21 @@ check_errtest_result () + fi + } + +-for testcommand in "${testarray[@]}"; do +- twobyte="$(echo "$testcommand" | cut -d";" -f 1)" +- c="$(echo "$testcommand" | cut -d";" -f 2)" +- from="$(echo "$testcommand" | cut -d";" -f 3)" +- to="$(echo "$testcommand" | cut -d";" -f 4)" +- eret="$(echo "$testcommand" | cut -d";" -f 5)" +- execute_test +- check_errtest_result +-done ++run_test_array () ++{ ++ for testcommand in "${testarray[@]}"; do ++ twobyte="$(echo "$testcommand" | cut -d";" -f 1)" ++ c="$(echo "$testcommand" | cut -d";" -f 2)" ++ from="$(echo "$testcommand" | cut -d";" -f 3)" ++ to="$(echo "$testcommand" | cut -d";" -f 4)" ++ eret="$(echo "$testcommand" | cut -d";" -f 5)" ++ execute_test ++ check_errtest_result ++ done ++} ++ ++echo "info: testing C locale" ++run_test_array ++echo "info: testing en_US.UTF-8 locale" ++run_program_env="$run_program_env LC_ALL=en_US.UTF-8" ++run_test_array diff --git a/SOURCES/glibc-RHEL-180336-1.patch b/SOURCES/glibc-RHEL-180336-1.patch new file mode 100644 index 0000000..9894622 --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-1.patch @@ -0,0 +1,47 @@ +commit 360f352c9a6da545d798ef3015e73ca114f0d230 +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Declare __p_class_syms, __p_type_syms for internal use + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/include/resolv.h b/include/resolv.h +index 4dbbac3800..d5ad9994b9 100644 +--- a/include/resolv.h ++++ b/include/resolv.h +@@ -70,6 +70,11 @@ libc_hidden_proto (__libc_res_nameinquery) + extern __typeof (__res_queriesmatch) __libc_res_queriesmatch; + libc_hidden_proto (__libc_res_queriesmatch) + ++extern const struct res_sym __p_class_syms[]; ++libresolv_hidden_proto (__p_class_syms) ++extern const struct res_sym __p_type_syms[]; ++libresolv_hidden_proto (__p_type_syms) ++ + /* Variant of res_hnok which operates on binary (but uncompressed) names. */ + bool __res_binary_hnok (const unsigned char *dn) attribute_hidden; + +diff --git a/resolv/res_debug.c b/resolv/res_debug.c +index 73af0c72fe..6bf9962916 100644 +--- a/resolv/res_debug.c ++++ b/resolv/res_debug.c +@@ -390,8 +390,6 @@ p_fqname(const u_char *cp, const u_char *msg, FILE *file) { + * that C_ANY is a qclass but not a class. (You can ask for records of class + * C_ANY, but you can't have any records of that class in the database.) + */ +-extern const struct res_sym __p_class_syms[]; +-libresolv_hidden_proto (__p_class_syms) + const struct res_sym __p_class_syms[] = { + {C_IN, (char *) "IN"}, + {C_CHAOS, (char *) "CHAOS"}, +@@ -426,8 +424,6 @@ const struct res_sym __p_update_section_syms[] attribute_hidden = { + * Names of RR types and qtypes. The list is incomplete because its + * size is part of the ABI. + */ +-extern const struct res_sym __p_type_syms[]; +-libresolv_hidden_proto (__p_type_syms) + const struct res_sym __p_type_syms[] = { + {ns_t_a, (char *) "A", (char *) "address"}, + {ns_t_ns, (char *) "NS", (char *) "name server"}, diff --git a/SOURCES/glibc-RHEL-180336-2.patch b/SOURCES/glibc-RHEL-180336-2.patch new file mode 100644 index 0000000..2f90da8 --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-2.patch @@ -0,0 +1,70 @@ +commit f69b7f95e3694177546faec25d88bb266885c3b8 +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Fix ns_sprintrrf formatting of class, type values (bug 34289) + + The p_class and p_type results could overwrite each other if both + were unknown. Format unknown values with CLASS and TYPE prefixes, + as in RFC 3597. Handle A6 separately because it cannot be added + to __p_type_syms for ABI reasons. + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/resolv/ns_print.c b/resolv/ns_print.c +index cef2212fd2..e75c39eaa8 100644 +--- a/resolv/ns_print.c ++++ b/resolv/ns_print.c +@@ -78,6 +78,24 @@ ns_sprintrr(const ns_msg *handle, const ns_rr *rr, + } + libresolv_hidden_def (ns_sprintrr) + ++/* Writes the class/type symbol NUMBER to *BUF, using the name from ++ *SYMS if possible. If NUMBER is not found in *SYMS, print the ++ number with PREFIX. */ ++static int ++addsym (const struct res_sym *syms, int number, const char *prefix, ++ char **buf, size_t *buflen) ++{ ++ for (; syms->name != NULL; syms++) ++ if (number == syms->number) ++ { ++ T (addstr (" ", 1, buf, buflen)); ++ return addstr (syms->name, strlen (syms->name), buf, buflen); ++ } ++ char tmp[20]; ++ int len = snprintf (tmp, sizeof (tmp), " %s%d", prefix, number); ++ return addstr (tmp, len, buf, buflen); ++} ++ + /*% + * Convert the fields of an RR into presentation format. + * +@@ -128,11 +146,21 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + /* + * TTL, Class, Type. + */ +- T(x = ns_format_ttl(ttl, buf, buflen)); +- addlen(x, &buf, &buflen); +- len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); +- T(addstr(tmp, len, &buf, &buflen)); +- T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); ++ { ++ char *start = buf; ++ ++ T (x = ns_format_ttl (ttl, buf, buflen)); ++ addlen (x, &buf, &buflen); ++ T (addsym (__p_class_syms, class, "CLASS", &buf, &buflen)); ++ if (type == ns_t_a6) ++ /* A6 is not part of __p_type_syms, which is exported. ++ Adding A6 there would change its size. Handle it here. */ ++ T (addstr (" A6", 3, &buf, &buflen)); ++ else ++ T (addsym (__p_type_syms, type, "TYPE", &buf, &buflen)); ++ ++ T (spaced = addtab(buf - start, 16, spaced, &buf, &buflen)); ++ } + + /* + * RData. diff --git a/SOURCES/glibc-RHEL-180336-3.patch b/SOURCES/glibc-RHEL-180336-3.patch new file mode 100644 index 0000000..cc495bc --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-3.patch @@ -0,0 +1,48 @@ +commit d58415eb17d457a160af99f9e8ab164404ca151b +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Improve formatting of unknown records in ns_sprintrrf + + Do not add the "unknown RR type" comment. After adding the TYPE + prefix, the number is largely redundant. + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/resolv/ns_print.c b/resolv/ns_print.c +index e75c39eaa8..3d38876483 100644 +--- a/resolv/ns_print.c ++++ b/resolv/ns_print.c +@@ -115,7 +115,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + + const char *comment; + char tmp[100]; +- char errbuf[40]; + int len, x; + + /* +@@ -590,20 +589,18 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + T(addstr(tmp, len, &buf, &buflen)); + break; + } +- + default: +- snprintf (errbuf, sizeof (errbuf), "unknown RR type %d", type); +- comment = errbuf; ++ comment = ""; + goto hexify; + } + return (buf - obuf); + formerr: +- comment = "RR format error"; ++ comment = " ; RR format error"; + hexify: { + int n, m; + char *p; + +- len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata), ++ len = SPRINTF((tmp, "\\# %u%s%s", (unsigned)(edata - rdata), + rdlen != 0U ? " (" : "", comment)); + T(addstr(tmp, len, &buf, &buflen)); + while (rdata < edata) { diff --git a/SOURCES/glibc-RHEL-180336-4.patch b/SOURCES/glibc-RHEL-180336-4.patch new file mode 100644 index 0000000..acef669 --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-4.patch @@ -0,0 +1,63 @@ +commit cd0db208d56a2cecd528b8ae96df752ba5344d9a +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Check for inet_ntop failure in ns_sprintrrf + + This makes the output more consistent (either failure or complete + output) and helps with systematic testing with varying buffer + sizes. + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/resolv/ns_print.c b/resolv/ns_print.c +index 3d38876483..e58df5f35a 100644 +--- a/resolv/ns_print.c ++++ b/resolv/ns_print.c +@@ -167,8 +167,9 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + switch (type) { + case ns_t_a: + if (rdlen != (size_t)NS_INADDRSZ) +- goto formerr; +- (void) inet_ntop(AF_INET, rdata, buf, buflen); ++ goto formerr; ++ if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL) ++ return -1; + addlen(strlen(buf), &buf, &buflen); + break; + +@@ -334,9 +335,10 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + } + + case ns_t_aaaa: +- if (rdlen != (size_t)NS_IN6ADDRSZ) +- goto formerr; +- (void) inet_ntop(AF_INET6, rdata, buf, buflen); ++ if (rdlen != (size_t)NS_IN6ADDRSZ) ++ goto formerr; ++ if (inet_ntop (AF_INET6, rdata, buf, buflen) == NULL) ++ return -1; + addlen(strlen(buf), &buf, &buflen); + break; + +@@ -427,7 +429,8 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + goto formerr; + + /* Address. */ +- (void) inet_ntop(AF_INET, rdata, buf, buflen); ++ if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL) ++ return -1; + addlen(strlen(buf), &buf, &buflen); + rdata += NS_INADDRSZ; + +@@ -569,7 +572,8 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + if (rdata + pbyte >= edata) goto formerr; + memset(&a, 0, sizeof(a)); + memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); +- (void) inet_ntop(AF_INET6, &a, buf, buflen); ++ if (inet_ntop (AF_INET6, &a, buf, buflen) == NULL) ++ return -1; + addlen(strlen(buf), &buf, &buflen); + rdata += sizeof(a) - pbyte; + } diff --git a/SOURCES/glibc-RHEL-180336-5.patch b/SOURCES/glibc-RHEL-180336-5.patch new file mode 100644 index 0000000..a019f27 --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-5.patch @@ -0,0 +1,130 @@ +commit ca44a6609c29a683b03575fa035c6d17aa591e72 +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: More types as unknown in ns_sprintrrf (CVE-2026-5435) + + Specifically, CERT, TKEY, TSIG, OPT. This removes the buggy + implementations of TSIG, fixing bug 34033, and partially + fixing bug 34069. + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +Conflicts: + resolv/ns_print.c + Account for typo fix in 7f0d9e61f40c + +diff --git a/resolv/ns_print.c b/resolv/ns_print.c +index e58df5f35a..ab68bf2cb7 100644 +--- a/resolv/ns_print.c ++++ b/resolv/ns_print.c +@@ -464,96 +464,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + break; + } + +- case ns_t_cert: { +- u_int c_type, key_tag, alg; +- int n; +- unsigned int siz; +- char base64_cert[8192], tmp[40]; +- const char *leader; +- +- c_type = ns_get16(rdata); rdata += NS_INT16SZ; +- key_tag = ns_get16(rdata); rdata += NS_INT16SZ; +- alg = (u_int) *rdata++; +- +- len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); +- T(addstr(tmp, len, &buf, &buflen)); +- siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ +- if (siz > sizeof(base64_cert) * 3/4) { +- const char *str = "record too long to print"; +- T(addstr(str, strlen(str), &buf, &buflen)); +- } +- else { +- len = b64_ntop(rdata, edata-rdata, base64_cert, siz); +- +- if (len < 0) +- goto formerr; +- else if (len > 15) { +- T(addstr(" (", 2, &buf, &buflen)); +- leader = "\n\t\t"; +- spaced = 0; +- } +- else +- leader = " "; +- +- for (n = 0; n < len; n += 48) { +- T(addstr(leader, strlen(leader), +- &buf, &buflen)); +- T(addstr(base64_cert + n, MIN(len - n, 48), +- &buf, &buflen)); +- } +- if (len > 15) +- T(addstr(" )", 2, &buf, &buflen)); +- } +- break; +- } +- +- case ns_t_tkey: { +- /* KJD - need to complete this */ +- u_long t; +- int mode, err, keysize; +- +- /* Algorithm name. */ +- T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); +- T(addstr(" ", 1, &buf, &buflen)); +- +- /* Inception. */ +- t = ns_get32(rdata); rdata += NS_INT32SZ; +- len = SPRINTF((tmp, "%lu ", t)); +- T(addstr(tmp, len, &buf, &buflen)); +- +- /* Experation. */ +- t = ns_get32(rdata); rdata += NS_INT32SZ; +- len = SPRINTF((tmp, "%lu ", t)); +- T(addstr(tmp, len, &buf, &buflen)); +- +- /* Mode , Error, Key Size. */ +- /* Priority, Weight, Port. */ +- mode = ns_get16(rdata); rdata += NS_INT16SZ; +- err = ns_get16(rdata); rdata += NS_INT16SZ; +- keysize = ns_get16(rdata); rdata += NS_INT16SZ; +- len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize)); +- T(addstr(tmp, len, &buf, &buflen)); +- +- /* XXX need to dump key, print otherdata length & other data */ +- break; +- } +- +- case ns_t_tsig: { +- /* BEW - need to complete this */ +- int n; +- +- T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); +- T(addstr(" ", 1, &buf, &buflen)); +- rdata += 8; /*%< time */ +- n = ns_get16(rdata); rdata += INT16SZ; +- rdata += n; /*%< sig */ +- n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */ +- sprintf(buf, "%d", ns_get16(rdata)); +- rdata += INT16SZ; +- addlen(strlen(buf), &buf, &buflen); +- break; +- } +- + case ns_t_a6: { + struct in6_addr a; + int pbyte, pbit; +@@ -588,11 +498,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + break; + } + +- case ns_t_opt: { +- len = SPRINTF((tmp, "%u bytes", class)); +- T(addstr(tmp, len, &buf, &buflen)); +- break; +- } + default: + comment = ""; + goto hexify; diff --git a/SOURCES/glibc-RHEL-180336-6.patch b/SOURCES/glibc-RHEL-180336-6.patch new file mode 100644 index 0000000..1222ad9 --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-6.patch @@ -0,0 +1,59 @@ +commit a7b60d23bbb56eaef59f4962e4140062e552600a +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Fix buffer overreads in ns_sprintrrf (CVE-2026-6238) + + Check that the RDATA payload does not require more than RDATALEN + bytes while processing it. The fixes cover A6, LOC records. + (CERT, TKEY, TSIG were fixed before, by switching to the generic + formatter.) + + The vulnerable LOC record handling was first introduced before + glibc 2.0, in commit ee188d555b8c32ad9704a7440cab400af967292f. + + CERT, TSIG, TKEY handling came with commit + b43b13ac2544b11f35be301d1589b51a8473e32b, released with glibc 2.2. + + A6 record handling was introduced in commit + 91633816430e7ec5a19fe3ff510a7c4822a9557e ("* resolv/ns_print.c + (ns_sprintrrf): Handle ns_t_a6 and ns_t_opt."), which went into glibc + 2.7. + + This fixes bug 34069. + + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/resolv/ns_print.c b/resolv/ns_print.c +index ab68bf2cb7..f9dd086804 100644 +--- a/resolv/ns_print.c ++++ b/resolv/ns_print.c +@@ -345,7 +345,8 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + case ns_t_loc: { + char t[255]; + +- /* XXX protocol format checking? */ ++ if (rdlen != 16) ++ goto formerr; + (void) loc_ntoa(rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; +@@ -479,13 +480,14 @@ ns_sprintrrf(const u_char *msg, size_t msglen, + + /* address suffix: provided only when prefix len != 128 */ + if (pbit < 128) { +- if (rdata + pbyte >= edata) goto formerr; ++ unsigned int bytelen = sizeof(a) - pbyte; ++ if (edata - rdata < bytelen) goto formerr; + memset(&a, 0, sizeof(a)); +- memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); ++ memcpy(&a.s6_addr[pbyte], rdata, bytelen); + if (inet_ntop (AF_INET6, &a, buf, buflen) == NULL) + return -1; + addlen(strlen(buf), &buf, &buflen); +- rdata += sizeof(a) - pbyte; ++ rdata += bytelen; + } + + /* prefix name: provided only when prefix len > 0 */ diff --git a/SOURCES/glibc-RHEL-180336-7.patch b/SOURCES/glibc-RHEL-180336-7.patch new file mode 100644 index 0000000..3250e7b --- /dev/null +++ b/SOURCES/glibc-RHEL-180336-7.patch @@ -0,0 +1,371 @@ +commit 4ba0b79b9596e5a4951cc9eaa1546a55e543e083 +Author: Florian Weimer +Date: Fri Jun 19 18:22:20 2026 +0200 + + resolv: Add test case tst-ns_sprintrr (bug 34033, bug 34069) + + This test case covers both input buffer overreads and output buffer + overflows. It should systematically cover these issues. + + I used code auto-generation for updating the test expectations for + truncated RDATA in TXT, ISDN records, after writing the rest + of the test by hand. + + Assisted-by: LLM + Reviewed-by: Carlos O'Donell + Reviewed-by: Adhemerval Zanella + +diff --git a/resolv/Makefile b/resolv/Makefile +index 68b3a4dbf3..02cc751732 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -108,6 +108,7 @@ tests += \ + tst-ns_name \ + tst-ns_name_compress \ + tst-ns_name_pton \ ++ tst-ns_sprintrr \ + tst-res_hconf_reorder \ + tst-res_hnok \ + tst-resolv-aliases \ +@@ -341,5 +342,6 @@ $(objpfx)tst-ns_name: $(objpfx)libresolv.so + $(objpfx)tst-ns_name.out: tst-ns_name.data + $(objpfx)tst-ns_name_compress: $(objpfx)libresolv.so + $(objpfx)tst-ns_name_pton: $(objpfx)libresolv.so ++$(objpfx)tst-ns_sprintrr: $(objpfx)libresolv.so + $(objpfx)tst-res_hnok: $(objpfx)libresolv.so + $(objpfx)tst-p_secstodate: $(objpfx)libresolv.so +diff --git a/resolv/tst-ns_sprintrr.c b/resolv/tst-ns_sprintrr.c +new file mode 100644 +index 0000000000..34739b5924 +--- /dev/null ++++ b/resolv/tst-ns_sprintrr.c +@@ -0,0 +1,329 @@ ++/* Tests for the ns_sprintrr function. ++ 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 ++ ++/* Regions that test_one_record uses for input and output. */ ++static struct support_next_to_fault ntf_in; ++static struct support_next_to_fault ntf_out; ++ ++/* This is used by test_one_record to construct the packet. */ ++static const char packet_prefix[] = ++ /* DNS response with one question, one answer record. */ ++ "AA\x81\x80\0\1\0\1\0\0\0\0" ++ /* Question: www.example.org/IN/ANY. */ ++ "\3www\7example\3org\0\0\xff\0\1" ++ /* Response: compression reference. */ ++ "\xc0\x0c"; ++ ++/* Use ns_sprintrr to format a DNS record (starting with ++ packet_prefix) of type RTYPE, with a record payload of RDATALEN ++ bytes starting at RDATA. Check successful formatting against ++ EXPECTED. Try various truncated input and output buffers to catch ++ overreads and buffer overflows, using ntf_in and ntf_out above. */ ++static void ++test_one_record (uint16_t rtype, const char *rdata, size_t rdatalen, ++ const char *expected) ++{ ++ struct rr_header ++ { ++ uint16_t typ; ++ uint16_t cls; ++ uint32_t ttl; ++ uint16_t rdatalen; ++ uint16_t pad; ++ } hdr = ++ { ++ .typ = htons (rtype), ++ .cls = htons (ns_c_in), ++ .ttl = htonl (86400), /* One day. */ ++ .rdatalen = htons (rdatalen), ++ }; ++ enum { hdrlen = offsetof (struct rr_header, pad) }; ++ TEST_COMPARE (hdrlen, 10); ++ ++ /* Construct the packet from packet_prefix, hdr, and rdata. */ ++ unsigned char packet[512]; ++ size_t packetlen; ++ { ++ struct alloc_buffer buf = alloc_buffer_create (packet, sizeof (packet)); ++ alloc_buffer_copy_bytes (&buf, packet_prefix, sizeof (packet_prefix) - 1); ++ alloc_buffer_copy_bytes (&buf, &hdr, hdrlen); ++ alloc_buffer_copy_bytes (&buf, rdata, rdatalen); ++ packetlen = sizeof (packet) - alloc_buffer_size (&buf); ++ } ++ ++ /* Parse the record. */ ++ ns_msg msg; ++ TEST_COMPARE (ns_initparse (packet, packetlen, &msg), 0); ++ ns_rr rr; ++ TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0); ++ ++ /* Try sizes up to this limit. Go a bit beyond the expected size to ++ check for errors. */ ++ size_t max_result_size = strlen (expected) + 16; ++ ++ bool success = false; ++ for (size_t result_size = 1; result_size <= max_result_size; ++result_size) ++ { ++ char *result_start = ntf_out.buffer + ntf_out.length - result_size; ++ memset (result_start, 'X', result_size); ++ ++ /* ns_sprintrr was deprecated in 2.34. */ ++ DIAG_PUSH_NEEDS_COMMENT; ++ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations"); ++ int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size); ++ DIAG_POP_NEEDS_COMMENT; ++ ++ if (ret > 0) ++ { ++ TEST_COMPARE_STRING (result_start, expected); ++ TEST_COMPARE (ret, strlen (expected)); ++ success = true; ++ } ++ else ++ { ++ TEST_VERIFY (!success); ++ TEST_COMPARE (ret, -1); ++ } ++ } ++ TEST_VERIFY (success); ++ ++ /* Test with truncated RDATA. */ ++ for (size_t rdata_size = 0; rdata_size <= rdatalen; ++rdata_size) ++ { ++ size_t truncated_packet_size = packetlen - rdatalen + rdata_size; ++ unsigned char *packet_start ++ = ((unsigned char *) ntf_in.buffer + ntf_in.length ++ - truncated_packet_size); ++ memcpy (packet_start, packet, truncated_packet_size); ++ /* Patch in the updated RDATA length field. */ ++ uint16_t new_rdatalen = htons (rdata_size); ++ memcpy (packet_start + truncated_packet_size - rdata_size - 2, ++ &new_rdatalen, 2); ++ ++ ns_msg msg; ++ TEST_COMPARE (ns_initparse (packet_start, truncated_packet_size, &msg), ++ 0); ++ ns_rr rr; ++ TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0); ++ ++ size_t result_size = strlen (expected) + 1; ++ char *result_start = ntf_out.buffer + ntf_out.length - result_size; ++ memset (result_start, 'X', result_size); ++ ++ /* ns_sprintrr was deprecated in 2.34. */ ++ DIAG_PUSH_NEEDS_COMMENT; ++ DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations"); ++ int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size); ++ DIAG_POP_NEEDS_COMMENT; ++ ++ /* This flag indicates whether the output is syntactically ++ correct. In some cases, truncation may still yield a valid ++ payload. */ ++ bool broken = rdata_size < rdatalen; ++ switch (rtype) ++ { ++ case ns_t_wks: ++ /* WKS records use all trailing bytes for the port bitmap. */ ++ broken = rdata_size < 5; ++ break; ++ case ns_t_nsap: ++ /* Uses all bytes that are available. */ ++ broken = false; ++ break; ++ case ns_t_txt: ++ /* Truncation produces a valid payload if it occurs right ++ after a complete string in the TXT payload. */ ++ broken = false; ++ for (size_t pos = 0; pos < rdata_size; ) ++ { ++ unsigned int slen = rdata[pos] & 0xff; ++ if (pos + 1 + slen > rdata_size) ++ { ++ broken = true; ++ break; ++ } ++ pos += 1 + slen; ++ } ++ break; ++ case ns_t_isdn: ++ /* The second field is optional. If it is present, it must ++ not be truncated. */ ++ broken = rdata_size < 6 || (rdata_size > 6 && rdata_size < rdatalen); ++ break; ++ case ns_t_a6: ++ /* The first A6 subtest contains a trailing domain name, ++ which is ignored and not formatted. */ ++ if (rdata_size > 0 && rdata[0] == 0) ++ broken = rdata_size < 17; ++ break; ++ case ns_t_cert: ++ case ns_t_tkey: ++ case ns_t_tsig: ++ /* Only generic printing, which does not validate anything. */ ++ broken = false; ++ break; ++ } ++ ++ if (broken) ++ { ++ if (strstr (result_start, "RR format error") != NULL) ++ /* No further checks if an error indicator has been added ++ to the output. */ ++ ; ++ else ++ TEST_COMPARE (ret, -1); ++ } ++ else ++ TEST_VERIFY (ret > 0); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ ntf_in = support_next_to_fault_allocate (512); ++ ntf_out = support_next_to_fault_allocate (256); ++ ++#define T(rtype, rdata, expected) \ ++ test_one_record (rtype, rdata, sizeof (rdata) - 1, expected) ++ T (ns_t_a, "\xc0\0\2\1", "www.example.org.\t1D IN A\t\t192.0.2.1"); ++ T (ns_t_cname, "\4www1\4prod\xc0\x10", ++ "www.example.org.\t1D IN CNAME\twww1.prod.example.org."); ++ T (ns_t_hinfo, "\5first\6second", ++ "www.example.org.\t1D IN HINFO\t\"first\" \"second\""); ++ T (ns_t_isdn, "\5first\6second", ++ "www.example.org.\t1D IN ISDN\t\"first\" \"second\""); ++ /* Bug: Extra space at the end in the text representation of ISDN RRs. */ ++ T (ns_t_isdn, "\5first", "www.example.org.\t1D IN ISDN\t\"first\" "); ++ T (ns_t_soa, ++ "\2ns\xc0\x10\12hostmaster\xc0\x10" ++ "\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5", ++ "www.example.org.\t1D IN SOA\tns.example.org. hostmaster.example.org. (\n" ++ "\t\t\t\t\t1\t\t; serial\n" ++ "\t\t\t\t\t2S\t\t; refresh\n" ++ "\t\t\t\t\t3S\t\t; retry\n" ++ "\t\t\t\t\t4S\t\t; expiry\n" ++ "\t\t\t\t\t5S )\t\t; minimum\n"); ++ T (ns_t_mx, "\0\xa\2mx\xc0\x10", ++ "www.example.org.\t1D IN MX\t10 mx.example.org."); ++ T (ns_t_px, "\0\xa\3px1\xc0\x10\3px2\xc0\x10", ++ "www.example.org.\t1D IN PX\t10 px1.example.org. px2.example.org."); ++ T (ns_t_x25, "\4X.25", ++ "www.example.org.\t1D IN X25\t\"X.25\""); ++ T (ns_t_txt, "\1A\2BC\3DEF", ++ "www.example.org.\t1D IN TXT\t\"A\" \"BC\" \"DEF\""); ++ T (ns_t_nsap, "", ++ "www.example.org.\t1D IN NSAP\t"); ++ T (ns_t_nsap, "\1", ++ "www.example.org.\t1D IN NSAP\t01"); ++ T (ns_t_nsap, "\1\2", ++ "www.example.org.\t1D IN NSAP\t01.02"); ++ T (ns_t_nsap, "\1\2\3", ++ "www.example.org.\t1D IN NSAP\t01.0203"); ++ T (ns_t_nsap, "\1\2\3\4", ++ "www.example.org.\t1D IN NSAP\t01.0203.04"); ++ T (ns_t_nsap, ++ "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32" ++ "\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61" ++ "\62\63\64\65\66\67\70\71\72\73\74\75\76\77\100\101\102\103\104\105\106" ++ "\107\110\111\112\113\114\115\116\117\120\121\122\123\124\125\126\127" ++ "\130\131\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150" ++ "\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171" ++ "\172\173\174\175\176\177\200\201\202\203\204\205\206\207\210\211\212" ++ "\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233" ++ "\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254" ++ "\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275" ++ "\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316" ++ "\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337" ++ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360" ++ "\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377", ++ "www.example.org.\t1D IN NSAP\t" ++ "01.0203.0405.0607.0809.0A0B.0C0D.0E0F.1011.1213.1415.1617.1819.1A1B" ++ ".1C1D.1E1F.2021.2223.2425.2627.2829.2A2B.2C2D.2E2F.3031.3233.3435.3637" ++ ".3839.3A3B.3C3D.3E3F.4041.4243.4445.4647.4849.4A4B.4C4D.4E4F.5051.5253" ++ ".5455.5657.5859.5A5B.5C5D.5E5F.6061.6263.6465.6667.6869.6A6B.6C6D.6E6F" ++ ".7071.7273.7475.7677.7879.7A7B.7C7D.7E7F.8081.8283.8485.8687.8889.8A8B" ++ ".8C8D.8E8F.9091.9293.9495.9697.9899.9A9B.9C9D.9E9F.A0A1.A2A3.A4A5.A6A7" ++ ".A8A9.AAAB.ACAD.AEAF.B0B1.B2B3.B4B5.B6B7.B8B9.BABB.BCBD.BEBF.C0C1.C2C3" ++ ".C4C5.C6C7.C8C9.CACB.CCCD.CECF.D0D1.D2D3.D4D5.D6D7.D8D9.DADB.DCDD.DEDF" ++ ".E0E1.E2E3.E4E5.E6E7.E8E9.EAEB.ECED.EEEF.F0F1.F2F3.F4F5.F6F7.F8F9.FAFB" ++ ".FCFD.FEFF"); ++ T (ns_t_aaaa, "\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34", ++ "www.example.org.\t1D IN AAAA\t2001:db8::1234"); ++ /* Example from RFC 1876. The loc_ntoa format is different from the ++ official text representation. */ ++ T (ns_t_loc, ++ "\000\063\026\023\211\027\055\320\160\276\025\360\000\230\215\040", ++ "www.example.org.\t1D IN LOC" ++ "\t42 21 54.000 N 71 06 18.000 W -24.00m 30.00m 10000.00m 10.00m"); ++ T (ns_t_naptr, ++ "\0\1\0\2\5flags\7service\2.*\5naptr\xc0\x10", ++ "www.example.org.\t1D IN NAPTR\t1 2 \"flags\" \"service\" \".*\"" ++ " naptr.example.org."); ++ T (ns_t_srv, ++ "\0\1\0\2\0\x50\4www1\xc0\x10", ++ "www.example.org.\t1D IN SRV\t1 2 80 www1.example.org."); ++ T (ns_t_rp, "\3rp1\xc0\x10\3rp2\xc0\x10", ++ "www.example.org.\t1D IN RP\trp1.example.org. rp2.example.org."); ++ T (ns_t_wks, "\xc0\0\2\1\6\0\0\0\0\0\0\0\0\0\0\200", ++ "www.example.org.\t1D IN WKS\t192.0.2.1 6 ( \n\t\t\t\t80 )"); ++ T (ns_t_cert, "\0\1\x04\xd2\0blob", ++ "www.example.org.\t1D IN CERT\t\\# 9 (\n" ++ "\t00 01 04 d2 00 62 6c 6f 62 )\t\t\t; .....blob"); ++ T (ns_t_tkey, "\4algo\0\0\0\0\1\0\0\0\2\0\3\0\4" ++ "\0\5\xa1\xa2\xa3\xa4\xa5\0\3\xb1\xb2\xb3", ++ "www.example.org.\t1D IN TYPE249\t\\# 30 (\n" ++ "\t04 61 6c 67 6f 00 00 00 00 01 00 00 00 02 00 03 ; .algo...........\n" ++ "\t00 04 00 05 a1 a2 a3 a4 a5 00 03 b1 b2 b3 )\t; .............."); ++ T (ns_t_tsig, "\4algo\0" ++ "\0\20\xdd\xcd\x64\x10\xe9\x21\x34\x1a\x8e\xe0\xa1\x9a\x30\xfc\x3b\xd1" ++ "\0\2\0\3\0\5other", ++ "www.example.org.\t1D IN TSIG\t\\# 35 (\n" ++ "\t04 61 6c 67 6f 00 00 10 dd cd 64 10 e9 21 34 1a ; .algo.....d..!4.\n" ++ "\t8e e0 a1 9a 30 fc 3b d1 00 02 00 03 00 05 6f 74 ; ....0.;.......ot\n" ++ "\t68 65 72 )\t\t\t\t\t; her"); ++ T (ns_t_a6, ++ "\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34\6prefix\xc0\x10", ++ "www.example.org.\t1D IN A6\t0 2001:db8::1234"); ++ T (ns_t_a6, ++ "\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x35", ++ "www.example.org.\t1D IN A6\t0 2001:db8::1235"); ++ T (ns_t_a6, "\200\6prefix\xc0\x10", ++ "www.example.org.\t1D IN A6\t128 prefix.example.org."); ++ T (ns_t_a6, "\x20\0\0\0\0\0\0\0\0\0\0\x12\x36\6prefix\xc0\x10", ++ "www.example.org.\t1D IN A6\t32 ::1236 prefix.example.org."); ++#undef T ++ ++ support_next_to_fault_free (&ntf_in); ++ support_next_to_fault_free (&ntf_out); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-180340.patch b/SOURCES/glibc-RHEL-180340.patch new file mode 100644 index 0000000..0771d0a --- /dev/null +++ b/SOURCES/glibc-RHEL-180340.patch @@ -0,0 +1,102 @@ +commit ef3bfb5f910011f3780cb06aa47e730035f53285 +Author: Rocket Ma +Date: Fri May 1 20:39:07 2026 -0700 + + libio: Fix ungetwc operating on byte stream [BZ #33998] + + * libio/wgenops.c: When _IO_wdefault_pbackfail attempts to push back one + character, it accidently compare the wchar to push back with the last + char from byte stream, instead of wide stream. Under specific coding, + attacker may exploit this to leak information. This commit fix bug + 33998, or CVE-2026-5928. + + Signed-off-by: Rocket Ma + Reviewed-by: Carlos O'Donell + +diff --git a/libio/Makefile b/libio/Makefile +index 93656466df..6e0627bb88 100644 +--- a/libio/Makefile ++++ b/libio/Makefile +@@ -84,6 +84,7 @@ tests = \ + bug-ungetwc1 \ + bug-ungetwc2 \ + bug-wfflush \ ++ bug-wgenops-bz33998 \ + bug-wmemstream1 \ + bug-wsetpos \ + test-fmemopen \ +diff --git a/libio/bug-wgenops-bz33998.c b/libio/bug-wgenops-bz33998.c +new file mode 100644 +index 0000000000..cc4067da99 +--- /dev/null ++++ b/libio/bug-wgenops-bz33998.c +@@ -0,0 +1,54 @@ ++/* Regression test for ungetwc operating on byte stream (BZ #33998) ++ Copyright (C) 2026 The GNU Toolchain Authors. ++ 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 "support/temp_file.h" ++#include "support/xstdio.h" ++#include "support/xunistd.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ char *filename; ++ int fd = create_temp_file ("tst-bz33998-", &filename); ++ TEST_VERIFY (fd != -1); ++ xwrite (fd, "A", sizeof ("A")); // write "A\0" by design ++ xclose (fd); ++ ++ FILE *fp = xfopen (filename, "r+"); ++ TEST_COMPARE (getwc (fp), L'A'); ++ /* If the bug is fixed, then ungetwc should not touch byte stream. ++ If the bug is not fixed, ungetwc firstly match last read char, L'A', ++ failed, then the pbackfail branch, matching last read char in byte ++ stream, that is, '\0' (initialized when setup wide stream). */ ++ char *old_read_ptr = fp->_IO_read_ptr; ++ TEST_COMPARE (ungetwc (L'\0', fp), L'\0'); ++ TEST_VERIFY (fp->_IO_read_ptr == old_read_ptr); ++ ++ xfclose (fp); ++ free (filename); ++ ++ return 0; ++} ++ ++#include +diff --git a/libio/wgenops.c b/libio/wgenops.c +index 6829477e0c..5f36bc49a1 100644 +--- a/libio/wgenops.c ++++ b/libio/wgenops.c +@@ -110,8 +110,8 @@ _IO_wdefault_pbackfail (FILE *fp, wint_t c) + { + if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base + && !_IO_in_backup (fp) +- && (wint_t) fp->_IO_read_ptr[-1] == c) +- --fp->_IO_read_ptr; ++ && (wint_t) fp->_wide_data->_IO_read_ptr[-1] == c) ++ --fp->_wide_data->_IO_read_ptr; + else + { + /* Need to handle a filebuf in write mode (switch to read mode). FIXME!*/ diff --git a/SOURCES/glibc-RHEL-49785-1.patch b/SOURCES/glibc-RHEL-49785-1.patch new file mode 100644 index 0000000..b9a8d92 --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-1.patch @@ -0,0 +1,56 @@ +commit a0f9bfc3a5cc10920787d70d0653720a8fa013f3 +Author: Adhemerval Zanella +Date: Mon Nov 6 17:25:48 2023 -0300 + + elf: Remove any_debug from dl_main_state + + Its usage can be implied by the GLRO(dl_debug_mask). + Reviewed-by: Siddhesh Poyarekar + +Conflicts: + elf/dl-main.h + (fixup context) + +diff --git a/elf/dl-main.h b/elf/dl-main.h +index d3820e0063143878..35728d47d463c3fb 100644 +--- a/elf/dl-main.h ++++ b/elf/dl-main.h +@@ -94,9 +94,6 @@ struct dl_main_state + + enum rtld_mode mode; + +- /* True if any of the debugging options is enabled. */ +- bool any_debug; +- + /* True if information about versions has to be printed. */ + bool version_info; + }; +diff --git a/elf/rtld.c b/elf/rtld.c +index b788857924e5a388..629ad7f8f4f75353 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -305,7 +305,6 @@ dl_main_state_init (struct dl_main_state *state) + state->glibc_hwcaps_prepend = NULL; + state->glibc_hwcaps_mask = NULL; + state->mode = rtld_mode_normal; +- state->any_debug = false; + state->version_info = false; + } + +@@ -2707,7 +2706,6 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug) + && memcmp (dl_debug, debopts[cnt].name, len) == 0) + { + GLRO(dl_debug_mask) |= debopts[cnt].mask; +- state->any_debug = true; + break; + } + +@@ -2954,7 +2952,7 @@ process_envvars (struct dl_main_state *state) + /* 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. */ +- else if (state->any_debug && debug_output != NULL) ++ else if (GLRO(dl_debug_mask) != 0 && debug_output != NULL) + { + const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW; + size_t name_len = strlen (debug_output); diff --git a/SOURCES/glibc-RHEL-49785-2.patch b/SOURCES/glibc-RHEL-49785-2.patch new file mode 100644 index 0000000..312faf7 --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-2.patch @@ -0,0 +1,374 @@ +commit 332f8e62afef53492dd8285490bcf7aeef18c80a +Author: Frédéric Bérat +Date: Fri Sep 5 16:14:38 2025 +0200 + + tls: Add debug logging for TLS and TCB management + + Introduce the `DL_DEBUG_TLS` debug mask to enable detailed logging for + Thread-Local Storage (TLS) and Thread Control Block (TCB) management. + + This change integrates a new `tls` option into the `LD_DEBUG` + environment variable, allowing developers to trace: + - TCB allocation, deallocation, and reuse events in `dl-tls.c`, + `nptl/allocatestack.c`, and `nptl/nptl-stack.c`. + - Thread startup events, including the TID and TCB address, in + `nptl/pthread_create.c`. + + A new test, `tst-dl-debug-tid`, has been added to validate the + functionality of this new debug logging, ensuring that relevant messages + are correctly generated for both main and worker threads. + + This enhances the debugging capabilities for diagnosing issues related + to TLS allocation and thread lifecycle within the dynamic linker. + + Reviewed-by: DJ Delorie + +Conflicts: + sysdeps/generic/ldsodefs.h + (PRELINK is still there downstream) + +diff --git a/elf/dl-tls.c b/elf/dl-tls.c +index 1b1bc4292eb24747..20d6036b891f653c 100644 +--- a/elf/dl-tls.c ++++ b/elf/dl-tls.c +@@ -503,6 +503,8 @@ _dl_allocate_tls_storage (void) + result = allocate_dtv (result); + if (result == NULL) + free (allocated); ++ else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("TCB allocated: 0x%lx\n", (unsigned long int) result); + + _dl_tls_allocate_end (); + return result; +@@ -691,6 +693,10 @@ rtld_hidden_def (_dl_allocate_tls) + void + _dl_deallocate_tls (void *tcb, bool dealloc_tcb) + { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("TCB deallocating: 0x%lx (dealloc_tcb=%d)\n", ++ (unsigned long int) tcb, dealloc_tcb); ++ + dtv_t *dtv = GET_DTV (tcb); + + /* We need to free the memory allocated for non-static TLS. */ +diff --git a/elf/rtld.c b/elf/rtld.c +index 629ad7f8f4f75353..ff4c160d8fafd70d 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2676,10 +2676,12 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug) + DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("scopes"), "display scope information", + DL_DEBUG_SCOPES }, ++ { LEN_AND_STR ("tls"), "display TLS structures processing", ++ DL_DEBUG_TLS }, + { LEN_AND_STR ("all"), "all previous options combined", + DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS + | DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS +- | DL_DEBUG_SCOPES }, ++ | DL_DEBUG_SCOPES | DL_DEBUG_TLS }, + { LEN_AND_STR ("statistics"), "display relocation statistics", + DL_DEBUG_STATISTICS }, + { LEN_AND_STR ("unused"), "determined unused DSOs", +diff --git a/nptl/Makefile b/nptl/Makefile +index 80c7587f0086677b..7001ecd17b752dba 100644 +--- a/nptl/Makefile ++++ b/nptl/Makefile +@@ -374,6 +374,7 @@ tests-container = tst-pthread-getattr + tests-internal := \ + tst-barrier5 \ + tst-cond22 \ ++ tst-dl-debug-tid \ + tst-mutex8 \ + tst-mutex8-static \ + tst-mutexpi8 \ +@@ -562,6 +563,7 @@ xtests-static += tst-setuid1-static + + ifeq ($(run-built-tests),yes) + tests-special += \ ++ $(objpfx)tst-dl-debug-tid.out \ + $(objpfx)tst-oddstacklimit.out \ + $(objpfx)tst-stack3-mem.out \ + # tests-special +@@ -685,6 +687,11 @@ tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child" + tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child" + + ifeq ($(run-built-tests),yes) ++$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid ++ $(SHELL) $< $(common-objpfx) '$(test-wrapper)' '$(rtld-prefix)' \ ++ '$(test-wrapper-env)' '$(run-program-env)' \ ++ $(objpfx)tst-dl-debug-tid > $@; $(evaluate-test) ++ + $(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1 + $(test-program-prefix) $< --command '$(host-test-program-cmd)' > $@; \ + $(evaluate-test) +diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c +index b0f40afd3f5801f6..9e0bb8f0eeff8e88 100644 +--- a/nptl/allocatestack.c ++++ b/nptl/allocatestack.c +@@ -114,6 +114,10 @@ get_cached_stack (size_t *sizep, void **memp) + /* Release the lock early. */ + lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE); + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ("TLS TCB reused from cache: 0x%lx\n", ++ (unsigned long int) result); ++ + /* Report size and location of the stack to the caller. */ + *sizep = result->stackblock_size; + *memp = result->stackblock; +@@ -291,6 +295,12 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, + stack cache nor will the memory (except the TLS memory) be freed. */ + pd->user_stack = true; + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ( ++ "TCB for user-supplied stack created: 0x%lx, stack=0x%lx, size=%lu\n", ++ (unsigned long int) pd, (unsigned long int) pd->stackblock, ++ (unsigned long int) pd->stackblock_size); ++ + /* This is at least the second thread. */ + pd->header.multiple_threads = 1; + #ifndef TLS_MULTIPLE_THREADS_IN_TCB +@@ -425,6 +435,10 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, + /* Don't allow setxid until cloned. */ + pd->setxid_futex = -1; + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ("TCB for new stack allocated: 0x%lx\n", ++ (unsigned long int) pd); ++ + /* Allocate the DTV for this thread. */ + if (_dl_allocate_tls (TLS_TPADJ (pd)) == NULL) + { +diff --git a/nptl/nptl-stack.c b/nptl/nptl-stack.c +index 3f33a4c20b39a4aa..4ef6527ff7e8192b 100644 +--- a/nptl/nptl-stack.c ++++ b/nptl/nptl-stack.c +@@ -75,6 +75,11 @@ __nptl_free_stacks (size_t limit) + /* Account for the freed memory. */ + GL (dl_stack_cache_actsize) -= curr->stackblock_size; + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ( ++ "TCB cache full, deallocating: TID=%ld, TCB=0x%lx\n", ++ (long int) curr->tid, (unsigned long int) curr); ++ + /* Free the memory associated with the ELF TLS. */ + _dl_deallocate_tls (TLS_TPADJ (curr), false); + +@@ -96,6 +101,12 @@ static inline void + __attribute ((always_inline)) + queue_stack (struct pthread *stack) + { ++ /* The 'stack' parameter is a pointer to the TCB (struct pthread), ++ not just the stack. */ ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ("TCB deallocated into cache: TID=%ld, TCB=0x%lx\n", ++ (long int) stack->tid, (unsigned long int) stack); ++ + /* We unconditionally add the stack to the list. The memory may + still be in use but it will not be reused until the kernel marks + the stack as not used anymore. */ +@@ -123,8 +134,16 @@ __nptl_deallocate_stack (struct pthread *pd) + if (__glibc_likely (! pd->user_stack)) + (void) queue_stack (pd); + else +- /* Free the memory associated with the ELF TLS. */ +- _dl_deallocate_tls (TLS_TPADJ (pd), false); ++ { ++ /* User-provided stack. We must not free it. But we must free ++ the TLS memory. */ ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ( ++ "TCB for user-supplied stack deallocated: TID=%ld, TCB=0x%lx\n", ++ (long int) pd->tid, (unsigned long int) pd); ++ /* Free the memory associated with the ELF TLS. */ ++ _dl_deallocate_tls (TLS_TPADJ (pd), false); ++ } + + lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE); + } +diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c +index 109c5e3dc78c9aa2..37de54eaa570cedb 100644 +--- a/nptl/pthread_create.c ++++ b/nptl/pthread_create.c +@@ -362,6 +362,10 @@ start_thread (void *arg) + goto out; + } + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ GLRO (dl_debug_printf) ("Thread starting: TID=%ld, TCB=0x%lx\n", ++ (long int) pd->tid, (unsigned long int) pd); ++ + /* Initialize resolver state pointer. */ + __resp = &pd->res; + +diff --git a/nptl/tst-dl-debug-tid.c b/nptl/tst-dl-debug-tid.c +new file mode 100644 +index 0000000000000000..231fa43516b233b3 +--- /dev/null ++++ b/nptl/tst-dl-debug-tid.c +@@ -0,0 +1,69 @@ ++/* Test for thread ID logging in dynamic linker. ++ Copyright (C) 2025 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 ++ . */ ++ ++/* This test checks that the dynamic linker correctly logs thread creation ++ and destruction. It creates a detached thread followed by a joinable ++ thread to exercise different code paths. A barrier is used to ensure ++ the detached thread has started before the joinable one is created, ++ making the test more deterministic. The tst-dl-debug-tid.sh shell script ++ wrapper then verifies the LD_DEBUG output. */ ++ ++#include ++#include ++#include ++#include ++ ++static void * ++thread_function (void *arg) ++{ ++ if (arg) ++ pthread_barrier_wait ((pthread_barrier_t *) arg); ++ return NULL; ++} ++ ++static int ++do_test (void) ++{ ++ pthread_t thread1; ++ pthread_attr_t attr; ++ pthread_barrier_t barrier; ++ ++ pthread_barrier_init (&barrier, NULL, 2); ++ ++ /* A detached thread. ++ * Deallocation is done by the thread itself upon exit. */ ++ xpthread_attr_init (&attr); ++ xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ++ /* We don't need the thread handle for the detached thread. */ ++ xpthread_create (&attr, thread_function, &barrier); ++ xpthread_attr_destroy (&attr); ++ ++ /* Wait for the detached thread to be executed. */ ++ pthread_barrier_wait (&barrier); ++ pthread_barrier_destroy (&barrier); ++ ++ /* A joinable thread. ++ * Deallocation is done by the main thread in pthread_join. */ ++ thread1 = xpthread_create (NULL, thread_function, NULL); ++ ++ xpthread_join (thread1); ++ ++ return 0; ++} ++ ++#include +diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh +new file mode 100644 +index 0000000000000000..93d27134a09eaca7 +--- /dev/null ++++ b/nptl/tst-dl-debug-tid.sh +@@ -0,0 +1,72 @@ ++#!/bin/sh ++# Test for thread ID logging in dynamic linker. ++# Copyright (C) 2025 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 ++# . ++ ++# This script runs the tst-dl-debug-tid test case and verifies its ++# LD_DEBUG output. It checks for thread creation/destruction messages ++# to ensure the dynamic linker's thread-aware logging is working. ++ ++set -e ++ ++# Arguments are from Makefile. ++common_objpfx="$1" ++test_wrapper="$2" ++rtld_prefix="$3" ++test_wrapper_env="$4" ++run_program_env="$5" ++test_program="$6" ++ ++debug_output="${common_objpfx}elf/tst-dl-debug-tid.debug" ++rm -f "${debug_output}".* ++ ++# Run the test program with LD_DEBUG=tls. ++eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \ ++ "${test_wrapper}" "${rtld_prefix}" "${test_program}" ++ ++debug_output=$(ls "${debug_output}".*) ++# Check for the "Thread starting" message. ++if ! grep -q 'Thread starting: TID=' "${debug_output}"; then ++ echo "error: 'Thread starting' message not found" ++ cat "${debug_output}" ++ exit 1 ++fi ++ ++# Check that we have a message where the PID (from prefix) is different ++# from the TID (in the message). This indicates a worker thread log. ++if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{ ++ sub(/,/, "", $5); ++ sub(/TID=/, "", $5); ++ if ($1 != $5) ++ exit 0; ++ exit 1 ++}'; then ++ echo "error: No 'Thread starting' message from a worker thread found" ++ cat "${debug_output}" ++ exit 1 ++fi ++ ++# We expect messages from thread creation and destruction. ++if ! grep -q 'TCB allocated\|TCB deallocating\|TCB reused\|TCB deallocated' \ ++ "${debug_output}"; then ++ echo "error: Expected TCB allocation/deallocation message not found" ++ cat "${debug_output}" ++ exit 1 ++fi ++ ++cat "${debug_output}" ++rm -f "${debug_output}" +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index e5860916345487e7..5080db9381c31c4f 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -560,9 +560,10 @@ struct rtld_global_ro + #define DL_DEBUG_STATISTICS (1 << 7) + #define DL_DEBUG_UNUSED (1 << 8) + #define DL_DEBUG_SCOPES (1 << 9) +-/* These two are used only internally. */ ++/* DL_DEBUG_HELP is only used internally. */ + #define DL_DEBUG_HELP (1 << 10) + #define DL_DEBUG_PRELINK (1 << 11) ++#define DL_DEBUG_TLS (1 << 12) + + /* OS version. */ + EXTERN unsigned int _dl_osversion; diff --git a/SOURCES/glibc-RHEL-49785-3.patch b/SOURCES/glibc-RHEL-49785-3.patch new file mode 100644 index 0000000..3969165 --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-3.patch @@ -0,0 +1,107 @@ +commit d4d472366ba69df7b14eba22a75f887b99855d70 +Author: Frédéric Bérat +Date: Thu Oct 2 19:15:29 2025 +0200 + + docs: Add dynamic linker environment variable docs + + The Dynamic Linker chapter now includes a new section detailing + environment variables that influence its behavior. + + This new section documents the `LD_DEBUG` environment variable, + explaining how to enable debugging output and listing its various + keywords like `libs`, `reloc`, `files`, `symbols`, `bindings`, + `versions`, `scopes`, `tls`, `all`, `statistics`, `unused`, and `help`. + + It also documents `LD_DEBUG_OUTPUT`, which controls where the debug + output is written, allowing redirection to a file with the process ID + appended. + + This provides users with essential information for controlling and + debugging the dynamic linker. + + Reviewed-by: DJ Delorie + +diff --git a/manual/dynlink.texi b/manual/dynlink.texi +index 2b35e5883c3e65c0..469340e1d4ae8a88 100644 +--- a/manual/dynlink.texi ++++ b/manual/dynlink.texi +@@ -14,6 +14,8 @@ Dynamic linkers are sometimes called @dfn{dynamic loaders}. + + @menu + * Dynamic Linker Invocation:: Explicit invocation of the dynamic linker. ++* Dynamic Linker Environment Variables:: Environment variables that control the ++ dynamic linker. + * Dynamic Linker Introspection:: Interfaces for querying mapping information. + @end menu + +@@ -348,6 +350,70 @@ probed CPU are omitted. Nothing is printed if the system does not + support the XGETBV instruction. + @end table + ++@node Dynamic Linker Environment Variables ++@section Dynamic Linker Environment Variables ++ ++The behavior of the dynamic linker can be modified through various environment ++variables. ++ ++@table @code ++@item LD_DEBUG ++@cindex @code{LD_DEBUG} environment variable ++The @env{LD_DEBUG} environment variable can be set to a comma-separated list ++of keywords to enable debugging output from the dynamic linker. Setting it to ++@code{help} will display a list of all available keywords. The output is ++written to standard output by default. ++ ++@table @code ++@item libs ++Display library search paths. ++ ++@item reloc ++Display relocation processing. ++ ++@item files ++Display progress for input file processing. ++ ++@item symbols ++Display symbol table processing. ++ ++@item bindings ++Display information about symbol binding. ++ ++@item versions ++Display version dependencies. ++ ++@item scopes ++Display scope information. ++ ++@item tls ++Display information about Thread-Local Storage (TLS) handling, including TCB ++allocation, deallocation, and reuse. This is useful for debugging issues ++related to thread creation and lifecycle. ++ ++@item all ++All previous options combined. ++ ++@item statistics ++Display relocation statistics. ++ ++@item unused ++Determined unused DSOs. ++ ++@item help ++Display a help message with all available options and exit. ++@end table ++ ++@item LD_DEBUG_OUTPUT ++@cindex @code{LD_DEBUG_OUTPUT} environment variable ++If @env{LD_DEBUG} is set, the output is written to standard output by ++default. If @env{LD_DEBUG_OUTPUT} is set, the output is written to the ++file specified by its value, with the process ID appended. For example, if ++@env{LD_DEBUG_OUTPUT} is set to @file{/tmp/glibc.debug}, the output will be ++written to a file named @file{/tmp/glibc.debug.12345}, where @code{12345} is ++the process ID. ++@end table ++ + @node Dynamic Linker Introspection + @section Dynamic Linker Introspection + diff --git a/SOURCES/glibc-RHEL-49785-4.patch b/SOURCES/glibc-RHEL-49785-4.patch new file mode 100644 index 0000000..6ee9ef6 --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-4.patch @@ -0,0 +1,70 @@ +commit 20092f2ef601aef57cc184cbacd7cab39bba5a25 +Author: Yury Khrustalev +Date: Mon Dec 1 10:09:14 2025 +0000 + + nptl: tests: Fix test-wrapper use in tst-dl-debug-tid.sh + + Test wrapper script was used twice: once to run the test + command and second time within the text command which + seems unnecessary and results in false errors when running + this test. + + Fixes 332f8e62afef53492dd8285490bcf7aeef18c80a + + Reviewed-by: Frédéric Bérat + +diff --git a/nptl/Makefile b/nptl/Makefile +index 7001ecd17b752dba..597311bf07223c4f 100644 +--- a/nptl/Makefile ++++ b/nptl/Makefile +@@ -688,8 +688,8 @@ tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child + + ifeq ($(run-built-tests),yes) + $(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid +- $(SHELL) $< $(common-objpfx) '$(test-wrapper)' '$(rtld-prefix)' \ +- '$(test-wrapper-env)' '$(run-program-env)' \ ++ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \ ++ '$(run-program-env)' \ + $(objpfx)tst-dl-debug-tid > $@; $(evaluate-test) + + $(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1 +diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh +index 93d27134a09eaca7..9ee31ac4f241f0b4 100644 +--- a/nptl/tst-dl-debug-tid.sh ++++ b/nptl/tst-dl-debug-tid.sh +@@ -25,18 +25,17 @@ set -e + + # Arguments are from Makefile. + common_objpfx="$1" +-test_wrapper="$2" ++test_wrapper_env="$2" + rtld_prefix="$3" +-test_wrapper_env="$4" +-run_program_env="$5" +-test_program="$6" ++run_program_env="$4" ++test_program="$5" + + debug_output="${common_objpfx}elf/tst-dl-debug-tid.debug" + rm -f "${debug_output}".* + + # Run the test program with LD_DEBUG=tls. + eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \ +- "${test_wrapper}" "${rtld_prefix}" "${test_program}" ++ "${rtld_prefix}" "${test_program}" + + debug_output=$(ls "${debug_output}".*) + # Check for the "Thread starting" message. +@@ -49,9 +48,9 @@ fi + # Check that we have a message where the PID (from prefix) is different + # from the TID (in the message). This indicates a worker thread log. + if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{ +- sub(/,/, "", $5); +- sub(/TID=/, "", $5); +- if ($1 != $5) ++ sub(/,/, "", $4); ++ sub(/TID=/, "", $4); ++ if ($1 != $4) + exit 0; + exit 1 + }'; then diff --git a/SOURCES/glibc-RHEL-49785-5.patch b/SOURCES/glibc-RHEL-49785-5.patch new file mode 100644 index 0000000..be0b0eb --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-5.patch @@ -0,0 +1,681 @@ +commit 1e47dbcce4d5cf2ce71377e729014e454a3e15ae +Author: Frédéric Bérat +Date: Fri Dec 12 16:19:43 2025 +0100 + + elf(tls): Add debug logging for TLS operations + + This commit introduces extensive debug logging for thread-local storage + (TLS) operations within the dynamic linker. When `LD_DEBUG=tls` is + enabled, messages are printed for: + - TLS module assignment and release. + - DTV (Dynamic Thread Vector) resizing events. + - TLS block allocations and deallocations. + - `__tls_get_addr` slow path events (DTV updates, lazy allocations, and + static TLS usage). + + The log format is standardized to use a "tls: " prefix and identifies + modules using the "modid %lu" convention. To aid in debugging + multithreaded applications, thread-specific logs include the Thread + Control Block (TCB) address to identify the context of the operation. + + A new test module `tst-tls-debug-mod.c` and a corresponding shell script + `tst-tls-debug-recursive.sh` have been added. Additionally, the existing + `tst-dl-debug-tid` NPTL test has been updated to verify these TLS debug + messages in a multithreaded context. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/Makefile + (fixup context) + elf/dl-tls.c + (missing 5e249192cac7354af02a7347a0d8c984e0c88ed3 downstream) + sysdeps/x86_64/dl-tls.c + (missing 5e249192cac7354af02a7347a0d8c984e0c88ed3 downstream) + +diff --git a/elf/Makefile b/elf/Makefile +index bd12a20435538fdd..2431e7032c44beb8 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -1252,6 +1252,7 @@ $(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \ + + ifeq ($(run-built-tests),yes) + tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out ++tests-special += $(objpfx)tst-tls-debug-recursive.out + endif + + # The test requires shared _and_ PIE because the executable +@@ -3052,3 +3053,15 @@ LDFLAGS-tst-version-hash-zero-linkmod.so = \ + $(objpfx)tst-version-hash-zero-refmod.so: \ + $(objpfx)tst-version-hash-zero-linkmod.so + tst-version-hash-zero-refmod.so-no-z-defs = yes ++ ++ifeq ($(run-built-tests),yes) ++$(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \ ++ $(objpfx)tst-recursive-tls \ ++ $(objpfx)tst-recursive-tlsmallocmod.so \ ++ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \ ++ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) ++ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \ ++ '$(rtld-prefix)' '$(run_program_env)' \ ++ $(objpfx)tst-recursive-tls > $@; \ ++ $(evaluate-test) ++endif +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 4514c53d2db261c1..8020b0b182d66f4f 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -79,6 +79,11 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, + if (__glibc_likely (old_map != NULL)) + { + /* Mark the entry as unused. These can be read concurrently. */ ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: release modid %lu from %s [%ld]\n", ++ (unsigned long int) idx, DSO_FILENAME (old_map->l_name), ++ (long int) old_map->l_ns); + atomic_store_relaxed (&listp->slotinfo[idx - disp].gen, + GL(dl_tls_generation) + 1); + atomic_store_relaxed (&listp->slotinfo[idx - disp].map, NULL); +diff --git a/elf/dl-tls.c b/elf/dl-tls.c +index 20d6036b891f653c..f22d828a86539057 100644 +--- a/elf/dl-tls.c ++++ b/elf/dl-tls.c +@@ -215,6 +215,12 @@ _dl_assign_tls_modid (struct link_map *l) + } + + l->l_tls_modid = result; ++ ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: assign modid %lu to %s [%ld]\n", ++ (unsigned long int) result, ++ DSO_FILENAME (l->l_name), ++ (long int) l->l_ns); + } + + +@@ -504,7 +510,7 @@ _dl_allocate_tls_storage (void) + if (result == NULL) + free (allocated); + else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- _dl_debug_printf ("TCB allocated: 0x%lx\n", (unsigned long int) result); ++ _dl_debug_printf ("tls: allocate TCB 0x%lx\n", (unsigned long int) result); + + _dl_tls_allocate_end (); + return result; +@@ -517,13 +523,18 @@ extern dtv_t _dl_static_dtv[]; + #endif + + static dtv_t * +-_dl_resize_dtv (dtv_t *dtv, size_t max_modid) ++_dl_resize_dtv (dtv_t *dtv, size_t max_modid, void *tcb) + { + /* Resize the dtv. */ + dtv_t *newp; + size_t newsize = max_modid + DTV_SURPLUS; + size_t oldsize = dtv[-1].counter; + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: DTV resized for TCB 0x%lx: oldsize=%lu, newsize=%lu\n", ++ (unsigned long int) tcb, ++ (unsigned long int) oldsize, (unsigned long int) newsize); ++ + _dl_tls_allocate_begin (); + if (dtv == GL(dl_initial_dtv)) + { +@@ -592,7 +603,7 @@ _dl_allocate_tls_init (void *result, bool main_thread) + if (dtv[-1].counter < GL(dl_tls_max_dtv_idx)) + { + /* Resize the dtv. */ +- dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx)); ++ dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx), result); + + /* Install this new dtv in the thread data structures. */ + INSTALL_DTV (result, &dtv[-1]); +@@ -683,9 +694,14 @@ rtld_hidden_def (_dl_allocate_tls_init) + void * + _dl_allocate_tls (void *mem) + { +- return _dl_allocate_tls_init (mem == NULL +- ? _dl_allocate_tls_storage () +- : allocate_dtv (mem), false); ++ void *result = _dl_allocate_tls_init (mem == NULL ++ ? _dl_allocate_tls_storage () ++ : allocate_dtv (mem), false); ++ if (__glibc_unlikely (result != NULL ++ && (GLRO (dl_debug_mask) & DL_DEBUG_TLS))) ++ _dl_debug_printf ("tls: TLS initialized for TCB 0x%lx\n", ++ (unsigned long int) result); ++ return result; + } + rtld_hidden_def (_dl_allocate_tls) + +@@ -694,14 +710,22 @@ void + _dl_deallocate_tls (void *tcb, bool dealloc_tcb) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- _dl_debug_printf ("TCB deallocating: 0x%lx (dealloc_tcb=%d)\n", ++ _dl_debug_printf ("tls: deallocate TCB 0x%lx (dealloc_tcb=%d)\n", + (unsigned long int) tcb, dealloc_tcb); + + dtv_t *dtv = GET_DTV (tcb); + + /* We need to free the memory allocated for non-static TLS. */ + for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt) +- free (dtv[1 + cnt].pointer.to_free); ++ { ++ if (dtv[1 + cnt].pointer.to_free != NULL ++ && __glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: deallocate block 0x%lx for modid %lu; TCB=0x%lx\n", ++ (unsigned long int) dtv[1 + cnt].pointer.to_free, ++ (unsigned long int) (1 + cnt), (unsigned long int) tcb); ++ free (dtv[1 + cnt].pointer.to_free); ++ } + + /* The array starts with dtv[-1]. */ + if (dtv != GL(dl_initial_dtv)) +@@ -773,6 +797,12 @@ allocate_and_init (struct link_map *map) + (map->l_tls_align, map->l_tls_blocksize); + if (result.val == NULL) + oom (); ++ else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: allocate block 0x%lx for modid %lu; size=%lu, TCB=0x%lx\n", ++ (unsigned long int) result.to_free, ++ (unsigned long int) map->l_tls_modid, ++ (unsigned long int) map->l_tls_blocksize, ++ (unsigned long int) THREAD_SELF); + + /* Initialize the memory. */ + memset (__mempcpy (result.val, map->l_tls_initimage, +@@ -874,7 +904,7 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen) + continue; + + /* Resizing the dtv aborts on failure: bug 16134. */ +- dtv = _dl_resize_dtv (dtv, max_modid); ++ dtv = _dl_resize_dtv (dtv, max_modid, THREAD_SELF); + + assert (modid <= dtv[-1].counter); + +@@ -895,6 +925,12 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen) + least some dynamic TLS usage by interposed mallocs. */ + if (dtv[modid].pointer.to_free != NULL) + { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: DTV update for TCB 0x%lx: modid %lu deallocated block 0x%lx\n", ++ (unsigned long int) THREAD_SELF, ++ (unsigned long int) modid, ++ (unsigned long int) dtv[modid].pointer.to_free); + _dl_tls_allocate_begin (); + free (dtv[modid].pointer.to_free); + _dl_tls_allocate_end (); +@@ -975,6 +1011,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) + dtv[GET_ADDR_MODULE].pointer.to_free = NULL; + dtv[GET_ADDR_MODULE].pointer.val = p; + ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: modid %lu using static TLS; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, ++ (unsigned long int) THREAD_SELF); ++ + return (char *) p + GET_ADDR_OFFSET; + } + else +@@ -1030,18 +1071,28 @@ __tls_get_addr (GET_ADDR_ARGS) + { + if (_dl_tls_allocate_active () + && GET_ADDR_MODULE < _dl_tls_initial_modid_limit) ++ { + /* This is a reentrant __tls_get_addr call, but we can + satisfy it because it's an initially-loaded module ID. + These TLS slotinfo slots do not change, so the + out-of-date generation counter does not matter. However, + if not in a TLS update, still update_get_addr below, to + get off the slow path eventually. */ +- ; ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: modid %lu reentrant usage; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, ++ (unsigned long int) THREAD_SELF); ++ } + else + { + /* Update DTV up to the global generation, see CONCURRENCY NOTES + in _dl_update_slotinfo. */ + gen = atomic_load_acquire (&GL(dl_tls_generation)); ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: modid %lu update DTV to generation %lu; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, (unsigned long int) gen, ++ (unsigned long int) THREAD_SELF); + return update_get_addr (GET_ADDR_PARAM, gen); + } + } +@@ -1049,7 +1100,13 @@ __tls_get_addr (GET_ADDR_ARGS) + void *p = dtv[GET_ADDR_MODULE].pointer.val; + + if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED)) +- return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); ++ { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: modid %lu lazy allocation; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, ++ (unsigned long int) THREAD_SELF); ++ return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); ++ } + + return (char *) p + GET_ADDR_OFFSET; + } +@@ -1119,6 +1176,10 @@ _dl_tls_initial_modid_limit_setup (void) + break; + } + _dl_tls_initial_modid_limit = idx; ++ ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: initial modid limit set to %lu\n", ++ (unsigned long int) idx); + } + + +diff --git a/elf/rtld.c b/elf/rtld.c +index ff4c160d8fafd70d..3221bc8520996097 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1257,6 +1257,11 @@ rtld_setup_main_map (struct link_map *main_map) + + /* This image gets the ID one. */ + GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: assign modid %lu to %s [%ld]\n", ++ (unsigned long int) main_map->l_tls_modid, ++ DSO_FILENAME (main_map->l_name), ++ (long int) main_map->l_ns); + } + break; + +diff --git a/elf/tst-recursive-tlsmodN.c b/elf/tst-recursive-tlsmodN.c +index bb7592aee6ed347e..921980605cf32a38 100644 +--- a/elf/tst-recursive-tlsmodN.c ++++ b/elf/tst-recursive-tlsmodN.c +@@ -19,10 +19,10 @@ + /* Compiled with VAR and FUNC set via -D. FUNC requires some + relocation against TLS variable VAR. */ + +-__thread int VAR; ++__thread char VAR[32768]; + + int + FUNC (void) + { +- return VAR; ++ return VAR[0]; + } +diff --git a/elf/tst-tls-debug-recursive.sh b/elf/tst-tls-debug-recursive.sh +new file mode 100755 +index 0000000000000000..083e716f7216ffd5 +--- /dev/null ++++ b/elf/tst-tls-debug-recursive.sh +@@ -0,0 +1,83 @@ ++#!/bin/sh ++# Test for TLS logging in dynamic linker. ++# 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 ++# . ++ ++# This script runs the tst-tls-debug-recursive test case and verifies its ++# LD_DEBUG=tls output. It checks for various TLS-related messages ++# to ensure the dynamic linker's TLS logging is working correctly. ++ ++set -e ++common_objpfx="$1" ++test_wrapper_env="$2" ++rtld_prefix="$3" ++run_program_env="$4" ++test_program="$5" ++ ++debug_output="${common_objpfx}elf/tst-tls-debug-recursive.debug" ++rm -f "${debug_output}".* ++ ++# Run the test program with LD_DEBUG=tls. ++eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \ ++ "${rtld_prefix}" "${test_program}" ++ ++debug_output=$(ls "${debug_output}".*) ++ ++fail=0 ++ ++# Check for expected messages ++if ! grep -q 'tls: DTV resized for TCB 0x.*: oldsize' "${debug_output}"; then ++ echo "FAIL: DTV resized message not found" ++ fail=1 ++fi ++ ++if ! grep -q 'tls: DTV update for TCB 0x.*: modid .* deallocated block' "${debug_output}"; then ++ echo "FAIL: module deallocated during DTV update message not found" ++ fail=1 ++fi ++ ++if ! grep -q 'tls: assign modid .* to' "${debug_output}"; then ++ echo "FAIL: module assigned message not found" ++ fail=1 ++fi ++ ++if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' "${debug_output}"; then ++ echo "FAIL: module allocated message not found" ++ fail=1 ++fi ++ ++if ! grep -q 'tls: modid .* update DTV to generation .* TCB=0x' "${debug_output}"; then ++ echo "FAIL: update DTV message not found" ++ fail=1 ++fi ++ ++if ! grep -q 'tls: initial modid limit set to' "${debug_output}"; then ++ echo "FAIL: initial modid limit message not found" ++ fail=1 ++fi ++ ++if [ $fail -ne 0 ]; then ++ echo "Test FAILED" ++ cat "${debug_output}" ++ rm -f "${debug_output}" ++ exit 1 ++fi ++ ++echo "Test PASSED" ++cat "${debug_output}" ++rm -f "${debug_output}" ++exit 0 +diff --git a/nptl/Makefile b/nptl/Makefile +index 597311bf07223c4f..958de6b0002f63aa 100644 +--- a/nptl/Makefile ++++ b/nptl/Makefile +@@ -274,6 +274,7 @@ CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11 + LDLIBS-tst-thread-exit-clobber = -lstdc++ + CFLAGS-tst-minstack-throw.o = -std=gnu++11 + LDLIBS-tst-minstack-throw = -lstdc++ ++LDLIBS-tst-dl-debug-tid = $(libdl) + + tests = \ + tst-attr2 \ +@@ -486,6 +487,7 @@ modules-names = \ + tst-compat-forwarder-mod \ + tst-execstack-mod \ + tst-stack4mod \ ++ tst-tls-debug-mod \ + tst-tls3mod \ + tst-tls5mod \ + tst-tls5moda \ +@@ -687,7 +689,8 @@ tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child" + tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child" + + ifeq ($(run-built-tests),yes) +-$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid ++$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid \ ++ $(objpfx)tst-tls-debug-mod.so + $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \ + '$(run-program-env)' \ + $(objpfx)tst-dl-debug-tid > $@; $(evaluate-test) +diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c +index 9e0bb8f0eeff8e88..6c6094e929f927cc 100644 +--- a/nptl/allocatestack.c ++++ b/nptl/allocatestack.c +@@ -115,7 +115,7 @@ get_cached_stack (size_t *sizep, void **memp) + lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE); + + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- GLRO (dl_debug_printf) ("TLS TCB reused from cache: 0x%lx\n", ++ GLRO (dl_debug_printf) ("tls: TCB reused from cache: 0x%lx\n", + (unsigned long int) result); + + /* Report size and location of the stack to the caller. */ +@@ -297,9 +297,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, + + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) + GLRO (dl_debug_printf) ( +- "TCB for user-supplied stack created: 0x%lx, stack=0x%lx, size=%lu\n", +- (unsigned long int) pd, (unsigned long int) pd->stackblock, +- (unsigned long int) pd->stackblock_size); ++ "tls: TCB created (user-supplied stack); stack=0x%lx, size=%lu, TCB=0x%lx\n", ++ (unsigned long int) pd->stackblock, ++ (unsigned long int) pd->stackblock_size, (unsigned long int) pd); + + /* This is at least the second thread. */ + pd->header.multiple_threads = 1; +@@ -436,7 +436,7 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, + pd->setxid_futex = -1; + + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- GLRO (dl_debug_printf) ("TCB for new stack allocated: 0x%lx\n", ++ GLRO (dl_debug_printf) ("tls: TCB allocated (new stack): 0x%lx\n", + (unsigned long int) pd); + + /* Allocate the DTV for this thread. */ +diff --git a/nptl/nptl-stack.c b/nptl/nptl-stack.c +index 4ef6527ff7e8192b..d2d0729035a19dfe 100644 +--- a/nptl/nptl-stack.c ++++ b/nptl/nptl-stack.c +@@ -77,7 +77,7 @@ __nptl_free_stacks (size_t limit) + + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) + GLRO (dl_debug_printf) ( +- "TCB cache full, deallocating: TID=%ld, TCB=0x%lx\n", ++ "tls: TCB deallocating from full cache; TID=%ld, TCB=0x%lx\n", + (long int) curr->tid, (unsigned long int) curr); + + /* Free the memory associated with the ELF TLS. */ +@@ -104,7 +104,7 @@ queue_stack (struct pthread *stack) + /* The 'stack' parameter is a pointer to the TCB (struct pthread), + not just the stack. */ + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- GLRO (dl_debug_printf) ("TCB deallocated into cache: TID=%ld, TCB=0x%lx\n", ++ GLRO (dl_debug_printf) ("tls: TCB deallocated into cache; TID=%ld, TCB=0x%lx\n", + (long int) stack->tid, (unsigned long int) stack); + + /* We unconditionally add the stack to the list. The memory may +@@ -139,7 +139,7 @@ __nptl_deallocate_stack (struct pthread *pd) + the TLS memory. */ + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) + GLRO (dl_debug_printf) ( +- "TCB for user-supplied stack deallocated: TID=%ld, TCB=0x%lx\n", ++ "tls: TCB deallocated (user-supplied stack); TID=%ld, TCB=0x%lx\n", + (long int) pd->tid, (unsigned long int) pd); + /* Free the memory associated with the ELF TLS. */ + _dl_deallocate_tls (TLS_TPADJ (pd), false); +diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c +index 37de54eaa570cedb..c69b4108f60f647d 100644 +--- a/nptl/pthread_create.c ++++ b/nptl/pthread_create.c +@@ -363,7 +363,7 @@ start_thread (void *arg) + } + + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) +- GLRO (dl_debug_printf) ("Thread starting: TID=%ld, TCB=0x%lx\n", ++ GLRO (dl_debug_printf) ("tls: thread starting; TID=%ld, TCB=0x%lx\n", + (long int) pd->tid, (unsigned long int) pd); + + /* Initialize resolver state pointer. */ +diff --git a/nptl/tst-dl-debug-tid.c b/nptl/tst-dl-debug-tid.c +index 231fa43516b233b3..f3b443f9786a9a99 100644 +--- a/nptl/tst-dl-debug-tid.c ++++ b/nptl/tst-dl-debug-tid.c +@@ -27,12 +27,25 @@ + #include + #include + #include ++#include ++#include ++#include + + static void * + thread_function (void *arg) + { + if (arg) + pthread_barrier_wait ((pthread_barrier_t *) arg); ++ ++ /* Load a module with TLS to verify allocation/deallocation logs. */ ++ void *h = xdlopen ("tst-tls-debug-mod.so", RTLD_NOW); ++ ++ /* Call a function that accesses TLS. */ ++ int (*fp) (void) = (int (*) (void)) xdlsym (h, "in_dso"); ++ TEST_COMPARE (fp (), 0); ++ ++ xdlclose (h); ++ + return NULL; + } + +diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh +index 9ee31ac4f241f0b4..8708ac9f2ea25c9e 100644 +--- a/nptl/tst-dl-debug-tid.sh ++++ b/nptl/tst-dl-debug-tid.sh +@@ -39,7 +39,7 @@ eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \ + + debug_output=$(ls "${debug_output}".*) + # Check for the "Thread starting" message. +-if ! grep -q 'Thread starting: TID=' "${debug_output}"; then ++if ! grep -q 'tls: thread starting; TID=.*, TCB=0x' "${debug_output}"; then + echo "error: 'Thread starting' message not found" + cat "${debug_output}" + exit 1 +@@ -47,10 +47,10 @@ fi + + # Check that we have a message where the PID (from prefix) is different + # from the TID (in the message). This indicates a worker thread log. +-if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{ +- sub(/,/, "", $4); +- sub(/TID=/, "", $4); +- if ($1 != $4) ++if ! grep 'tls: thread starting; TID=.*, TCB=0x' "${debug_output}" | awk -F '[ \t:]+' '{ ++ sub(/TID=/, "", $5); ++ sub(/,/, "", $5); ++ if ($1 != $5) + exit 0; + exit 1 + }'; then +@@ -60,12 +60,33 @@ if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{ + fi + + # We expect messages from thread creation and destruction. +-if ! grep -q 'TCB allocated\|TCB deallocating\|TCB reused\|TCB deallocated' \ ++if ! grep -q 'tls: allocate TCB 0x\|tls: deallocate TCB 0x\|tls: TCB reused from cache\|tls: TCB deallocated' \ + "${debug_output}"; then + echo "error: Expected TCB allocation/deallocation message not found" + cat "${debug_output}" + exit 1 + fi + ++# Check for TLS module ID assignment. ++if ! grep -q 'tls: assign modid .* to' "${debug_output}"; then ++ echo "error: Expected 'modid ... assigned to' message not found" ++ cat "${debug_output}" ++ exit 1 ++fi ++ ++# Check for TLS block allocation. ++if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' "${debug_output}"; then ++ echo "error: Expected 'modid ... allocated' message not found" ++ cat "${debug_output}" ++ exit 1 ++fi ++ ++# TLS block deallocation might be skipped due to DTV surplus. ++if grep -q 'tls: deallocate block .* for modid .* TCB=0x' "${debug_output}"; then ++ echo "INFO: module deallocated message found" ++else ++ echo "INFO: module deallocated message not found (may be due to DTV surplus)" ++fi ++ + cat "${debug_output}" + rm -f "${debug_output}" +diff --git a/nptl/tst-tls-debug-mod.c b/nptl/tst-tls-debug-mod.c +new file mode 100644 +index 0000000000000000..0308c8a3e2d76fe1 +--- /dev/null ++++ b/nptl/tst-tls-debug-mod.c +@@ -0,0 +1,26 @@ ++/* Test for TLS logging in dynamic linker. ++ 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 ++ . */ ++ ++__thread char tls_var[32768] __attribute__ ((tls_model ("global-dynamic"))); ++ ++int ++in_dso (void) ++{ ++ tls_var[0] = 42; ++ return tls_var[0] - 42; ++} +diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c +index 7abfb18413b1d06c..2bd336876a6613c8 100644 +--- a/sysdeps/x86_64/dl-tls.c ++++ b/sysdeps/x86_64/dl-tls.c +@@ -41,11 +41,36 @@ __tls_get_addr_slow (GET_ADDR_ARGS) + dtv_t *dtv = THREAD_DTV (); + + size_t gen = atomic_load_acquire (&GL(dl_tls_generation)); +- if (__glibc_unlikely (dtv[0].counter != gen) ++ if (__glibc_unlikely (dtv[0].counter != gen)) ++ { + /* See comment in __tls_get_addr in elf/dl-tls.c. */ +- && !(_dl_tls_allocate_active () +- && GET_ADDR_MODULE < _dl_tls_initial_modid_limit)) +- return update_get_addr (GET_ADDR_PARAM, gen); ++ if (_dl_tls_allocate_active () ++ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit) ++ { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: modid %lu reentrant usage; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, ++ (unsigned long int) THREAD_SELF); ++ } ++ else ++ { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ( ++ "tls: modid %lu update DTV to generation %lu; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, (unsigned long int) gen, ++ (unsigned long int) THREAD_SELF); ++ return update_get_addr (GET_ADDR_PARAM, gen); ++ } ++ } ++ ++ if (__glibc_unlikely (dtv[GET_ADDR_MODULE].pointer.val == TLS_DTV_UNALLOCATED)) ++ { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS)) ++ _dl_debug_printf ("tls: modid %lu lazy allocation; TCB=0x%lx\n", ++ (unsigned long int) GET_ADDR_MODULE, ++ (unsigned long int) THREAD_SELF); ++ } + + return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); + } diff --git a/SOURCES/glibc-RHEL-49785-6.patch b/SOURCES/glibc-RHEL-49785-6.patch new file mode 100644 index 0000000..e109ac5 --- /dev/null +++ b/SOURCES/glibc-RHEL-49785-6.patch @@ -0,0 +1,177 @@ +commit 9181dc6eb6084da95d8c14d9defe96189fd0360d +Author: Frédéric Bérat +Date: Tue Jan 27 23:07:17 2026 +0100 + + feat(rtld): Allow LD_DEBUG category exclusion + + Adds support for excluding specific categories from `LD_DEBUG` output. + + The `LD_DEBUG` environment variable now accepts category names prefixed + with a dash (`-`) to disable their debugging output. This allows users + to enable broad categories (e.g., `all`) while suppressing verbose or + irrelevant information from specific sub-categories (e.g., `-tls`). + + The `process_dl_debug` function in `rtld.c` has been updated to parse + these exclusion options and unset the corresponding bits in + `GLRO(dl_debug_mask)`. The `LD_DEBUG=help` output has also been updated + to document this new functionality. A new test `tst-dl-debug-exclude.sh` + is added to verify the correct behavior of category exclusion. + + Reviewed-by: Adhemerval Zanella + +diff --git a/elf/Makefile b/elf/Makefile +index 2431e7032c44beb8..ec87811c61565511 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -1253,6 +1253,7 @@ $(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \ + ifeq ($(run-built-tests),yes) + tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out + tests-special += $(objpfx)tst-tls-debug-recursive.out ++tests-special += $(objpfx)tst-dl-debug-exclude.out + endif + + # The test requires shared _and_ PIE because the executable +@@ -3064,4 +3065,14 @@ $(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \ + '$(rtld-prefix)' '$(run_program_env)' \ + $(objpfx)tst-recursive-tls > $@; \ + $(evaluate-test) ++ ++$(objpfx)tst-dl-debug-exclude.out: tst-dl-debug-exclude.sh \ ++ $(objpfx)tst-recursive-tls \ ++ $(objpfx)tst-recursive-tlsmallocmod.so \ ++ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \ ++ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) ++ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \ ++ '$(rtld-prefix)' '$(run_program_env)' \ ++ $(objpfx)tst-recursive-tls > $@; \ ++ $(evaluate-test) + endif +diff --git a/elf/rtld.c b/elf/rtld.c +index 3221bc8520996097..a4290890d6bca263 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2708,11 +2708,18 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug) + && dl_debug[len] != ',' && dl_debug[len] != ':') + ++len; + ++ bool exclude = *dl_debug == '-'; ++ const char *name = exclude ? dl_debug + 1 : dl_debug; ++ size_t name_len = exclude ? len - 1 : len; ++ + for (cnt = 0; cnt < ndebopts; ++cnt) +- if (debopts[cnt].len == len +- && memcmp (dl_debug, debopts[cnt].name, len) == 0) ++ if (debopts[cnt].len == name_len ++ && memcmp (name, debopts[cnt].name, name_len) == 0) + { +- GLRO(dl_debug_mask) |= debopts[cnt].mask; ++ if (exclude) ++ GLRO(dl_debug_mask) &= ~debopts[cnt].mask; ++ else ++ GLRO(dl_debug_mask) |= debopts[cnt].mask; + break; + } + +@@ -2754,7 +2761,8 @@ Valid options for the LD_DEBUG environment variable are:\n\n"); + + _dl_printf ("\n\ + To direct the debugging output into a file instead of standard output\n\ +-a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n"); ++a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n\ ++Categories can be excluded by prefixing them with a dash (-).\n"); + _exit (0); + } + } +diff --git a/elf/tst-dl-debug-exclude.sh b/elf/tst-dl-debug-exclude.sh +new file mode 100644 +index 0000000000000000..9837ffcea8e07933 +--- /dev/null ++++ b/elf/tst-dl-debug-exclude.sh +@@ -0,0 +1,87 @@ ++#!/bin/sh ++# Test for LD_DEBUG category exclusion. ++# 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 ++# . ++ ++# This script verifies the LD_DEBUG category exclusion functionality. ++# It checks that: ++# 1. Categories can be excluded using the '-' prefix. ++# 2. Options are processed sequentially, meaning the last specified ++# option for a category (enable or exclude) takes precedence. ++ ++set -e ++ ++common_objpfx="$1" ++test_wrapper_env="$2" ++rtld_prefix="$3" ++run_program_env="$4" ++test_program="$5" ++ ++debug_output="${common_objpfx}elf/tst-dl-debug-exclude.debug" ++rm -f "${debug_output}".* ++ ++# Run the test program with LD_DEBUG=all,-tls. ++# We expect general logs but no TLS logs. ++eval "${test_wrapper_env}" LD_DEBUG=all,-tls LD_DEBUG_OUTPUT="${debug_output}" \ ++ "${rtld_prefix}" "${test_program}" ++ ++fail=0 ++ ++# 1. Check that general logs are present (e.g., file loading) ++if ! grep -q 'file=' "${debug_output}".*; then ++ echo "FAIL: 'file=' message not found (LD_DEBUG=all failed)" ++ fail=1 ++fi ++ ++# 2. Check that TLS logs are NOT present ++if grep -q 'tls: ' "${debug_output}".*; then ++ echo "FAIL: TLS message found (exclusion of -tls failed)" ++ fail=1 ++fi ++ ++rm -f "${debug_output}".* ++# 3. Check for LD_DEBUG=all,-tls,tls (ordering verification) ++# We expect TLS logs to BE present ++eval "${test_wrapper_env}" LD_DEBUG=all,-tls,tls LD_DEBUG_OUTPUT="${debug_output}" \ ++ "${rtld_prefix}" "${test_program}" ++ ++if ! grep -q 'tls: ' "${debug_output}".*; then ++ echo "FAIL: TLS message not found (ordering -tls,tls failed)" ++ fail=1 ++fi ++ ++rm -f "${debug_output}".* ++# 4. Check for LD_DEBUG=tls,-tls ++# We expect TLS logs to NOT be present ++eval "${test_wrapper_env}" LD_DEBUG=tls,-tls LD_DEBUG_OUTPUT="${debug_output}" \ ++ "${rtld_prefix}" "${test_program}" ++ ++if grep -q 'tls: ' "${debug_output}".*; then ++ echo "FAIL: TLS message found (ordering tls,-tls failed)" ++ fail=1 ++fi ++ ++if [ $fail -ne 0 ]; then ++ echo "Test FAILED" ++ cat "${debug_output}".* ++ rm -f "${debug_output}".* ++ exit 1 ++fi ++ ++echo "Test PASSED" ++rm -f "${debug_output}".* ++exit 0 diff --git a/SOURCES/glibc-RHEL-52966.patch b/SOURCES/glibc-RHEL-52966.patch new file mode 100644 index 0000000..52a65c1 --- /dev/null +++ b/SOURCES/glibc-RHEL-52966.patch @@ -0,0 +1,288 @@ +commit 39ca997ab378990d5ac1aadbaa52aaf1db6d526f +Author: Andreas Schwab +Date: Mon Aug 5 10:55:51 2024 +0200 + + Fix name space violation in fortify wrappers (bug 32052) + + Rename the identifier sz to __sz everywhere. + + Fixes: a643f60c53 ("Make sure that the fortified function conditionals are constant") + +Conflicts: + libio/bits/stdio2.h + (29951991f5db1001c059f8ed7afa2c359cdc7f44 missing downstream) + socket/bits/socket2.h + (4289b00d4393f490515527864cf09093f4f8c2c4 missing downstream) + stdlib/bits/stdlib.h + (d39a893ed6de8e63ffbfbcc4b7176a2fa852f8a8 missing downstream) + wcsmbs/bits/wchar2.h + (68444c045077368446eced143510419c901e31b1 missing downstream) + +diff --git a/libio/bits/stdio2.h b/libio/bits/stdio2.h +index 4570f86a4496c1ee..5d1f3574f9f3fa9e 100644 +--- a/libio/bits/stdio2.h ++++ b/libio/bits/stdio2.h +@@ -208,12 +208,12 @@ extern char *__REDIRECT (__fgets_chk_warn, + __fortify_function __wur __fortified_attr_access (__write_only__, 1, 2) char * + fgets (char *__restrict __s, int __n, FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __fgets_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __fgets_chk_warn (__s, sz, __n, __stream); +- return __fgets_chk (__s, sz, __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __fgets_chk_warn (__s, __sz, __n, __stream); ++ return __fgets_chk (__s, __sz, __n, __stream); + } + + extern size_t __REDIRECT (__fread_alias, +@@ -232,12 +232,12 @@ __fortify_function __wur size_t + fread (void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize0 (__ptr); +- if (__glibc_safe_or_unknown_len (__n, __size, sz)) ++ size_t __sz = __glibc_objsize0 (__ptr); ++ if (__glibc_safe_or_unknown_len (__n, __size, __sz)) + return __fread_alias (__ptr, __size, __n, __stream); +- if (__glibc_unsafe_len (__n, __size, sz)) +- return __fread_chk_warn (__ptr, sz, __size, __n, __stream); +- return __fread_chk (__ptr, sz, __size, __n, __stream); ++ if (__glibc_unsafe_len (__n, __size, __sz)) ++ return __fread_chk_warn (__ptr, __sz, __size, __n, __stream); ++ return __fread_chk (__ptr, __sz, __size, __n, __stream); + } + + #ifdef __USE_GNU +@@ -254,12 +254,12 @@ extern char *__REDIRECT (__fgets_unlocked_chk_warn, + __fortify_function __wur __fortified_attr_access (__write_only__, 1, 2) char * + fgets_unlocked (char *__restrict __s, int __n, FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __fgets_unlocked_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __fgets_unlocked_chk_warn (__s, sz, __n, __stream); +- return __fgets_unlocked_chk (__s, sz, __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __fgets_unlocked_chk_warn (__s, __sz, __n, __stream); ++ return __fgets_unlocked_chk (__s, __sz, __n, __stream); + } + #endif + +@@ -281,8 +281,8 @@ __fortify_function __wur size_t + fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize0 (__ptr); +- if (__glibc_safe_or_unknown_len (__n, __size, sz)) ++ size_t __sz = __glibc_objsize0 (__ptr); ++ if (__glibc_safe_or_unknown_len (__n, __size, __sz)) + { + # ifdef __USE_EXTERN_INLINES + if (__builtin_constant_p (__size) +@@ -307,9 +307,9 @@ fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n, + # endif + return __fread_unlocked_alias (__ptr, __size, __n, __stream); + } +- if (__glibc_unsafe_len (__n, __size, sz)) +- return __fread_unlocked_chk_warn (__ptr, sz, __size, __n, __stream); +- return __fread_unlocked_chk (__ptr, sz, __size, __n, __stream); ++ if (__glibc_unsafe_len (__n, __size, __sz)) ++ return __fread_unlocked_chk_warn (__ptr, __sz, __size, __n, __stream); ++ return __fread_unlocked_chk (__ptr, __sz, __size, __n, __stream); + + } + #endif +diff --git a/socket/bits/socket2.h b/socket/bits/socket2.h +index b28cde55f3fd9c16..e278ebf37b03f121 100644 +--- a/socket/bits/socket2.h ++++ b/socket/bits/socket2.h +@@ -33,12 +33,12 @@ extern ssize_t __REDIRECT (__recv_chk_warn, + __fortify_function ssize_t + recv (int __fd, void *__buf, size_t __n, int __flags) + { +- size_t sz = __glibc_objsize0 (__buf); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize0 (__buf); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __recv_alias (__fd, __buf, __n, __flags); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __recv_chk_warn (__fd, __buf, __n, sz, __flags); +- return __recv_chk (__fd, __buf, __n, sz, __flags); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __recv_chk_warn (__fd, __buf, __n, __sz, __flags); ++ return __recv_chk (__fd, __buf, __n, __sz, __flags); + } + + extern ssize_t __recvfrom_chk (int __fd, void *__restrict __buf, size_t __n, +@@ -61,11 +61,11 @@ __fortify_function ssize_t + recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags, + __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len) + { +- size_t sz = __glibc_objsize0 (__buf); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize0 (__buf); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __recvfrom_alias (__fd, __buf, __n, __flags, __addr, __addr_len); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __recvfrom_chk_warn (__fd, __buf, __n, sz, __flags, __addr, ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __recvfrom_chk_warn (__fd, __buf, __n, __sz, __flags, __addr, + __addr_len); +- return __recvfrom_chk (__fd, __buf, __n, sz, __flags, __addr, __addr_len); ++ return __recvfrom_chk (__fd, __buf, __n, __sz, __flags, __addr, __addr_len); + } +diff --git a/stdlib/bits/stdlib.h b/stdlib/bits/stdlib.h +index ac9badc81f3990c5..658a08b80f3a3141 100644 +--- a/stdlib/bits/stdlib.h ++++ b/stdlib/bits/stdlib.h +@@ -36,16 +36,16 @@ extern char *__REDIRECT_NTH (__realpath_chk_warn, + __fortify_function __wur char * + __NTH (realpath (const char *__restrict __name, char *__restrict __resolved)) + { +- size_t sz = __glibc_objsize (__resolved); ++ size_t __sz = __glibc_objsize (__resolved); + +- if (sz == (size_t) -1) ++ if (__sz == (size_t) -1) + return __realpath_alias (__name, __resolved); + + #if defined _LIBC_LIMITS_H_ && defined PATH_MAX +- if (__glibc_unsafe_len (PATH_MAX, sizeof (char), sz)) +- return __realpath_chk_warn (__name, __resolved, sz); ++ if (__glibc_unsafe_len (PATH_MAX, sizeof (char), __sz)) ++ return __realpath_chk_warn (__name, __resolved, __sz); + #endif +- return __realpath_chk (__name, __resolved, sz); ++ return __realpath_chk (__name, __resolved, __sz); + } + + +diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h +index 50151b424d85a032..ee43dcb6a9170564 100644 +--- a/wcsmbs/bits/wchar2.h ++++ b/wcsmbs/bits/wchar2.h +@@ -107,9 +107,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscpy_alias, + __fortify_function wchar_t * + __NTH (wcscpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcscpy_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcscpy_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcscpy_alias (__dest, __src); + } + +@@ -121,9 +121,9 @@ extern wchar_t *__REDIRECT_NTH (__wcpcpy_alias, + __fortify_function wchar_t * + __NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcpcpy_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcpcpy_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcpcpy_alias (__dest, __src); + } + +@@ -177,9 +177,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscat_alias, + __fortify_function wchar_t * + __NTH (wcscat (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcscat_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcscat_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcscat_alias (__dest, __src); + } + +@@ -193,9 +193,9 @@ __fortify_function wchar_t * + __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src, + size_t __n)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcsncat_chk (__dest, __src, __n, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcsncat_chk (__dest, __src, __n, __sz / sizeof (wchar_t)); + return __wcsncat_alias (__dest, __src, __n); + } + +@@ -211,10 +211,10 @@ __fortify_function int + __NTH (swprintf (wchar_t *__restrict __s, size_t __n, + const wchar_t *__restrict __fmt, ...)) + { +- size_t sz = __glibc_objsize (__s); +- if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) ++ size_t __sz = __glibc_objsize (__s); ++ if (__sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) + return __swprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, +- sz / sizeof (wchar_t), __fmt, __va_arg_pack ()); ++ __sz / sizeof (wchar_t), __fmt, __va_arg_pack ()); + return __swprintf_alias (__s, __n, __fmt, __va_arg_pack ()); + } + #elif !defined __cplusplus +@@ -236,10 +236,10 @@ __fortify_function int + __NTH (vswprintf (wchar_t *__restrict __s, size_t __n, + const wchar_t *__restrict __fmt, __gnuc_va_list __ap)) + { +- size_t sz = __glibc_objsize (__s); +- if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) ++ size_t __sz = __glibc_objsize (__s); ++ if (__sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) + return __vswprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, +- sz / sizeof (wchar_t), __fmt, __ap); ++ __sz / sizeof (wchar_t), __fmt, __ap); + return __vswprintf_alias (__s, __n, __fmt, __ap); + } + +@@ -293,12 +293,12 @@ extern wchar_t *__REDIRECT (__fgetws_chk_warn, + __fortify_function __wur wchar_t * + fgetws (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), __sz)) + return __fgetws_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz)) +- return __fgetws_chk_warn (__s, sz / sizeof (wchar_t), __n, __stream); +- return __fgetws_chk (__s, sz / sizeof (wchar_t), __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (wchar_t), __sz)) ++ return __fgetws_chk_warn (__s, __sz / sizeof (wchar_t), __n, __stream); ++ return __fgetws_chk (__s, __sz / sizeof (wchar_t), __n, __stream); + } + + #ifdef __USE_GNU +@@ -316,13 +316,13 @@ extern wchar_t *__REDIRECT (__fgetws_unlocked_chk_warn, + __fortify_function __wur wchar_t * + fgetws_unlocked (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), __sz)) + return __fgetws_unlocked_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz)) +- return __fgetws_unlocked_chk_warn (__s, sz / sizeof (wchar_t), __n, ++ if (__glibc_unsafe_len (__n, sizeof (wchar_t), __sz)) ++ return __fgetws_unlocked_chk_warn (__s, __sz / sizeof (wchar_t), __n, + __stream); +- return __fgetws_unlocked_chk (__s, sz / sizeof (wchar_t), __n, __stream); ++ return __fgetws_unlocked_chk (__s, __sz / sizeof (wchar_t), __n, __stream); + } + #endif + diff --git a/SOURCES/glibc-RHEL-54450.patch b/SOURCES/glibc-RHEL-54450.patch new file mode 100644 index 0000000..ab7c187 --- /dev/null +++ b/SOURCES/glibc-RHEL-54450.patch @@ -0,0 +1,111 @@ +commit b52619f2e8bbae57d79c95538346198c4a9f24a6 +Author: Arjun Shankar +Date: Mon Jan 26 13:49:37 2026 +0100 + + dlfcn: Add dlinfo request type RTLD_DI_ORIGIN_PATH (bug #24298) + + The existing dlinfo request type RTLD_DI_ORIGIN used for querying the + value of the '$ORIGIN' dynamic string token is prone to buffer + overflows. + + This commit adds a new request type named RTLD_DI_ORIGIN_PATH that + returns a pointer to the dynamic string token (i.e. the 'l_origin' field + in the link map) instead. The dlinfo manual is updated with the new + request type, and the description of RTLD_DI_ORIGIN is updated to + recommend RTLD_DI_ORIGIN_PATH instead. + + A test for the new request type is also added to tst-dlinfo. + + Reviewed-by: Carlos O'Donell + +diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h +index 8168a71dbfe90e75..b80c3e3d9f240e80 100644 +--- a/dlfcn/dlfcn.h ++++ b/dlfcn/dlfcn.h +@@ -169,7 +169,12 @@ enum + the number of program headers in the array. */ + RTLD_DI_PHDR = 11, + +- RTLD_DI_MAX = 11 ++ /* Treat ARG as `const char **' and at that location, store the address ++ of the directory name used to expand $ORIGIN in this shared object's ++ dependency file names. */ ++ RTLD_DI_ORIGIN_PATH = 12, ++ ++ RTLD_DI_MAX = 12 + }; + + +diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c +index 1842925fb7c594dd..28681a865a11e102 100644 +--- a/dlfcn/dlinfo.c ++++ b/dlfcn/dlinfo.c +@@ -67,6 +67,13 @@ dlinfo_doit (void *argsblock) + strcpy (args->arg, l->l_origin); + break; + ++ case RTLD_DI_ORIGIN_PATH: ++ if (l->l_origin != (char *) -1) ++ *(const char **) args->arg = l->l_origin; ++ else ++ *(const char **) args->arg = NULL; ++ break; ++ + case RTLD_DI_TLS_MODID: + *(size_t *) args->arg = 0; + *(size_t *) args->arg = l->l_tls_modid; +diff --git a/dlfcn/tst-dlinfo.c b/dlfcn/tst-dlinfo.c +index d47eebc013a556b4..0765be8e649251b7 100644 +--- a/dlfcn/tst-dlinfo.c ++++ b/dlfcn/tst-dlinfo.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #define TEST_FUNCTION do_test () + +@@ -56,6 +57,13 @@ do_test (void) + printf ("origin: %s\n", origin); + } + ++ const char *origin_path; ++ TRY (RTLD_DI_ORIGIN_PATH, &origin_path) ++ { ++ TEST_COMPARE_STRING (origin, origin_path); ++ printf ("origin_path: %s\n", origin_path); ++ } ++ + Dl_serinfo counts; + TRY (RTLD_DI_SERINFOSIZE, &counts) + { +diff --git a/manual/dynlink.texi b/manual/dynlink.texi +index f97f13444f1c5946..2b35e5883c3e65c0 100644 +--- a/manual/dynlink.texi ++++ b/manual/dynlink.texi +@@ -482,13 +482,23 @@ The namespace identifier of @var{handle} is written to + @code{*@var{arg}}. The @var{arg} argument must be the address of an + object of type @code{Lmid_t}. + ++@item RTLD_DI_ORIGIN_PATH ++The @code{$ORIGIN} dynamic string token pointer for @var{handle} is written ++to @code{*@var{arg}}. The @var{arg} argument must be the address of a ++@code{const char *}. If the @code{$ORIGIN} could not be determined, the ++function still returns 0 but writes a null pointer to @code{*@var{arg}}. ++The returned string is only valid as long as the corresponding handle is ++valid. Accessing it after @code{dlclose} has been called for the ++corresponding @var{handle} is undefined behavior. ++ + @item RTLD_DI_ORIGIN + The value of the @code{$ORIGIN} dynamic string token for @var{handle} is + written to the character array starting at @var{arg} as a + null-terminated string. + + This request type should not be used because it is prone to buffer +-overflows. ++overflows. Instead, @code{RTLD_DI_ORIGIN_PATH} described above should be ++used. + + @item RTLD_DI_SERINFO + @itemx RTLD_DI_SERINFOSIZE diff --git a/SOURCES/patch-git-generated-commit.txt b/SOURCES/patch-git-generated-commit.txt index de3c433..4f0dab5 100644 --- a/SOURCES/patch-git-generated-commit.txt +++ b/SOURCES/patch-git-generated-commit.txt @@ -1,2 +1,2 @@ -eae38d8010e461f8f0d4a9ec27080eb89bc2e6fe +92cd2bbb684588d562a37ffea13a37cccebe428a v1 diff --git a/SOURCES/patch-git-generated-log.txt b/SOURCES/patch-git-generated-log.txt index 1e7e3c4..229a8dd 100644 --- a/SOURCES/patch-git-generated-log.txt +++ b/SOURCES/patch-git-generated-log.txt @@ -1,3 +1,402 @@ +commit 92cd2bbb684588d562a37ffea13a37cccebe428a +Author: DJ Delorie +AuthorDate: Wed Jul 29 13:35:13 2026 -0400 +Commit: DJ Delorie +CommitDate: Wed Jul 29 14:23:26 2026 -0400 + + Stop shipping glibc32-debuginfo (RHEL-219516) + + By stripping the symbol table from the glibc32 shared objects, + we disable the creation of debuginfo packages, which we do not + need nor want to ship. + + Resolves: RHEL-219516 + +:100644 100644 509fa59 c7c189a M glibc.spec + +commit 107c909712e52b9e6bd3792ac930d9436eb0ba8f +Author: Bruno Goncalves +AuthorDate: Fri Jun 19 14:28:54 2026 +0200 +Commit: Bruno Goncalves +CommitDate: Tue Jul 28 09:55:38 2026 +0200 + + CI Gating: drop kernel-qe gating + + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 9300333 2df044a M gating.yaml + +commit 4927d93cd55390c899eae233b64b508e599fc678 +Author: Patsy Griffin +AuthorDate: Fri Jul 10 17:09:17 2026 -0400 +Commit: pfrankli +CommitDate: Wed Jul 22 16:46:27 2026 +0000 + + iconv: Suppress intermediate errors with //TRANSLIT (RHEL-178279) + + Resolves: RHEL-178279 + +:000000 100644 0000000 ded65f1 A glibc-RHEL-178279.patch + +commit 16fb6ccc05ad3ba571a4adf64058fa7bc30e455f +Author: Lenka Špačková +AuthorDate: Wed Jul 15 15:47:43 2026 +0200 +Commit: Lenka Špačková +CommitDate: Wed Jul 15 15:47:43 2026 +0200 + + Regression/bz2115831-glibc-missing-gnu-debuglink-section-in: adjust relevancy for RHEL 9 + + Fixed in RHEL 9.8: RHEL-27851 + + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 36de426 b9c1fe0 M tests/Regression/bz2115831-glibc-missing-gnu-debuglink-section-in/main.fmf + +commit 1d22b956dda1e7fdc090165ae3bc8d882cd4e516 +Author: DJ Delorie +AuthorDate: Thu Jun 25 23:10:08 2026 -0400 +Commit: Frédéric Bérat +CommitDate: Tue Jul 7 12:54:03 2026 +0000 + + CVE-2026-5435 Out-of-bounds write via TSIG record processing + + Resolves: RHEL-180336 + Resolves: RHEL-185614 + +:000000 100644 0000000 9894622 A glibc-RHEL-180336-1.patch +:000000 100644 0000000 2f90da8 A glibc-RHEL-180336-2.patch +:000000 100644 0000000 cc495bc A glibc-RHEL-180336-3.patch +:000000 100644 0000000 acef669 A glibc-RHEL-180336-4.patch +:000000 100644 0000000 a019f27 A glibc-RHEL-180336-5.patch +:000000 100644 0000000 1222ad9 A glibc-RHEL-180336-6.patch +:000000 100644 0000000 3250e7b A glibc-RHEL-180336-7.patch + +commit 2acec79102345198aa8dfd4b8a89f75f11205a0b +Author: Patsy Griffin +AuthorDate: Fri Jun 26 11:52:03 2026 -0400 +Commit: Patsy Griffin +CommitDate: Fri Jun 26 11:52:03 2026 -0400 + + CVE-2026-5928: Fix ungetwc operating on byte stream (RHEL-180340) + + Resolves: RHEL-180340 + +:000000 100644 0000000 0771d0a A glibc-RHEL-180340.patch + +commit ec99e297ec21a794af038ae0b8ad9eae6a5b7a54 +Author: Patsy Griffin +AuthorDate: Mon Jun 22 06:53:56 2026 -0400 +Commit: pfrankli +CommitDate: Mon Jun 22 13:49:01 2026 +0000 + + CVE-2026-5450: Fix buffer overflow in scanf (RHEL-172710) + + Resolves: RHEL-172710 + +:000000 100644 0000000 1bd6427 A glibc-RHEL-172710-1.patch +:000000 100644 0000000 a334a8c A glibc-RHEL-172710-2.patch +:000000 100644 0000000 6bfe8c5 A glibc-RHEL-172710-3.patch +:000000 100644 0000000 12a03cd A glibc-RHEL-172710-4.patch + +commit 396dbae5399bc8ee97a617cc9ad4e1d95d26faf1 +Author: Sergey Kolosov +AuthorDate: Fri Jun 19 14:54:09 2026 +0200 +Commit: Sergey Kolosov +CommitDate: Fri Jun 19 14:54:09 2026 +0200 + + CI Gating: Add fail-fast test plan jobs as required rules + + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 ba7f921 9300333 M gating.yaml + +commit beeb5b354b9f5fd8745cc034694041b2ca91e31d +Author: Frédéric Bérat +AuthorDate: Thu Jun 4 11:49:13 2026 +0200 +Commit: Frédéric Bérat +CommitDate: Tue Jun 9 11:29:39 2026 +0200 + + Fix gconv module reference counter overflow in swscanf and swprintf families + + Resolves: RHEL-145156 + +:000000 100644 0000000 d591eea A glibc-RHEL-145156-1.patch +:000000 100644 0000000 01b7d9c A glibc-RHEL-145156-2.patch +:000000 100644 0000000 78b117e A glibc-RHEL-145156-3.patch +:000000 100644 0000000 62dec63 A glibc-RHEL-145156-4.patch +:000000 100644 0000000 41d91fd A glibc-RHEL-145156-5.patch + +commit 381936891b4e421cd9a03b682bcb5a66039fd230 +Author: Patsy Griffin +AuthorDate: Mon May 4 22:48:56 2026 -0400 +Commit: Florian Weimer +CommitDate: Sun May 10 17:40:44 2026 +0000 + + Fix incorrect setting of CXX variable in spec file (RHEL-148252) + + Resolves: RHEL-148252 + +:100644 100644 54e9fb1 509fa59 M glibc.spec + +commit c86a21c28e56e47fa7ff5507191d9f3146363574 +Author: Frédéric Bérat +AuthorDate: Wed May 6 10:07:53 2026 +0200 +Commit: Florian Weimer +CommitDate: Sun May 10 05:29:05 2026 +0000 + + Fix __nss_get_default_domain logic to restore netgroup user enumeration (RHEL-168096) + + Resolves: RHEL-168096 + +:000000 100644 0000000 cd80ed7 A glibc-RHEL-168096.patch + +commit a35ccbc1615d3cc93ac7427a3c3b3d2f4da64e55 +Author: Arjun Shankar +AuthorDate: Mon May 4 13:48:25 2026 +0200 +Commit: Florian Weimer +CommitDate: Sat May 9 08:46:30 2026 +0000 + + resolv: Correctly count records and check hostname validity (RHEL-168851, RHEL-168852) + + Resolves: RHEL-168851 + Resolves: RHEL-168852 + +:000000 100644 0000000 9e41b3f A glibc-RHEL-168851.patch +:000000 100644 0000000 159e4d5 A glibc-RHEL-168852.patch + +commit 437183f942920f2ae5a825d4a710edd5b0def2d1 +Author: Frédéric Bérat +AuthorDate: Thu Apr 30 15:34:53 2026 +0200 +Commit: Florian Weimer +CommitDate: Wed May 6 08:18:22 2026 +0000 + + CVE-2026-4046: Fix assertion failure in IBM1390 and IBM1399 iconv modules (RHEL-162901) + + Resolves: RHEL-162901 + +:000000 100644 0000000 39624a7 A glibc-RHEL-162901-1.patch +:000000 100644 0000000 b31887c A glibc-RHEL-162901-2.patch +:000000 100644 0000000 9294f79 A glibc-RHEL-162901-3.patch + +commit 7fbc71a39a05131ddc4e61bd013acc2e34c62ca5 +Author: Martin Coufal +AuthorDate: Wed Apr 29 13:22:27 2026 +0000 +Commit: Martin Coufal +CommitDate: Thu Apr 30 10:13:36 2026 +0200 + + gating.yaml: mark power9 and power10 specific jobs as required + + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 a1e2d55 ba7f921 M gating.yaml + +commit 6876da6c3cd740735f9227fa71494e3e72c91cdf +Author: Frédéric Bérat +AuthorDate: Tue Mar 24 15:29:18 2026 +0100 +Commit: Frédéric Bérat +CommitDate: Thu Mar 26 16:10:21 2026 +0000 + + Fix namespace violation in fortify wrappers (bug 32052) (RHEL-52966) + + Resolves: RHEL-52966 + +:000000 100644 0000000 52a65c1 A glibc-RHEL-52966.patch + +commit d19518e588c4477fcde9374ec70350a1bc382c0b +Author: Frédéric Bérat +AuthorDate: Tue Mar 24 14:18:52 2026 +0100 +Commit: Frédéric Bérat +CommitDate: Wed Mar 25 20:42:54 2026 +0000 + + Fix null pointer dereference and assertion failures in __nss_database_get (bug 28940) (RHEL-150269) + + Resolves: RHEL-150269 + +:000000 100644 0000000 0eda8a7 A glibc-RHEL-150269-1.patch +:000000 100644 0000000 3925565 A glibc-RHEL-150269-2.patch +:000000 100644 0000000 7c99f3d A glibc-RHEL-150269-3.patch +:000000 100644 0000000 f811d42 A glibc-RHEL-150269-4.patch + +commit 0e8e4ecae9fa126d2cea194d09042c554bdc420c +Author: Frédéric Bérat +AuthorDate: Tue Mar 24 11:01:32 2026 +0100 +Commit: Frédéric Bérat +CommitDate: Wed Mar 25 12:30:41 2026 +0000 + + Add LD_DEBUG=tls support for tracking TLS and TCB events (RHEL-49785) + + Resolves: RHEL-49785 + +:000000 100644 0000000 b9a8d92 A glibc-RHEL-49785-1.patch +:000000 100644 0000000 312faf7 A glibc-RHEL-49785-2.patch +:000000 100644 0000000 3969165 A glibc-RHEL-49785-3.patch +:000000 100644 0000000 6ee9ef6 A glibc-RHEL-49785-4.patch +:000000 100644 0000000 be0b0eb A glibc-RHEL-49785-5.patch +:000000 100644 0000000 e109ac5 A glibc-RHEL-49785-6.patch + +commit d58e05d599e91bf2d7597da217208b2ef4c92fc1 +Author: Florian Weimer +AuthorDate: Mon Mar 23 16:53:51 2026 +0100 +Commit: Florian Weimer +CommitDate: Mon Mar 23 16:53:51 2026 +0100 + + Avoid duplicate DNS query if search list contains '.' + + Resolves: RHEL-153056 + +:000000 100644 0000000 bf93cf7 A glibc-RHEL-153056-1.patch +:000000 100644 0000000 df43536 A glibc-RHEL-153056-2.patch +:000000 100644 0000000 ed5efd8 A glibc-RHEL-153056-3.patch + +commit 6a5da8faa95148af5ace04cd63a9e11686b61d12 +Author: Arjun Shankar +AuthorDate: Tue Mar 17 12:39:23 2026 +0100 +Commit: Arjun Shankar +CommitDate: Tue Mar 17 12:39:23 2026 +0100 + + dlfcn: Add dlinfo request type RTLD_DI_ORIGIN_PATH (RHEL-54450) + + Resolves: RHEL-54450 + +:000000 100644 0000000 ab7c187 A glibc-RHEL-54450.patch + +commit 7bff59bb7c59d40d81878bbe411782c7e393b538 +Author: Sergey Kolosov +AuthorDate: Mon Mar 2 20:38:08 2026 +0100 +Commit: Sergey Kolosov +CommitDate: Mon Mar 9 08:59:24 2026 +0000 + + CI Gating: update gating.yaml for taskrpm deprecation + + Replace the deprecated baseos-ci.brew-build.tier1.functional rule with + fast-line and slow-line rules for tier0, tier1 and testsuite tests. + + Related: RHEL-126175 + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 07eeedb a1e2d55 M gating.yaml + +commit e2834b00283401e1a1cb8c6a1768b2c2fdaa7836 +Author: Sergey Kolosov +AuthorDate: Tue Feb 24 09:51:00 2026 +0100 +Commit: Sergey Kolosov +CommitDate: Mon Mar 2 19:10:44 2026 +0100 + + CI Tests: remove conflicting glibc32 to fix prepare stage + + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 7249575 7749a2d M plans/ci.fmf + +commit ca2c60812dc5d34b77f94382c7340dbdf0ca2748 +Author: Sergey Kolosov +AuthorDate: Mon Feb 23 21:36:01 2026 +0100 +Commit: Sergey Kolosov +CommitDate: Mon Mar 2 19:10:44 2026 +0100 + + CI Tests: add Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has + + This test was originally missing during the import from Fedora test + plan. + + RPM-Changelog: - + RPM-Skip-Release: yes + +:000000 100644 0000000 2852f58 A tests/Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has/Makefile +:000000 100644 0000000 020631c A tests/Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has/PURPOSE +:000000 100644 0000000 45d1371 A tests/Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has/main.fmf +:000000 100755 0000000 d36bb2d A tests/Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has/runtest.sh +:000000 100644 0000000 b8a08ec A tests/Regression/bz1882466-RHEL8-2-LD-PRELOAD-of-some-lib-that-has/testlib.cc + +commit 1d7a0617ff0c43317425ee2a5b7379c7e2ef73ac +Author: Patsy Griffin +AuthorDate: Mon Feb 23 18:22:41 2026 -0500 +Commit: Frédéric Bérat +CommitDate: Thu Feb 26 10:03:06 2026 +0000 + + Remove default value for LD_PROFILE_OUTPUT + + Resolves: RHEL-142196 + +:000000 100644 0000000 fa52d55 A glibc-RHEL-142196-1.patch +:000000 100644 0000000 f12aeb8 A glibc-RHEL-142196-2.patch +:000000 100644 0000000 28567d2 A glibc-RHEL-142196-3.patch +:000000 100644 0000000 e1a8ab7 A glibc-RHEL-142196-4.patch +:000000 100644 0000000 84b027a A glibc-RHEL-142196-5.patch +:000000 100644 0000000 68cdcff A glibc-RHEL-142196-6.patch +:000000 100644 0000000 7997560 A glibc-RHEL-142196-7.patch +:000000 100644 0000000 a999fc8 A glibc-RHEL-142196-8.patch + +commit 8a9ddda3347d613915845ebc322ed2c38145b070 +Author: Frédéric Bérat +AuthorDate: Tue Feb 24 15:23:26 2026 +0100 +Commit: Frédéric Bérat +CommitDate: Tue Feb 24 15:23:26 2026 +0100 + + Fix race condition in io/tst-stat symlink test (RHEL-142209) + + Resolves: RHEL-142209 + +:000000 100644 0000000 0b0cc99 A glibc-RHEL-142209.patch + +commit a5df4f06f796a4c1f78aa9c8e06f4ca823431072 +Author: Frédéric Bérat +AuthorDate: Mon Feb 23 14:17:12 2026 +0100 +Commit: Frédéric Bérat +CommitDate: Mon Feb 23 14:17:12 2026 +0100 + + Restore iconv tool verbosity for unrecognized encoding names (RHEL-1018) + + Resolves: RHEL-1018 + +:000000 100644 0000000 ffe3c50 A glibc-RHEL-1018.patch + +commit 3b96603253e3faf6588dee6d8b20043566c6d5e4 +Author: Patsy Griffin +AuthorDate: Fri Feb 6 16:08:23 2026 -0500 +Commit: Florian Weimer +CommitDate: Mon Feb 16 07:43:17 2026 +0000 + + Optimize trylock for high cache contention workloads. + + Resolves: RHEL-141072 + +:000000 100644 0000000 496a62f A glibc-RHEL-141072.patch + +commit c7f278779ca2b29a5524121252ab11f051cfb8bb +Author: Patsy Griffin +AuthorDate: Tue Feb 10 18:53:05 2026 -0500 +Commit: Florian Weimer +CommitDate: Fri Feb 13 15:27:36 2026 +0000 + + Update locales for Croatia to use EUR as currency symbol + + Resolves: RHEL-140105 + +:000000 100644 0000000 1b763ae A glibc-RHEL-140105.patch + +commit 426ad63b6259338de1b2083c57dab64d16b06bb0 +Author: Sergey Kolosov +AuthorDate: Thu Feb 12 20:37:12 2026 +0100 +Commit: Florian Weimer +CommitDate: Fri Feb 13 15:21:49 2026 +0000 + + CI Tests: enable Regression/bz2115831-glibc-missing-gnu-debuglink-section-in + + Re-enable this test as RHEL-27851 has been fixed + + Related: RHEL-27851 + RPM-Changelog: - + RPM-Skip-Release: yes + +:100644 100644 4a3341c 36de426 M tests/Regression/bz2115831-glibc-missing-gnu-debuglink-section-in/main.fmf + commit eae38d8010e461f8f0d4a9ec27080eb89bc2e6fe Author: Florian Weimer AuthorDate: Thu Feb 12 15:17:05 2026 +0100 diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index 54e9fb1..c7c189a 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -1844,7 +1844,7 @@ build build-%{target}-32 \ # Default set of compiler options. build build-%{target} \ CC="gcc $glibc_flags_cc $glibc_flags_cc_main" \ - CXX="gcc $glibc_flags_cc $glibc_flags_cc_main" \ + CXX="g++ $glibc_flags_cc $glibc_flags_cc_main" \ CFLAGS="$glibc_flags_cflags" \ %{?glibc_rtld_early_cflags:--with-rtld-early-cflags=%glibc_rtld_early_cflags} \ %ifarch x86_64 @@ -1902,6 +1902,15 @@ rm -rf etc var usr/bin usr/lib/gconv usr/libexec usr/sbin usr/share rm -f lib/libnss_db* lib/libnss_hesiod* lib/libnsl* usr/lib/libnsl* usr/lib/libnss* rm usr/lib/libc_malloc_debug.so strip -g usr/lib/*.o +for f in usr/lib/*.so; do + case "$f" in + # libc.so is a text file, not an object + usr/lib/libc.so ) ;; + * ) strip $f ;; + esac +done +strip usr/lib/*.so.? +strip usr/lib/audit/*.so popd %endif