diff --git a/SOURCES/glibc-rh2172949.patch b/SOURCES/glibc-rh2172949.patch new file mode 100644 index 0000000..8ce2592 --- /dev/null +++ b/SOURCES/glibc-rh2172949.patch @@ -0,0 +1,121 @@ +commit 969e9733c7d17edf1e239a73fa172f357561f440 +Author: Florian Weimer +Date: Tue Feb 21 09:20:28 2023 +0100 + + gshadow: Matching sgetsgent, sgetsgent_r ERANGE handling (bug 30151) + + Before this change, sgetsgent_r did not set errno to ERANGE, but + sgetsgent only check errno, not the return value from sgetsgent_r. + Consequently, sgetsgent did not detect any error, and reported + success to the caller, without initializing the struct sgrp object + whose address was returned. + + This commit changes sgetsgent_r to set errno as well. This avoids + similar issues in applications which only change errno. + + Reviewed-by: Siddhesh Poyarekar + +diff --git a/gshadow/Makefile b/gshadow/Makefile +index 796fbbf473..a95524593a 100644 +--- a/gshadow/Makefile ++++ b/gshadow/Makefile +@@ -26,7 +26,7 @@ headers = gshadow.h + routines = getsgent getsgnam sgetsgent fgetsgent putsgent \ + getsgent_r getsgnam_r sgetsgent_r fgetsgent_r + +-tests = tst-gshadow tst-putsgent tst-fgetsgent_r ++tests = tst-gshadow tst-putsgent tst-fgetsgent_r tst-sgetsgent + + CFLAGS-getsgent_r.c += -fexceptions + CFLAGS-getsgent.c += -fexceptions +diff --git a/gshadow/sgetsgent_r.c b/gshadow/sgetsgent_r.c +index ea085e91d7..c75624e1f7 100644 +--- a/gshadow/sgetsgent_r.c ++++ b/gshadow/sgetsgent_r.c +@@ -61,7 +61,10 @@ __sgetsgent_r (const char *string, struct sgrp *resbuf, char *buffer, + buffer[buflen - 1] = '\0'; + sp = strncpy (buffer, string, buflen); + if (buffer[buflen - 1] != '\0') +- return ERANGE; ++ { ++ __set_errno (ERANGE); ++ return ERANGE; ++ } + } + else + sp = (char *) string; +diff --git a/gshadow/tst-sgetsgent.c b/gshadow/tst-sgetsgent.c +new file mode 100644 +index 0000000000..0370c10fd0 +--- /dev/null ++++ b/gshadow/tst-sgetsgent.c +@@ -0,0 +1,69 @@ ++/* Test large input for sgetsgent (bug 30151). ++ Copyright (C) 2023 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 ++ ++static int ++do_test (void) ++{ ++ /* Create a shadow group with 1000 members. */ ++ struct xmemstream mem; ++ xopen_memstream (&mem); ++ const char *passwd = "k+zD0nucwfxAo3sw1NXUj6K5vt5M16+X0TVGdE1uFvq5R8V7efJ"; ++ fprintf (mem.out, "group-name:%s::m0", passwd); ++ for (int i = 1; i < 1000; ++i) ++ fprintf (mem.out, ",m%d", i); ++ xfclose_memstream (&mem); ++ ++ /* Call sgetsgent. */ ++ char *input = mem.buffer; ++ struct sgrp *e = sgetsgent (input); ++ TEST_VERIFY_EXIT (e != NULL); ++ TEST_COMPARE_STRING (e->sg_namp, "group-name"); ++ TEST_COMPARE_STRING (e->sg_passwd, passwd); ++ /* No administrators. */ ++ TEST_COMPARE_STRING (e->sg_adm[0], NULL); ++ /* Check the members list. */ ++ for (int i = 0; i < 1000; ++i) ++ { ++ char *member = xasprintf ("m%d", i); ++ TEST_COMPARE_STRING (e->sg_mem[i], member); ++ free (member); ++ } ++ TEST_COMPARE_STRING (e->sg_mem[1000], NULL); ++ ++ /* Check that putsgent brings back the input string. */ ++ xopen_memstream (&mem); ++ TEST_COMPARE (putsgent (e, mem.out), 0); ++ xfclose_memstream (&mem); ++ /* Compare without the trailing '\n' that putsgent added. */ ++ TEST_COMPARE (mem.buffer[mem.length - 1], '\n'); ++ mem.buffer[mem.length - 1] = '\0'; ++ TEST_COMPARE_STRING (mem.buffer, input); ++ ++ free (mem.buffer); ++ free (input); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-rh2176707-1.patch b/SOURCES/glibc-rh2176707-1.patch new file mode 100644 index 0000000..6d17d83 --- /dev/null +++ b/SOURCES/glibc-rh2176707-1.patch @@ -0,0 +1,144 @@ +From 436a604b7dc741fc76b5a6704c6cd8bb178518e7 Mon Sep 17 00:00:00 2001 +From: Adam Yi +Date: Tue, 7 Mar 2023 07:30:02 -0500 +Subject: posix: Fix system blocks SIGCHLD erroneously [BZ #30163] + +Fix bug that SIGCHLD is erroneously blocked forever in the following +scenario: + +1. Thread A calls system but hasn't returned yet +2. Thread B calls another system but returns + +SIGCHLD would be blocked forever in thread B after its system() returns, +even after the system() in thread A returns. + +Although POSIX does not require, glibc system implementation aims to be +thread and cancellation safe. This bug was introduced in +5fb7fc96350575c9adb1316833e48ca11553be49 when we moved reverting signal +mask to happen when the last concurrently running system returns, +despite that signal mask is per thread. This commit reverts this logic +and adds a test. + +Signed-off-by: Adam Yi +Reviewed-by: Adhemerval Zanella + +[DJ: Edited to use integer sleep() instead of nanosleep() dependency rabbit hole] +diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c +index 634acfe264..47a0afe6bf 100644 +--- a/stdlib/tst-system.c ++++ b/stdlib/tst-system.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + static char *tmpdir; +@@ -71,6 +72,20 @@ call_system (void *closure) + } + } + ++static void * ++sleep_and_check_sigchld (void *closure) ++{ ++ double *seconds = (double *) closure; ++ char cmd[namemax]; ++ sprintf (cmd, "sleep %lf" , *seconds); ++ TEST_COMPARE (system (cmd), 0); ++ ++ sigset_t blocked = {0}; ++ TEST_COMPARE (sigprocmask (SIG_BLOCK, NULL, &blocked), 0); ++ TEST_COMPARE (sigismember (&blocked, SIGCHLD), 0); ++ return NULL; ++} ++ + static int + do_test (void) + { +@@ -154,6 +169,17 @@ do_test (void) + xchmod (_PATH_BSHELL, st.st_mode); + } + ++ { ++ pthread_t long_sleep_thread = xpthread_create (NULL, ++ sleep_and_check_sigchld, ++ &(double) { 2 }); ++ pthread_t short_sleep_thread = xpthread_create (NULL, ++ sleep_and_check_sigchld, ++ &(double) { 1 }); ++ xpthread_join (short_sleep_thread); ++ xpthread_join (long_sleep_thread); ++ } ++ + TEST_COMPARE (system (""), 0); + + return 0; +diff --git a/support/shell-container.c b/support/shell-container.c +index ffa3378b5e..b1f9e793c1 100644 +--- a/support/shell-container.c ++++ b/support/shell-container.c +@@ -169,6 +170,31 @@ kill_func (char **argv) + return 0; + } + ++/* Emulate the "/bin/sleep" command. No suffix support. Options are ++ ignored. */ ++static int ++sleep_func (char **argv) ++{ ++ if (argv[0] == NULL) ++ { ++ fprintf (stderr, "sleep: missing operand\n"); ++ return 1; ++ } ++ char *endptr = NULL; ++ long sec = strtol (argv[0], &endptr, 0); ++ if (endptr == argv[0] || errno == ERANGE || sec < 0) ++ { ++ fprintf (stderr, "sleep: invalid time interval '%s'\n", argv[0]); ++ return 1; ++ } ++ if (sleep (sec) < 0) ++ { ++ fprintf (stderr, "sleep: failed to nanosleep\n"); ++ return 1; ++ } ++ return 0; ++} ++ + /* This is a list of all the built-in commands we understand. */ + static struct { + const char *name; +@@ -179,6 +206,7 @@ static struct { + { "cp", copy_func }, + { "exit", exit_func }, + { "kill", kill_func }, ++ { "sleep", sleep_func }, + { NULL, NULL } + }; + +diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c +index 2335a99184..d77720a625 100644 +--- a/sysdeps/posix/system.c ++++ b/sysdeps/posix/system.c +@@ -179,16 +179,16 @@ do_system (const char *line) + as if the shell had terminated using _exit(127). */ + status = W_EXITCODE (127, 0); + ++ /* sigaction can not fail with SIGINT/SIGQUIT used with old ++ disposition. Same applies for sigprocmask. */ + DO_LOCK (); + if (SUB_REF () == 0) + { +- /* sigaction can not fail with SIGINT/SIGQUIT used with old +- disposition. Same applies for sigprocmask. */ + __sigaction (SIGINT, &intr, NULL); + __sigaction (SIGQUIT, &quit, NULL); +- __sigprocmask (SIG_SETMASK, &omask, NULL); + } + DO_UNLOCK (); ++ __sigprocmask (SIG_SETMASK, &omask, NULL); + + if (ret != 0) + __set_errno (ret); diff --git a/SOURCES/glibc-rh2176707-2.patch b/SOURCES/glibc-rh2176707-2.patch new file mode 100644 index 0000000..3968fc7 --- /dev/null +++ b/SOURCES/glibc-rh2176707-2.patch @@ -0,0 +1,27 @@ +From d03094649d39949a30513bf3ffb03a28fecbccd8 Mon Sep 17 00:00:00 2001 +From: Adam Yi +Date: Wed, 8 Mar 2023 03:11:47 -0500 +Subject: hurd: fix build of tst-system.c + +We made tst-system.c depend on pthread, but that requires linking with +$(shared-thread-library). It does not fail under Linux because the +variable expands to nothing under Linux, but it fails for Hurd. + +I tested verified via cross-compiling that "make check" now works +for Hurd. + +Signed-off-by: Adam Yi +Reviewed-by: Adhemerval Zanella + +[DJ: Edited for RHEL 8] +diff -rup a/stdlib/Makefile b/stdlib/Makefile +--- a/stdlib/Makefile 2023-07-07 00:44:55.810981644 -0400 ++++ b/stdlib/Makefile 2023-07-07 00:46:47.541411091 -0400 +@@ -102,6 +102,7 @@ LDLIBS-test-atexit-race = $(shared-threa + LDLIBS-test-at_quick_exit-race = $(shared-thread-library) + LDLIBS-test-cxa_atexit-race = $(shared-thread-library) + LDLIBS-test-on_exit-race = $(shared-thread-library) ++LDLIBS-tst-system = $(shared-thread-library) + + LDLIBS-test-dlclose-exit-race = $(shared-thread-library) $(libdl) + LDFLAGS-test-dlclose-exit-race = $(LDFLAGS-rdynamic) diff --git a/SOURCES/glibc-rh2176707-3.patch b/SOURCES/glibc-rh2176707-3.patch new file mode 100644 index 0000000..0131a71 --- /dev/null +++ b/SOURCES/glibc-rh2176707-3.patch @@ -0,0 +1,42 @@ +This patch is a RHEL-only patch which modifies the custom changes +in the previous patches in this series to make the test case look +more like the upstream test case. + +diff -rup a/stdlib/tst-system.c b/stdlib/tst-system.c +--- a/stdlib/tst-system.c 2023-07-10 13:37:53.089505036 -0400 ++++ b/stdlib/tst-system.c 2023-07-10 14:04:03.922610279 -0400 +@@ -173,10 +173,10 @@ do_test (void) + { + pthread_t long_sleep_thread = xpthread_create (NULL, + sleep_and_check_sigchld, +- &(double) { 2 }); ++ &(double) { 0.2 }); + pthread_t short_sleep_thread = xpthread_create (NULL, + sleep_and_check_sigchld, +- &(double) { 1 }); ++ &(double) { 0.1 }); + xpthread_join (short_sleep_thread); + xpthread_join (long_sleep_thread); + } +diff -rup a/support/shell-container.c b/support/shell-container.c +--- a/support/shell-container.c 2023-07-10 13:37:53.089505036 -0400 ++++ b/support/shell-container.c 2023-07-10 14:03:20.392920627 -0400 +@@ -182,15 +182,15 @@ sleep_func (char **argv) + return 1; + } + char *endptr = NULL; +- long sec = strtol (argv[0], &endptr, 0); ++ double sec = strtod (argv[0], &endptr); + if (endptr == argv[0] || errno == ERANGE || sec < 0) + { + fprintf (stderr, "sleep: invalid time interval '%s'\n", argv[0]); + return 1; + } +- if (sleep (sec) < 0) ++ if (usleep ((useconds_t)(sec * 1000000.0)) < 0) + { +- fprintf (stderr, "sleep: failed to nanosleep\n"); ++ fprintf (stderr, "sleep: failed to usleep: %s\n", strerror (errno)); + return 1; + } + return 0; diff --git a/SOURCES/glibc-rh2180155-1.patch b/SOURCES/glibc-rh2180155-1.patch new file mode 100644 index 0000000..f38a0ec --- /dev/null +++ b/SOURCES/glibc-rh2180155-1.patch @@ -0,0 +1,70 @@ +commit 801af9fafd4689337ebf27260aa115335a0cb2bc +Author: Леонид Юрьев (Leonid Yuriev) +Date: Sat Feb 4 14:41:38 2023 +0300 + + gmon: Fix allocated buffer overflow (bug 29444) + + The `__monstartup()` allocates a buffer used to store all the data + accumulated by the monitor. + + The size of this buffer depends on the size of the internal structures + used and the address range for which the monitor is activated, as well + as on the maximum density of call instructions and/or callable functions + that could be potentially on a segment of executable code. + + In particular a hash table of arcs is placed at the end of this buffer. + The size of this hash table is calculated in bytes as + p->fromssize = p->textsize / HASHFRACTION; + + but actually should be + p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); + + This results in writing beyond the end of the allocated buffer when an + added arc corresponds to a call near from the end of the monitored + address range, since `_mcount()` check the incoming caller address for + monitored range but not the intermediate result hash-like index that + uses to write into the table. + + It should be noted that when the results are output to `gmon.out`, the + table is read to the last element calculated from the allocated size in + bytes, so the arcs stored outside the buffer boundary did not fall into + `gprof` for analysis. Thus this "feature" help me to found this bug + during working with https://sourceware.org/bugzilla/show_bug.cgi?id=29438 + + Just in case, I will explicitly note that the problem breaks the + `make test t=gmon/tst-gmon-dso` added for Bug 29438. + There, the arc of the `f3()` call disappears from the output, since in + the DSO case, the call to `f3` is located close to the end of the + monitored range. + + Signed-off-by: Леонид Юрьев (Leonid Yuriev) + + Another minor error seems a related typo in the calculation of + `kcountsize`, but since kcounts are smaller than froms, this is + actually to align the p->froms data. + + Co-authored-by: DJ Delorie + Reviewed-by: Carlos O'Donell + +diff --git a/gmon/gmon.c b/gmon/gmon.c +index dee64803ada583d7..bf76358d5b1aa2da 100644 +--- a/gmon/gmon.c ++++ b/gmon/gmon.c +@@ -132,6 +132,8 @@ __monstartup (u_long lowpc, u_long highpc) + p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); + p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); + p->textsize = p->highpc - p->lowpc; ++ /* This looks like a typo, but it's here to align the p->froms ++ section. */ + p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms)); + p->hashfraction = HASHFRACTION; + p->log_hashfraction = -1; +@@ -142,7 +144,7 @@ __monstartup (u_long lowpc, u_long highpc) + instead of integer division. Precompute shift amount. */ + p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1; + } +- p->fromssize = p->textsize / HASHFRACTION; ++ p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); + p->tolimit = p->textsize * ARCDENSITY / 100; + if (p->tolimit < MINARCS) + p->tolimit = MINARCS; diff --git a/SOURCES/glibc-rh2180155-2.patch b/SOURCES/glibc-rh2180155-2.patch new file mode 100644 index 0000000..912bc92 --- /dev/null +++ b/SOURCES/glibc-rh2180155-2.patch @@ -0,0 +1,477 @@ +This patch adds the required @order directives to preserve the +GLIBC_PRIVATE ABI. + +commit 31be941e4367c001b2009308839db5c67bf9dcbc +Author: Simon Kissane +Date: Sat Feb 11 20:12:13 2023 +1100 + + gmon: improve mcount overflow handling [BZ# 27576] + + When mcount overflows, no gmon.out file is generated, but no message is printed + to the user, leaving the user with no idea why, and thinking maybe there is + some bug - which is how BZ 27576 ended up being logged. Print a message to + stderr in this case so the user knows what is going on. + + As a comment in sys/gmon.h acknowledges, the hardcoded MAXARCS value is too + small for some large applications, including the test case in that BZ. Rather + than increase it, add tunables to enable MINARCS and MAXARCS to be overridden + at runtime (glibc.gmon.minarcs and glibc.gmon.maxarcs). So if a user gets the + mcount overflow error, they can try increasing maxarcs (they might need to + increase minarcs too if the heuristic is wrong in their case.) + + Note setting minarcs/maxarcs too large can cause monstartup to fail with an + out of memory error. If you set them large enough, it can cause an integer + overflow in calculating the buffer size. I haven't done anything to defend + against that - it would not generally be a security vulnerability, since these + tunables will be ignored in suid/sgid programs (due to the SXID_ERASE default), + and if you can set GLIBC_TUNABLES in the environment of a process, you can take + it over anyway (LD_PRELOAD, LD_LIBRARY_PATH, etc). I thought about modifying + the code of monstartup to defend against integer overflows, but doing so is + complicated, and I realise the existing code is susceptible to them even prior + to this change (e.g. try passing a pathologically large highpc argument to + monstartup), so I decided just to leave that possibility in-place. + + Add a test case which demonstrates mcount overflow and the tunables. + + Document the new tunables in the manual. + + Signed-off-by: Simon Kissane + Reviewed-by: DJ Delorie + +Conflicts: + manual/tunables.texi + (missing tunables downstream) + +diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list +index f11ca5b3e8b09b43..dc2999796042dbaf 100644 +--- a/elf/dl-tunables.list ++++ b/elf/dl-tunables.list +@@ -149,4 +149,17 @@ glibc { + default: 2 + } + } ++ ++ gmon { ++ minarcs { ++ type: INT_32 ++ minval: 50 ++ default: 50 ++ } ++ maxarcs { ++ type: INT_32 ++ minval: 50 ++ default: 1048576 ++ } ++ } + } +diff --git a/gmon/Makefile b/gmon/Makefile +index d94593c9d8a882eb..54f05894d4dd8c4a 100644 +--- a/gmon/Makefile ++++ b/gmon/Makefile +@@ -25,7 +25,7 @@ include ../Makeconfig + headers := sys/gmon.h sys/gmon_out.h sys/profil.h + routines := gmon mcount profil sprofil prof-freq + +-tests = tst-sprofil tst-gmon ++tests = tst-sprofil tst-gmon tst-mcount-overflow + ifeq ($(build-profile),yes) + tests += tst-profile-static + tests-static += tst-profile-static +@@ -56,6 +56,18 @@ ifeq ($(run-built-tests),yes) + tests-special += $(objpfx)tst-gmon-gprof.out + endif + ++CFLAGS-tst-mcount-overflow.c := -fno-omit-frame-pointer -pg ++tst-mcount-overflow-no-pie = yes ++CRT-tst-mcount-overflow := $(csu-objpfx)g$(start-installed-name) ++# Intentionally use invalid config where maxarcs&1 1>/dev/null | cat ++ifeq ($(run-built-tests),yes) ++tests-special += $(objpfx)tst-mcount-overflow-check.out ++endif ++ + CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg + CRT-tst-gmon-static := $(csu-objpfx)gcrt1.o + tst-gmon-static-no-pie = yes +@@ -103,6 +115,14 @@ $(objpfx)tst-gmon.out: clean-tst-gmon-data + clean-tst-gmon-data: + rm -f $(objpfx)tst-gmon.data.* + ++$(objpfx)tst-mcount-overflow.o: clean-tst-mcount-overflow-data ++clean-tst-mcount-overflow-data: ++ rm -f $(objpfx)tst-mcount-overflow.data.* ++ ++$(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)tst-mcount-overflow.out ++ $(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \ ++ $(evaluate-test) ++ + $(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out + $(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \ + $(evaluate-test) +diff --git a/gmon/gmon.c b/gmon/gmon.c +index bf76358d5b1aa2da..689bf80141e559ca 100644 +--- a/gmon/gmon.c ++++ b/gmon/gmon.c +@@ -46,6 +46,11 @@ + #include + #include + ++#if HAVE_TUNABLES ++# define TUNABLE_NAMESPACE gmon ++# include ++#endif ++ + #ifdef PIC + # include + +@@ -124,6 +129,22 @@ __monstartup (u_long lowpc, u_long highpc) + int o; + char *cp; + struct gmonparam *p = &_gmonparam; ++ long int minarcs, maxarcs; ++ ++#if HAVE_TUNABLES ++ /* Read minarcs/maxarcs tunables. */ ++ minarcs = TUNABLE_GET (minarcs, int32_t, NULL); ++ maxarcs = TUNABLE_GET (maxarcs, int32_t, NULL); ++ if (maxarcs < minarcs) ++ { ++ ERR("monstartup: maxarcs < minarcs, setting maxarcs = minarcs\n"); ++ maxarcs = minarcs; ++ } ++#else ++ /* No tunables, we use hardcoded defaults */ ++ minarcs = MINARCS; ++ maxarcs = MAXARCS; ++#endif + + /* + * round lowpc and highpc to multiples of the density we're using +@@ -146,10 +167,10 @@ __monstartup (u_long lowpc, u_long highpc) + } + p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); + p->tolimit = p->textsize * ARCDENSITY / 100; +- if (p->tolimit < MINARCS) +- p->tolimit = MINARCS; +- else if (p->tolimit > MAXARCS) +- p->tolimit = MAXARCS; ++ if (p->tolimit < minarcs) ++ p->tolimit = minarcs; ++ else if (p->tolimit > maxarcs) ++ p->tolimit = maxarcs; + p->tossize = p->tolimit * sizeof(struct tostruct); + + cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); +diff --git a/gmon/mcount.c b/gmon/mcount.c +index 9d4a1a50fa6ab21a..f7180fdb83399a14 100644 +--- a/gmon/mcount.c ++++ b/gmon/mcount.c +@@ -41,6 +41,10 @@ static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93"; + + #include + ++#include ++#include ++#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1) ++ + /* + * mcount is called on entry to each function compiled with the profiling + * switch set. _mcount(), which is declared in a machine-dependent way +@@ -170,6 +174,7 @@ done: + return; + overflow: + p->state = GMON_PROF_ERROR; ++ ERR("mcount: call graph buffer size limit exceeded, gmon.out will not be generated\n"); + return; + } + +diff --git a/gmon/sys/gmon.h b/gmon/sys/gmon.h +index b4cc3b043a2aec77..af0582a3717085b5 100644 +--- a/gmon/sys/gmon.h ++++ b/gmon/sys/gmon.h +@@ -111,6 +111,8 @@ extern struct __bb *__bb_head; + * Always allocate at least this many tostructs. This + * hides the inadequacy of the ARCDENSITY heuristic, at least + * for small programs. ++ * ++ * Value can be overridden at runtime by glibc.gmon.minarcs tunable. + */ + #define MINARCS 50 + +@@ -124,8 +126,8 @@ extern struct __bb *__bb_head; + * Used to be max representable value of ARCINDEX minus 2, but now + * that ARCINDEX is a long, that's too large; we don't really want + * to allow a 48 gigabyte table. +- * The old value of 1<<16 wasn't high enough in practice for large C++ +- * programs; will 1<<20 be adequate for long? FIXME ++ * ++ * Value can be overridden at runtime by glibc.gmon.maxarcs tunable. + */ + #define MAXARCS (1 << 20) + +diff --git a/gmon/tst-mcount-overflow-check.sh b/gmon/tst-mcount-overflow-check.sh +new file mode 100644 +index 0000000000000000..27eb5538fd573a6e +--- /dev/null ++++ b/gmon/tst-mcount-overflow-check.sh +@@ -0,0 +1,45 @@ ++#!/bin/sh ++# Test expected messages generated when mcount overflows ++# Copyright (C) 2017-2023 Free Software Foundation, Inc. ++# Copyright 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 ++# . ++ ++LC_ALL=C ++export LC_ALL ++set -e ++exec 2>&1 ++ ++program="$1" ++ ++check_msg() { ++ if ! grep -q "$1" "$program.out"; then ++ echo "FAIL: expected message not in output: $1" ++ exit 1 ++ fi ++} ++ ++check_msg 'monstartup: maxarcs < minarcs, setting maxarcs = minarcs' ++check_msg 'mcount: call graph buffer size limit exceeded, gmon.out will not be generated' ++ ++for data_file in $1.data.*; do ++ if [ -f "$data_file" ]; then ++ echo "FAIL: expected no data files, but found $data_file" ++ exit 1 ++ fi ++done ++ ++echo PASS +diff --git a/gmon/tst-mcount-overflow.c b/gmon/tst-mcount-overflow.c +new file mode 100644 +index 0000000000000000..06cc93ef872eb7c1 +--- /dev/null ++++ b/gmon/tst-mcount-overflow.c +@@ -0,0 +1,72 @@ ++/* Test program to trigger mcount overflow in profiling collection. ++ Copyright (C) 2017-2023 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 ++ . */ ++ ++/* Program with sufficiently complex, yet pointless, call graph ++ that it will trigger an mcount overflow, when you set the ++ minarcs/maxarcs tunables to very low values. */ ++ ++#define PREVENT_TAIL_CALL asm volatile ("") ++ ++/* Calls REP(n) macro 16 times, for n=0..15. ++ * You need to define REP(n) before using this. ++ */ ++#define REPS \ ++ REP(0) REP(1) REP(2) REP(3) REP(4) REP(5) REP(6) REP(7) \ ++ REP(8) REP(9) REP(10) REP(11) REP(12) REP(13) REP(14) REP(15) ++ ++/* Defines 16 leaf functions named f1_0 to f1_15 */ ++#define REP(n) \ ++ __attribute__ ((noinline, noclone, weak)) void f1_##n (void) {}; ++REPS ++#undef REP ++ ++/* Calls all 16 leaf functions f1_* in succession */ ++__attribute__ ((noinline, noclone, weak)) void ++f2 (void) ++{ ++# define REP(n) f1_##n(); ++ REPS ++# undef REP ++ PREVENT_TAIL_CALL; ++} ++ ++/* Defines 16 functions named f2_0 to f2_15, which all just call f2 */ ++#define REP(n) \ ++ __attribute__ ((noinline, noclone, weak)) void \ ++ f2_##n (void) { f2(); PREVENT_TAIL_CALL; }; ++REPS ++#undef REP ++ ++__attribute__ ((noinline, noclone, weak)) void ++f3 (int count) ++{ ++ for (int i = 0; i < count; ++i) ++ { ++ /* Calls f1_0(), f2_0(), f1_1(), f2_1(), f3_0(), etc */ ++# define REP(n) f1_##n(); f2_##n(); ++ REPS ++# undef REP ++ } ++} ++ ++int ++main (void) ++{ ++ f3 (1000); ++ return 0; ++} +diff --git a/manual/tunables.texi b/manual/tunables.texi +index 7b70e80391ee87f7..00eafcf44b562b9e 100644 +--- a/manual/tunables.texi ++++ b/manual/tunables.texi +@@ -73,6 +73,9 @@ glibc.malloc.check: 0 (min: 0, max: 3) + * Elision Tunables:: Tunables in elision subsystem + * Hardware Capability Tunables:: Tunables that modify the hardware + capabilities seen by @theglibc{} ++* gmon Tunables:: Tunables that control the gmon profiler, used in ++ conjunction with gprof ++ + @end menu + + @node Tunable names +@@ -506,3 +509,59 @@ instead. + + This tunable is specific to i386 and x86-64. + @end deftp ++ ++@node gmon Tunables ++@section gmon Tunables ++@cindex gmon tunables ++ ++@deftp {Tunable namespace} glibc.gmon ++This tunable namespace affects the behaviour of the gmon profiler. ++gmon is a component of @theglibc{} which is normally used in ++conjunction with gprof. ++ ++When GCC compiles a program with the @code{-pg} option, it instruments ++the program with calls to the @code{mcount} function, to record the ++program's call graph. At program startup, a memory buffer is allocated ++to store this call graph; the size of the buffer is calculated using a ++heuristic based on code size. If during execution, the buffer is found ++to be too small, profiling will be aborted and no @file{gmon.out} file ++will be produced. In that case, you will see the following message ++printed to standard error: ++ ++@example ++mcount: call graph buffer size limit exceeded, gmon.out will not be generated ++@end example ++ ++Most of the symbols discussed in this section are defined in the header ++@code{sys/gmon.h}. However, some symbols (for example @code{mcount}) ++are not defined in any header file, since they are only intended to be ++called from code generated by the compiler. ++@end deftp ++ ++@deftp Tunable glibc.mem.minarcs ++The heuristic for sizing the call graph buffer is known to be ++insufficient for small programs; hence, the calculated value is clamped ++to be at least a minimum size. The default minimum (in units of ++call graph entries, @code{struct tostruct}), is given by the macro ++@code{MINARCS}. If you have some program with an unusually complex ++call graph, for which the heuristic fails to allocate enough space, ++you can use this tunable to increase the minimum to a larger value. ++@end deftp ++ ++@deftp Tunable glibc.mem.maxarcs ++To prevent excessive memory consumption when profiling very large ++programs, the call graph buffer is allowed to have a maximum of ++@code{MAXARCS} entries. For some very large programs, the default ++value of @code{MAXARCS} defined in @file{sys/gmon.h} is too small; in ++that case, you can use this tunable to increase it. ++ ++Note the value of the @code{maxarcs} tunable must be greater or equal ++to that of the @code{minarcs} tunable; if this constraint is violated, ++a warning will printed to standard error at program startup, and ++the @code{minarcs} value will be used as the maximum as well. ++ ++Setting either tunable too high may result in a call graph buffer ++whose size exceeds the available memory; in that case, an out of memory ++error will be printed at program startup, the profiler will be ++disabled, and no @file{gmon.out} file will be generated. ++@end deftp +diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list b/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list +index 5c3c5292025607a1..265f82ef2be42fd0 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list ++++ b/sysdeps/unix/sysv/linux/aarch64/dl-tunables.list +@@ -24,3 +24,7 @@ + + # Tunables added in RHEL 8.8.0 + @order glibc.rtld.dynamic_sort ++ ++# Tunables added in RHEL 8.9.0 ++@order glibc.gmon.minarcs ++@order glibc.gmon.maxarcs +diff --git a/sysdeps/unix/sysv/linux/i386/dl-tunables.list b/sysdeps/unix/sysv/linux/i386/dl-tunables.list +index b9cad4af62d9f2e5..9c1ccb86501c61e7 100644 +--- a/sysdeps/unix/sysv/linux/i386/dl-tunables.list ++++ b/sysdeps/unix/sysv/linux/i386/dl-tunables.list +@@ -31,3 +31,7 @@ + + # Tunables added in RHEL 8.8.0 + @order glibc.rtld.dynamic_sort ++ ++# Tunables added in RHEL 8.9.0 ++@order glibc.gmon.minarcs ++@order glibc.gmon.maxarcs +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list +index ee1e6fca95e1f2da..c8bb1a8ec0283ac8 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/dl-tunables.list +@@ -24,3 +24,7 @@ + + # Tunables added in RHEL 8.8.0 + @order glibc.rtld.dynamic_sort ++ ++# Tunables added in RHEL 8.9.0 ++@order glibc.gmon.minarcs ++@order glibc.gmon.maxarcs +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list b/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list +index 099e28d8f8e67944..85b3a014ffcadc45 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/dl-tunables.list +@@ -23,3 +23,7 @@ + + # Tunables added in RHEL 8.8.0 + @order glibc.rtld.dynamic_sort ++ ++# Tunables added in RHEL 8.9.0 ++@order glibc.gmon.minarcs ++@order glibc.gmon.maxarcs +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list b/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list +index b9cad4af62d9f2e5..9c1ccb86501c61e7 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list ++++ b/sysdeps/unix/sysv/linux/x86_64/64/dl-tunables.list +@@ -31,3 +31,7 @@ + + # Tunables added in RHEL 8.8.0 + @order glibc.rtld.dynamic_sort ++ ++# Tunables added in RHEL 8.9.0 ++@order glibc.gmon.minarcs ++@order glibc.gmon.maxarcs diff --git a/SOURCES/glibc-rh2180155-3.patch b/SOURCES/glibc-rh2180155-3.patch new file mode 100644 index 0000000..af5f7ec --- /dev/null +++ b/SOURCES/glibc-rh2180155-3.patch @@ -0,0 +1,191 @@ +commit bde121872001d8f3224eeafa5b7effb871c3fbca +Author: Simon Kissane +Date: Sat Feb 11 08:58:02 2023 +1100 + + gmon: fix memory corruption issues [BZ# 30101] + + V2 of this patch fixes an issue in V1, where the state was changed to ON not + OFF at end of _mcleanup. I hadn't noticed that (counterintuitively) ON=0 and + OFF=3, hence zeroing the buffer turned it back on. So set the state to OFF + after the memset. + + 1. Prevent double free, and reads from unallocated memory, when + _mcleanup is (incorrectly) called two or more times in a row, + without an intervening call to __monstartup; with this patch, the + second and subsequent calls effectively become no-ops instead. + While setting tos=NULL is minimal fix, safest action is to zero the + whole gmonparam buffer. + + 2. Prevent memory leak when __monstartup is (incorrectly) called two + or more times in a row, without an intervening call to _mcleanup; + with this patch, the second and subsequent calls effectively become + no-ops instead. + + 3. After _mcleanup, treat __moncontrol(1) as __moncontrol(0) instead. + With zeroing of gmonparam buffer in _mcleanup, this stops the + state incorrectly being changed to GMON_PROF_ON despite profiling + actually being off. If we'd just done the minimal fix to _mcleanup + of setting tos=NULL, there is risk of far worse memory corruption: + kcount would point to deallocated memory, and the __profil syscall + would make the kernel write profiling data into that memory, + which could have since been reallocated to something unrelated. + + 4. Ensure __moncontrol(0) still turns off profiling even in error + state. Otherwise, if mcount overflows and sets state to + GMON_PROF_ERROR, when _mcleanup calls __moncontrol(0), the __profil + syscall to disable profiling will not be invoked. _mcleanup will + free the buffer, but the kernel will still be writing profiling + data into it, potentially corrupted arbitrary memory. + + Also adds a test case for (1). Issues (2)-(4) are not feasible to test. + + Signed-off-by: Simon Kissane + Reviewed-by: DJ Delorie + +Conflicts: + gmon/Makefile + (copyright year update) + +diff --git a/gmon/Makefile b/gmon/Makefile +index 54f05894d4dd8c4a..1bc4ad6e14e292a9 100644 +--- a/gmon/Makefile ++++ b/gmon/Makefile +@@ -1,4 +1,5 @@ +-# Copyright (C) 1995-2018 Free Software Foundation, Inc. ++# Copyright (C) 1995-2023 Free Software Foundation, Inc. ++# Copyright 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 +@@ -25,7 +26,7 @@ include ../Makeconfig + headers := sys/gmon.h sys/gmon_out.h sys/profil.h + routines := gmon mcount profil sprofil prof-freq + +-tests = tst-sprofil tst-gmon tst-mcount-overflow ++tests = tst-sprofil tst-gmon tst-mcount-overflow tst-mcleanup + ifeq ($(build-profile),yes) + tests += tst-profile-static + tests-static += tst-profile-static +@@ -68,6 +69,14 @@ ifeq ($(run-built-tests),yes) + tests-special += $(objpfx)tst-mcount-overflow-check.out + endif + ++CFLAGS-tst-mcleanup.c := -fno-omit-frame-pointer -pg ++tst-mcleanup-no-pie = yes ++CRT-tst-mcleanup := $(csu-objpfx)g$(start-installed-name) ++tst-mcleanup-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcleanup.data ++ifeq ($(run-built-tests),yes) ++tests-special += $(objpfx)tst-mcleanup.out ++endif ++ + CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg + CRT-tst-gmon-static := $(csu-objpfx)gcrt1.o + tst-gmon-static-no-pie = yes +@@ -123,6 +132,10 @@ $(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)ts + $(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \ + $(evaluate-test) + ++$(objpfx)tst-mcleanup.out: clean-tst-mcleanup-data ++clean-tst-mcleanup-data: ++ rm -f $(objpfx)tst-mcleanup.data.* ++ + $(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out + $(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \ + $(evaluate-test) +diff --git a/gmon/gmon.c b/gmon/gmon.c +index 689bf80141e559ca..5e99a7351dc71666 100644 +--- a/gmon/gmon.c ++++ b/gmon/gmon.c +@@ -102,11 +102,8 @@ __moncontrol (int mode) + { + struct gmonparam *p = &_gmonparam; + +- /* Don't change the state if we ran into an error. */ +- if (p->state == GMON_PROF_ERROR) +- return; +- +- if (mode) ++ /* Treat start request as stop if error or gmon not initialized. */ ++ if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL) + { + /* start */ + __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale); +@@ -116,7 +113,9 @@ __moncontrol (int mode) + { + /* stop */ + __profil(NULL, 0, 0, 0); +- p->state = GMON_PROF_OFF; ++ /* Don't change the state if we ran into an error. */ ++ if (p->state != GMON_PROF_ERROR) ++ p->state = GMON_PROF_OFF; + } + } + libc_hidden_def (__moncontrol) +@@ -146,6 +145,14 @@ __monstartup (u_long lowpc, u_long highpc) + maxarcs = MAXARCS; + #endif + ++ /* ++ * If we are incorrectly called twice in a row (without an ++ * intervening call to _mcleanup), ignore the second call to ++ * prevent leaking memory. ++ */ ++ if (p->tos != NULL) ++ return; ++ + /* + * round lowpc and highpc to multiples of the density we're using + * so the rest of the scaling (here and in gprof) stays in ints. +@@ -463,9 +470,14 @@ _mcleanup (void) + { + __moncontrol (0); + +- if (_gmonparam.state != GMON_PROF_ERROR) ++ if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL) + write_gmon (); + + /* free the memory. */ + free (_gmonparam.tos); ++ ++ /* reset buffer to initial state for safety */ ++ memset(&_gmonparam, 0, sizeof _gmonparam); ++ /* somewhat confusingly, ON=0, OFF=3 */ ++ _gmonparam.state = GMON_PROF_OFF; + } +diff --git a/gmon/tst-mcleanup.c b/gmon/tst-mcleanup.c +new file mode 100644 +index 0000000000000000..b259653ec833aca4 +--- /dev/null ++++ b/gmon/tst-mcleanup.c +@@ -0,0 +1,31 @@ ++/* Test program for repeated invocation of _mcleanup ++ Copyright 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 ++ . */ ++ ++/* Intentionally calls _mcleanup() twice: once manually, it will be ++ called again as an atexit handler. This is incorrect use of the API, ++ but the point of the test is to make sure we don't crash when the ++ API is misused in this way. */ ++ ++#include ++ ++int ++main (void) ++{ ++ _mcleanup(); ++ return 0; ++} diff --git a/SOURCES/glibc-rh2180462-1.patch b/SOURCES/glibc-rh2180462-1.patch new file mode 100644 index 0000000..3a5d685 --- /dev/null +++ b/SOURCES/glibc-rh2180462-1.patch @@ -0,0 +1,216 @@ +From af992e7abdc9049714da76cae1e5e18bc4838fb8 Mon Sep 17 00:00:00 2001 +From: Noah Goldstein +Date: Wed, 7 Jun 2023 13:18:01 -0500 +Subject: [PATCH] x86: Increase `non_temporal_threshold` to roughly `sizeof_L3 + / 4` +Content-type: text/plain; charset=UTF-8 + +Current `non_temporal_threshold` set to roughly '3/4 * sizeof_L3 / +ncores_per_socket'. This patch updates that value to roughly +'sizeof_L3 / 4` + +The original value (specifically dividing the `ncores_per_socket`) was +done to limit the amount of other threads' data a `memcpy`/`memset` +could evict. + +Dividing by 'ncores_per_socket', however leads to exceedingly low +non-temporal thresholds and leads to using non-temporal stores in +cases where REP MOVSB is multiple times faster. + +Furthermore, non-temporal stores are written directly to main memory +so using it at a size much smaller than L3 can place soon to be +accessed data much further away than it otherwise could be. As well, +modern machines are able to detect streaming patterns (especially if +REP MOVSB is used) and provide LRU hints to the memory subsystem. This +in affect caps the total amount of eviction at 1/cache_associativity, +far below meaningfully thrashing the entire cache. + +As best I can tell, the benchmarks that lead this small threshold +where done comparing non-temporal stores versus standard cacheable +stores. A better comparison (linked below) is to be REP MOVSB which, +on the measure systems, is nearly 2x faster than non-temporal stores +at the low-end of the previous threshold, and within 10% for over +100MB copies (well past even the current threshold). In cases with a +low number of threads competing for bandwidth, REP MOVSB is ~2x faster +up to `sizeof_L3`. + +The divisor of `4` is a somewhat arbitrary value. From benchmarks it +seems Skylake and Icelake both prefer a divisor of `2`, but older CPUs +such as Broadwell prefer something closer to `8`. This patch is meant +to be followed up by another one to make the divisor cpu-specific, but +in the meantime (and for easier backporting), this patch settles on +`4` as a middle-ground. + +Benchmarks comparing non-temporal stores, REP MOVSB, and cacheable +stores where done using: +https://github.com/goldsteinn/memcpy-nt-benchmarks + +Sheets results (also available in pdf on the github): +https://docs.google.com/spreadsheets/d/e/2PACX-1vS183r0rW_jRX6tG_E90m9qVuFiMbRIJvi5VAE8yYOvEOIEEc3aSNuEsrFbuXw5c3nGboxMmrupZD7K/pubhtml +Reviewed-by: DJ Delorie +Reviewed-by: Carlos O'Donell +--- + sysdeps/x86/dl-cacheinfo.h | 70 +++++++++++++++++++++++--------------- + 1 file changed, 43 insertions(+), 27 deletions(-) + +[DJ - ported to C8S] + +diff -rup a/sysdeps/x86/cacheinfo.h b/sysdeps/x86/cacheinfo.h +--- a/sysdeps/x86/cacheinfo.h 2023-08-08 11:54:09.969791421 -0400 ++++ b/sysdeps/x86/cacheinfo.h 2023-08-08 13:44:55.185333601 -0400 +@@ -46,7 +46,7 @@ long int __x86_rep_movsb_threshold attri + long int __x86_rep_stosb_threshold attribute_hidden = 2048; + + static void +-get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, ++get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, unsigned int *threads_ptr, + long int core) + { + unsigned int eax; +@@ -65,6 +65,7 @@ get_common_cache_info (long int *shared_ + unsigned int family = cpu_features->basic.family; + unsigned int model = cpu_features->basic.model; + long int shared = *shared_ptr; ++ long int shared_per_thread = *shared_per_thread_ptr; + unsigned int threads = *threads_ptr; + bool inclusive_cache = true; + bool support_count_mask = true; +@@ -80,6 +81,7 @@ get_common_cache_info (long int *shared_ + /* Try L2 otherwise. */ + level = 2; + shared = core; ++ shared_per_thread = core; + threads_l2 = 0; + threads_l3 = -1; + } +@@ -236,29 +238,28 @@ get_common_cache_info (long int *shared_ + } + else + { +-intel_bug_no_cache_info: +- /* Assume that all logical threads share the highest cache +- level. */ +- threads +- = ((cpu_features->features[COMMON_CPUID_INDEX_1].cpuid.ebx +- >> 16) & 0xff); +- } +- +- /* Cap usage of highest cache level to the number of supported +- threads. */ +- if (shared > 0 && threads > 0) +- shared /= threads; ++ intel_bug_no_cache_info: ++ /* Assume that all logical threads share the highest cache ++ level. */ ++ threads = ((cpu_features->features[COMMON_CPUID_INDEX_1].cpuid.ebx >> 16) ++ & 0xff); ++ ++ /* Get per-thread size of highest level cache. */ ++ if (shared_per_thread > 0 && threads > 0) ++ shared_per_thread /= threads; ++ } + } + + /* Account for non-inclusive L2 and L3 caches. */ + if (!inclusive_cache) + { + if (threads_l2 > 0) +- core /= threads_l2; ++ shared_per_thread += core / threads_l2; + shared += core; + } + + *shared_ptr = shared; ++ *shared_per_thread_ptr = shared_per_thread; + *threads_ptr = threads; + } + +@@ -272,6 +273,7 @@ init_cacheinfo (void) + int max_cpuid_ex; + long int data = -1; + long int shared = -1; ++ long int shared_per_thread = -1; + long int core; + unsigned int threads = 0; + const struct cpu_features *cpu_features = __get_cpu_features (); +@@ -287,22 +289,25 @@ init_cacheinfo (void) + data = handle_intel (_SC_LEVEL1_DCACHE_SIZE, cpu_features); + core = handle_intel (_SC_LEVEL2_CACHE_SIZE, cpu_features); + shared = handle_intel (_SC_LEVEL3_CACHE_SIZE, cpu_features); ++ shared_per_thread = shared; + +- get_common_cache_info (&shared, &threads, core); ++ get_common_cache_info (&shared, &shared_per_thread, &threads, core); + } + else if (cpu_features->basic.kind == arch_kind_zhaoxin) + { + data = handle_zhaoxin (_SC_LEVEL1_DCACHE_SIZE); + core = handle_zhaoxin (_SC_LEVEL2_CACHE_SIZE); + shared = handle_zhaoxin (_SC_LEVEL3_CACHE_SIZE); ++ shared_per_thread = shared; + +- get_common_cache_info (&shared, &threads, core); ++ get_common_cache_info (&shared, &shared_per_thread, &threads, core); + } + else if (cpu_features->basic.kind == arch_kind_amd) + { + data = handle_amd (_SC_LEVEL1_DCACHE_SIZE); + long int core = handle_amd (_SC_LEVEL2_CACHE_SIZE); + shared = handle_amd (_SC_LEVEL3_CACHE_SIZE); ++ shared_per_thread = shared; + + /* Get maximum extended function. */ + __cpuid (0x80000000, max_cpuid_ex, ebx, ecx, edx); +@@ -352,6 +357,9 @@ init_cacheinfo (void) + shared += core; + } + } ++ ++ if (shared_per_thread <= 0) ++ shared_per_thread = shared; + } + + if (cpu_features->data_cache_size != 0) +@@ -380,20 +388,30 @@ init_cacheinfo (void) + __x86_shared_cache_size = shared; + } + +- /* The default setting for the non_temporal threshold is 3/4 of one +- thread's share of the chip's cache. For most Intel and AMD processors +- with an initial release date between 2017 and 2020, a thread's typical +- share of the cache is from 500 KBytes to 2 MBytes. Using the 3/4 +- threshold leaves 125 KBytes to 500 KBytes of the thread's data +- in cache after a maximum temporal copy, which will maintain +- in cache a reasonable portion of the thread's stack and other +- active data. If the threshold is set higher than one thread's +- share of the cache, it has a substantial risk of negatively +- impacting the performance of other threads running on the chip. */ ++ /* The default setting for the non_temporal threshold is 1/4 of size ++ of the chip's cache. For most Intel and AMD processors with an ++ initial release date between 2017 and 2023, a thread's typical ++ share of the cache is from 18-64MB. Using the 1/4 L3 is meant to ++ estimate the point where non-temporal stores begin out-competing ++ REP MOVSB. As well the point where the fact that non-temporal ++ stores are forced back to main memory would already occurred to the ++ majority of the lines in the copy. Note, concerns about the ++ entire L3 cache being evicted by the copy are mostly alleviated ++ by the fact that modern HW detects streaming patterns and ++ provides proper LRU hints so that the maximum thrashing ++ capped at 1/associativity. */ ++ unsigned long int non_temporal_threshold = shared / 4; ++ /* If no ERMS, we use the per-thread L3 chunking. Normal cacheable stores run ++ a higher risk of actually thrashing the cache as they don't have a HW LRU ++ hint. As well, their performance in highly parallel situations is ++ noticeably worse. */ ++ if (!CPU_FEATURE_USABLE_P (cpu_features, ERMS)) ++ non_temporal_threshold = shared_per_thread * 3 / 4; ++ + __x86_shared_non_temporal_threshold + = (cpu_features->non_temporal_threshold != 0 + ? cpu_features->non_temporal_threshold +- : __x86_shared_cache_size * 3 / 4); ++ : non_temporal_threshold); + + /* NB: The REP MOVSB threshold must be greater than VEC_SIZE * 8. */ + unsigned int minimum_rep_movsb_threshold; +Only in b/sysdeps/x86: cacheinfo.h~ diff --git a/SOURCES/glibc-rh2180462-2.patch b/SOURCES/glibc-rh2180462-2.patch new file mode 100644 index 0000000..35d246b --- /dev/null +++ b/SOURCES/glibc-rh2180462-2.patch @@ -0,0 +1,47 @@ +From 47f747217811db35854ea06741be3685e8bbd44d Mon Sep 17 00:00:00 2001 +From: Noah Goldstein +Date: Mon, 17 Jul 2023 23:14:33 -0500 +Subject: [PATCH] x86: Fix slight bug in `shared_per_thread` cache size + calculation. +Content-type: text/plain; charset=UTF-8 + +After: +``` + commit af992e7abdc9049714da76cae1e5e18bc4838fb8 + Author: Noah Goldstein + Date: Wed Jun 7 13:18:01 2023 -0500 + + x86: Increase `non_temporal_threshold` to roughly `sizeof_L3 / 4` +``` + +Split `shared` (cumulative cache size) from `shared_per_thread` (cache +size per socket), the `shared_per_thread` *can* be slightly off from +the previous calculation. + +Previously we added `core` even if `threads_l2` was invalid, and only +used `threads_l2` to divide `core` if it was present. The changed +version only included `core` if `threads_l2` was valid. + +This change restores the old behavior if `threads_l2` is invalid by +adding the entire value of `core`. +Reviewed-by: DJ Delorie +--- + sysdeps/x86/dl-cacheinfo.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +[DJ - ported to C8S] + +diff -rup b1/sysdeps/x86/cacheinfo.h b2/sysdeps/x86/cacheinfo.h +--- b1/sysdeps/x86/cacheinfo.h 2023-08-08 13:44:55.185333601 -0400 ++++ b2/sysdeps/x86/cacheinfo.h 2023-08-08 13:55:16.474680016 -0400 +@@ -253,8 +253,8 @@ get_common_cache_info (long int *shared_ + /* Account for non-inclusive L2 and L3 caches. */ + if (!inclusive_cache) + { +- if (threads_l2 > 0) +- shared_per_thread += core / threads_l2; ++ long int core_per_thread = threads_l2 > 0 ? (core / threads_l2) : core; ++ shared_per_thread += core_per_thread; + shared += core; + } + diff --git a/SOURCES/glibc-rh2180462-3.patch b/SOURCES/glibc-rh2180462-3.patch new file mode 100644 index 0000000..fd001d3 --- /dev/null +++ b/SOURCES/glibc-rh2180462-3.patch @@ -0,0 +1,44 @@ +From 8b9a0af8ca012217bf90d1dc0694f85b49ae09da Mon Sep 17 00:00:00 2001 +From: Noah Goldstein +Date: Tue, 18 Jul 2023 10:27:59 -0500 +Subject: [PATCH] [PATCH v1] x86: Use `3/4*sizeof(per-thread-L3)` as low bound + for NT threshold. +Content-type: text/plain; charset=UTF-8 + +On some machines we end up with incomplete cache information. This can +make the new calculation of `sizeof(total-L3)/custom-divisor` end up +lower than intended (and lower than the prior value). So reintroduce +the old bound as a lower bound to avoid potentially regressing code +where we don't have complete information to make the decision. +Reviewed-by: DJ Delorie +--- + sysdeps/x86/dl-cacheinfo.h | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +[DJ - ported to C8S] + +diff -rup b2/sysdeps/x86/cacheinfo.h b3/sysdeps/x86/cacheinfo.h +--- b2/sysdeps/x86/cacheinfo.h 2023-08-08 13:55:16.474680016 -0400 ++++ b3/sysdeps/x86/cacheinfo.h 2023-08-08 13:59:14.507988958 -0400 +@@ -401,12 +401,20 @@ init_cacheinfo (void) + provides proper LRU hints so that the maximum thrashing + capped at 1/associativity. */ + unsigned long int non_temporal_threshold = shared / 4; ++ /* If the computed non_temporal_threshold <= 3/4 * per-thread L3, we most ++ likely have incorrect/incomplete cache info in which case, default to ++ 3/4 * per-thread L3 to avoid regressions. */ ++ unsigned long int non_temporal_threshold_lowbound ++ = shared_per_thread * 3 / 4; ++ if (non_temporal_threshold < non_temporal_threshold_lowbound) ++ non_temporal_threshold = non_temporal_threshold_lowbound; ++ + /* If no ERMS, we use the per-thread L3 chunking. Normal cacheable stores run + a higher risk of actually thrashing the cache as they don't have a HW LRU + hint. As well, their performance in highly parallel situations is + noticeably worse. */ + if (!CPU_FEATURE_USABLE_P (cpu_features, ERMS)) +- non_temporal_threshold = shared_per_thread * 3 / 4; ++ non_temporal_threshold = non_temporal_threshold_lowbound; + + __x86_shared_non_temporal_threshold + = (cpu_features->non_temporal_threshold != 0 diff --git a/SOURCES/glibc-rh2180462-4.patch b/SOURCES/glibc-rh2180462-4.patch new file mode 100644 index 0000000..e0c568a --- /dev/null +++ b/SOURCES/glibc-rh2180462-4.patch @@ -0,0 +1,39 @@ +Adjusted for backport to c8s by modifying sysdeps/x86/cacheinfo.h. + +commit 885a7f0feee951f514a121788f46f33b2867110f +Author: Noah Goldstein +Date: Fri Aug 11 12:29:11 2023 -0500 + + x86: Fix incorrect scope of setting `shared_per_thread` [BZ# 30745] + + The: + + ``` + if (shared_per_thread > 0 && threads > 0) + shared_per_thread /= threads; + ``` + + Code was accidentally moved to inside the else scope. This doesn't + match how it was previously (before af992e7abd). + + This patch fixes that by putting the division after the `else` block. + +diff --git a/sysdeps/x86/cacheinfo.h b/sysdeps/x86/cacheinfo.h +index 4dbfa979ef052eaa..e53fa25106c95253 100644 +--- a/sysdeps/x86/cacheinfo.h ++++ b/sysdeps/x86/cacheinfo.h +@@ -243,11 +243,10 @@ get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, u + level. */ + threads = ((cpu_features->features[COMMON_CPUID_INDEX_1].cpuid.ebx >> 16) + & 0xff); +- +- /* Get per-thread size of highest level cache. */ +- if (shared_per_thread > 0 && threads > 0) +- shared_per_thread /= threads; + } ++ /* Get per-thread size of highest level cache. */ ++ if (shared_per_thread > 0 && threads > 0) ++ shared_per_thread /= threads; + } + + /* Account for non-inclusive L2 and L3 caches. */ diff --git a/SOURCES/glibc-rh2183081-1.patch b/SOURCES/glibc-rh2183081-1.patch new file mode 100644 index 0000000..9c3829d --- /dev/null +++ b/SOURCES/glibc-rh2183081-1.patch @@ -0,0 +1,35 @@ +commit 5d1ccdda7b0c625751661d50977f3dfbc73f8eae +Author: Florian Weimer +Date: Mon Apr 3 17:23:11 2023 +0200 + + x86_64: Fix asm constraints in feraiseexcept (bug 30305) + + The divss instruction clobbers its first argument, and the constraints + need to reflect that. Fortunately, with GCC 12, generated code does + not actually change, so there is no externally visible bug. + + Suggested-by: Jakub Jelinek + Reviewed-by: Noah Goldstein + +diff --git a/sysdeps/x86_64/fpu/fraiseexcpt.c b/sysdeps/x86_64/fpu/fraiseexcpt.c +index ca1c223053bf016b..fb886ed540b52100 100644 +--- a/sysdeps/x86_64/fpu/fraiseexcpt.c ++++ b/sysdeps/x86_64/fpu/fraiseexcpt.c +@@ -33,7 +33,7 @@ __feraiseexcept (int excepts) + /* One example of an invalid operation is 0.0 / 0.0. */ + float f = 0.0; + +- __asm__ __volatile__ ("divss %0, %0 " : : "x" (f)); ++ __asm__ __volatile__ ("divss %0, %0 " : "+x" (f)); + (void) &f; + } + +@@ -43,7 +43,7 @@ __feraiseexcept (int excepts) + float f = 1.0; + float g = 0.0; + +- __asm__ __volatile__ ("divss %1, %0" : : "x" (f), "x" (g)); ++ __asm__ __volatile__ ("divss %1, %0" : "+x" (f) : "x" (g)); + (void) &f; + } + diff --git a/SOURCES/glibc-rh2183081-2.patch b/SOURCES/glibc-rh2183081-2.patch new file mode 100644 index 0000000..215b47f --- /dev/null +++ b/SOURCES/glibc-rh2183081-2.patch @@ -0,0 +1,28 @@ +Apply a fix similar to upstream commit 5d1ccdda7b0c625751661d50977f3dfbc73f8eae +to the installed header file. Upstream, the header file has been removed +in its uncorrected state, so there is no upstream fix to backport. + +Suggested by Jakub Jelinek. + +diff --git a/sysdeps/x86/fpu/bits/fenv.h b/sysdeps/x86/fpu/bits/fenv.h +index 4103982d8c8ae014..4ae2d2a04c6754bd 100644 +--- a/sysdeps/x86/fpu/bits/fenv.h ++++ b/sysdeps/x86/fpu/bits/fenv.h +@@ -132,7 +132,7 @@ __NTH (__feraiseexcept_invalid_divbyzero (int __excepts)) + float __f = 0.0; + + # ifdef __SSE_MATH__ +- __asm__ __volatile__ ("divss %0, %0 " : : "x" (__f)); ++ __asm__ __volatile__ ("divss %0, %0 " : "+x" (__f)); + # else + __asm__ __volatile__ ("fdiv %%st, %%st(0); fwait" + : "=t" (__f) : "0" (__f)); +@@ -145,7 +145,7 @@ __NTH (__feraiseexcept_invalid_divbyzero (int __excepts)) + float __g = 0.0; + + # ifdef __SSE_MATH__ +- __asm__ __volatile__ ("divss %1, %0" : : "x" (__f), "x" (__g)); ++ __asm__ __volatile__ ("divss %1, %0" : "+x" (__f) : "x" (__g)); + # else + __asm__ __volatile__ ("fdivp %%st, %%st(1); fwait" + : "=t" (__f) : "0" (__f), "u" (__g) : "st(1)"); diff --git a/SOURCES/glibc-rh2186781.patch b/SOURCES/glibc-rh2186781.patch new file mode 100644 index 0000000..9c848c7 --- /dev/null +++ b/SOURCES/glibc-rh2186781.patch @@ -0,0 +1,46 @@ +Only backport po/it.po and po/ja.po changes for the ESTALE message +translation which we use during CI testing. + +commit 7ff33eca6860648fb909df954da4996ce853d01d +Author: Carlos O'Donell +Date: Fri Jul 7 11:27:08 2023 -0400 + + Translations: Add new ro support and update others. + + This brings in the new Romanian language translations, and updates + nine other translations. Important translations in this update + include the Italian and Japanese translations for ESTALE which + remove the mention of "NFS" from the error message translation. + +diff --git a/po/it.po b/po/it.po +index 2750575a1082f1db..6c2be3a4df5611ff 100644 +--- a/po/it.po ++++ b/po/it.po +@@ -6793,10 +6793,8 @@ msgstr "Quota disco superata" + #. TRANS Repairing this condition usually requires unmounting, possibly repairing + #. TRANS and remounting the file system. + #: sysdeps/gnu/errlist.c:788 +-#, fuzzy +-#| msgid "Stale NFS file handle" + msgid "Stale file handle" +-msgstr "Gestione del file NFS interrotta" ++msgstr "Riferimento al file obsoleto" + + # lf + #. TRANS An attempt was made to NFS-mount a remote file system with a file name that +diff --git a/po/ja.po b/po/ja.po +index bd9b7ffbbd3e3bf6..8fb598c5edbc5891 100644 +--- a/po/ja.po ++++ b/po/ja.po +@@ -6360,10 +6360,8 @@ msgstr "ディスク使用量制限を超過しました" + #. TRANS Repairing this condition usually requires unmounting, possibly repairing + #. TRANS and remounting the file system. + #: sysdeps/gnu/errlist.c:788 +-#, fuzzy +-#| msgid "Stale NFS file handle" + msgid "Stale file handle" +-msgstr "実効性のないNFSファイルハンドルです" ++msgstr "古いファイルハンドルです" + + #. TRANS An attempt was made to NFS-mount a remote file system with a file name that + #. TRANS already specifies an NFS-mounted file. diff --git a/SOURCES/glibc-rh2213909.patch b/SOURCES/glibc-rh2213909.patch new file mode 100644 index 0000000..d18d5c8 --- /dev/null +++ b/SOURCES/glibc-rh2213909.patch @@ -0,0 +1,26 @@ +From abcf8db7fa46b73fd5b8193ce11f9312301b84c7 Mon Sep 17 00:00:00 2001 +From: Andreas Schwab +Date: Wed, 7 Jun 2023 11:21:48 +0200 +Subject: resolv_conf: release lock on allocation failure (bug 30527) + +When the initial allocation of global fails, the local lock is left +locked. + +Reported by Steffen Lammel of SAP HANA development. + +diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c +index bd5890773b..8bc9edc634 100644 +--- a/resolv/resolv_conf.c ++++ b/resolv/resolv_conf.c +@@ -93,7 +93,10 @@ get_locked_global (void) + { + global_copy = calloc (1, sizeof (*global)); + if (global_copy == NULL) +- return NULL; ++ { ++ __libc_lock_unlock (lock); ++ return NULL; ++ } + atomic_store_relaxed (&global, global_copy); + resolv_conf_array_init (&global_copy->array); + } diff --git a/SOURCES/glibc-rh2224348.patch b/SOURCES/glibc-rh2224348.patch new file mode 100644 index 0000000..dc4732f --- /dev/null +++ b/SOURCES/glibc-rh2224348.patch @@ -0,0 +1,27 @@ +commit 0fda2a41baf7e978d07322aa278e964f4dce8802 +Author: Florian Weimer +Date: Thu Jul 20 18:31:48 2023 +0200 + + debug: Mark libSegFault.so as NODELETE + + The signal handler installed in the ELF constructor cannot easily + be removed again (because the program may have changed handlers + in the meantime). Mark the object as NODELETE so that the registered + handler function is never unloaded. + + Reviewed-by: Carlos O'Donell + (cherry picked from commit 23ee92deea4c99d0e6a5f48fa7b942909b123ec5) + +diff --git a/debug/Makefile b/debug/Makefile +index b0f0b7beb6d5cef5..8bce89ddcd0a61ed 100644 +--- a/debug/Makefile ++++ b/debug/Makefile +@@ -213,6 +213,8 @@ extra-libs-others = $(extra-libs) + + libSegFault-routines = segfault + libSegFault-inhibit-o = $(filter-out .os,$(object-suffixes)) ++# libSegFault.so installs a signal handler in its ELF constructor. ++LDFLAGS-SegFault.so = -Wl,--enable-new-dtags,-z,nodelete + + libpcprofile-routines = pcprofile + libpcprofile-inhibit-o = $(filter-out .os,$(object-suffixes)) diff --git a/SOURCES/glibc-rh2233338-1.patch b/SOURCES/glibc-rh2233338-1.patch new file mode 100644 index 0000000..b865e85 --- /dev/null +++ b/SOURCES/glibc-rh2233338-1.patch @@ -0,0 +1,37 @@ +commit 7b5bfe77836442b9aeb75cc520f0d1eb7f82be67 +Author: Florian Weimer +Date: Mon May 18 15:21:04 2020 +0200 + + elf: Assert that objects are relocated before their constructors run + + If we try to run constructors before relocation, this is always + a dynamic linker bug. An assert is easier to notice than a call + via an invalid function pointer (which may not even produce a valid + call stack). + + Reviewed-by: Carlos O'Donell + +diff --git a/elf/dl-init.c b/elf/dl-init.c +index 45405cd0563845b4..99ce531d7b326f5f 100644 +--- a/elf/dl-init.c ++++ b/elf/dl-init.c +@@ -16,6 +16,7 @@ + License along with the GNU C Library; if not, see + . */ + ++#include + #include + #include + +@@ -27,6 +28,11 @@ typedef void (*init_t) (int, char **, char **); + static void + call_init (struct link_map *l, int argc, char **argv, char **env) + { ++ /* If the object has not been relocated, this is a bug. The ++ function pointers are invalid in this case. (Executables do not ++ need relocation, and neither do proxy objects.) */ ++ assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable); ++ + if (l->l_init_called) + /* This object is all done. */ + return; diff --git a/SOURCES/glibc-rh2233338-2.patch b/SOURCES/glibc-rh2233338-2.patch new file mode 100644 index 0000000..84e10da --- /dev/null +++ b/SOURCES/glibc-rh2233338-2.patch @@ -0,0 +1,237 @@ +commit 6f360366f7f76b158a0f4bf20d42f2854ad56264 +Author: Florian Weimer +Date: Thu Oct 27 11:36:44 2022 +0200 + + elf: Introduce to _dl_call_fini + + This consolidates the destructor invocations from _dl_fini and + dlclose. Remove the micro-optimization that avoids + calling _dl_call_fini if they are no destructors (as dlclose is quite + expensive anyway). The debug log message is now printed + unconditionally. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/dl-fini.c + (Missing ELF_INITFINI support downstream.) + sysdeps/generic/ldsodefs.h + (Missing dl_init_t declaration downstream.) + +diff --git a/elf/Makefile b/elf/Makefile +index 634c3113227d64a6..040d82e243a80c0f 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -50,6 +50,7 @@ routines = \ + # profiled libraries. + dl-routines = \ + dl-call-libc-early-init \ ++ dl-call_fini \ + dl-close \ + dl-debug \ + dl-deps \ +diff --git a/elf/dl-call_fini.c b/elf/dl-call_fini.c +new file mode 100644 +index 0000000000000000..9e7ba10fa2a4df77 +--- /dev/null ++++ b/elf/dl-call_fini.c +@@ -0,0 +1,50 @@ ++/* Invoke DT_FINI and DT_FINI_ARRAY callbacks. ++ Copyright (C) 1996-2022 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 ++ ++void ++_dl_call_fini (void *closure_map) ++{ ++ struct link_map *map = closure_map; ++ ++ /* When debugging print a message first. */ ++ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) ++ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", map->l_name, map->l_ns); ++ ++ /* Make sure nothing happens if we are called twice. */ ++ map->l_init_called = 0; ++ ++ ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY]; ++ if (fini_array != NULL) ++ { ++ ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr ++ + fini_array->d_un.d_ptr); ++ size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val ++ / sizeof (ElfW(Addr))); ++ ++ while (sz-- > 0) ++ ((fini_t) array[sz]) (); ++ } ++ ++ /* Next try the old-style destructor. */ ++ ElfW(Dyn) *fini = map->l_info[DT_FINI]; ++ if (fini != NULL) ++ DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr)); ++} +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 22225efb3226c3e1..26ea51dfbadc5b85 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -35,11 +35,6 @@ + + #include + +- +-/* Type of the constructor functions. */ +-typedef void (*fini_t) (void); +- +- + /* Special l_idx value used to indicate which objects remain loaded. */ + #define IDX_STILL_USED -1 + +@@ -109,31 +104,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, + return false; + } + +-/* Invoke dstructors for CLOSURE (a struct link_map *). Called with +- exception handling temporarily disabled, to make errors fatal. */ +-static void +-call_destructors (void *closure) +-{ +- struct link_map *map = closure; +- +- if (map->l_info[DT_FINI_ARRAY] != NULL) +- { +- ElfW(Addr) *array = +- (ElfW(Addr) *) (map->l_addr +- + map->l_info[DT_FINI_ARRAY]->d_un.d_ptr); +- unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val +- / sizeof (ElfW(Addr))); +- +- while (sz-- > 0) +- ((fini_t) array[sz]) (); +- } +- +- /* Next try the old-style destructor. */ +- if (map->l_info[DT_FINI] != NULL) +- DL_CALL_DT_FINI (map, ((void *) map->l_addr +- + map->l_info[DT_FINI]->d_un.d_ptr)); +-} +- + void + _dl_close_worker (struct link_map *map, bool force) + { +@@ -279,17 +249,7 @@ _dl_close_worker (struct link_map *map, bool force) + half-cooked objects. Temporarily disable exception + handling, so that errors are fatal. */ + if (imap->l_init_called) +- { +- /* When debugging print a message first. */ +- if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, +- 0)) +- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", +- imap->l_name, nsid); +- +- if (imap->l_info[DT_FINI_ARRAY] != NULL +- || imap->l_info[DT_FINI] != NULL) +- _dl_catch_exception (NULL, call_destructors, imap); +- } ++ _dl_catch_exception (NULL, _dl_call_fini, imap); + + #ifdef SHARED + /* Auditing checkpoint: we remove an object. */ +diff --git a/elf/dl-fini.c b/elf/dl-fini.c +index e14259a3c8806e0d..2d34658d4c3a470c 100644 +--- a/elf/dl-fini.c ++++ b/elf/dl-fini.c +@@ -20,11 +20,6 @@ + #include + #include + +- +-/* Type of the constructor functions. */ +-typedef void (*fini_t) (void); +- +- + void + _dl_fini (void) + { +@@ -115,38 +110,7 @@ _dl_fini (void) + + if (l->l_init_called) + { +- /* Make sure nothing happens if we are called twice. */ +- l->l_init_called = 0; +- +- /* Is there a destructor function? */ +- if (l->l_info[DT_FINI_ARRAY] != NULL +- || l->l_info[DT_FINI] != NULL) +- { +- /* When debugging print a message first. */ +- if (__builtin_expect (GLRO(dl_debug_mask) +- & DL_DEBUG_IMPCALLS, 0)) +- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", +- DSO_FILENAME (l->l_name), +- ns); +- +- /* First see whether an array is given. */ +- if (l->l_info[DT_FINI_ARRAY] != NULL) +- { +- ElfW(Addr) *array = +- (ElfW(Addr) *) (l->l_addr +- + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); +- unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val +- / sizeof (ElfW(Addr))); +- while (i-- > 0) +- ((fini_t) array[i]) (); +- } +- +- /* Next try the old-style destructor. */ +- if (l->l_info[DT_FINI] != NULL) +- DL_CALL_DT_FINI +- (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr); +- } +- ++ _dl_call_fini (l); + #ifdef SHARED + /* Auditing checkpoint: another object closed. */ + _dl_audit_objclose (l); +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 29bbde3e83e37d7e..0bad34d44a5685d9 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -93,6 +93,9 @@ typedef struct link_map *lookup_t; + : (__glibc_unlikely ((ref)->st_shndx == SHN_ABS) ? 0 \ + : LOOKUP_VALUE_ADDRESS (map, map_set)) + (ref)->st_value) + ++/* Type of a constructor function, in DT_FINI, DT_FINI_ARRAY. */ ++typedef void (*fini_t) (void); ++ + /* On some architectures a pointer to a function is not just a pointer + to the actual code of the function but rather an architecture + specific descriptor. */ +@@ -1047,6 +1050,11 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv, + initializer functions have completed. */ + extern void _dl_fini (void) attribute_hidden; + ++/* Invoke the DT_FINI_ARRAY and DT_FINI destructors for MAP, which ++ must be a struct link_map *. Can be used as an argument to ++ _dl_catch_exception. */ ++void _dl_call_fini (void *map) attribute_hidden; ++ + /* Sort array MAPS according to dependencies of the contained objects. + If FORCE_FIRST, MAPS[0] keeps its place even if the dependencies + say otherwise. */ diff --git a/SOURCES/glibc-rh2233338-3.patch b/SOURCES/glibc-rh2233338-3.patch new file mode 100644 index 0000000..1627d06 --- /dev/null +++ b/SOURCES/glibc-rh2233338-3.patch @@ -0,0 +1,30 @@ +commit f6c8204fd7fabf0cf4162eaf10ccf23258e4d10e +Author: Florian Weimer +Date: Tue Aug 22 13:56:25 2023 +0200 + + elf: Do not run constructors for proxy objects + + Otherwise, the ld.so constructor runs for each audit namespace + and each dlmopen namespace. + +diff --git a/elf/dl-init.c b/elf/dl-init.c +index 99ce531d7b326f5f..73c0259fbe6d19af 100644 +--- a/elf/dl-init.c ++++ b/elf/dl-init.c +@@ -28,10 +28,14 @@ typedef void (*init_t) (int, char **, char **); + static void + call_init (struct link_map *l, int argc, char **argv, char **env) + { ++ /* Do not run constructors for proxy objects. */ ++ if (l != l->l_real) ++ return; ++ + /* If the object has not been relocated, this is a bug. The + function pointers are invalid in this case. (Executables do not +- need relocation, and neither do proxy objects.) */ +- assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable); ++ need relocation.) */ ++ assert (l->l_relocated || l->l_type == lt_executable); + + if (l->l_init_called) + /* This object is all done. */ diff --git a/SOURCES/glibc-rh2233338-4.patch b/SOURCES/glibc-rh2233338-4.patch new file mode 100644 index 0000000..8db76e4 --- /dev/null +++ b/SOURCES/glibc-rh2233338-4.patch @@ -0,0 +1,637 @@ +commit 6985865bc3ad5b23147ee73466583dd7fdf65892 +Author: Florian Weimer +Date: Fri Sep 8 12:32:14 2023 +0200 + + elf: Always call destructors in reverse constructor order (bug 30785) + + The current implementation of dlclose (and process exit) re-sorts the + link maps before calling ELF destructors. Destructor order is not the + reverse of the constructor order as a result: The second sort takes + relocation dependencies into account, and other differences can result + from ambiguous inputs, such as cycles. (The force_first handling in + _dl_sort_maps is not effective for dlclose.) After the changes in + this commit, there is still a required difference due to + dlopen/dlclose ordering by the application, but the previous + discrepancies went beyond that. + + A new global (namespace-spanning) list of link maps, + _dl_init_called_list, is updated right before ELF constructors are + called from _dl_init. + + In dl_close_worker, the maps variable, an on-stack variable length + array, is eliminated. (VLAs are problematic, and dlclose should not + call malloc because it cannot readily deal with malloc failure.) + Marking still-used objects uses the namespace list directly, with + next and next_idx replacing the done_index variable. + + After marking, _dl_init_called_list is used to call the destructors + of now-unused maps in reverse destructor order. These destructors + can call dlopen. Previously, new objects do not have l_map_used set. + This had to change: There is no copy of the link map list anymore, + so processing would cover newly opened (and unmarked) mappings, + unloading them. Now, _dl_init (indirectly) sets l_map_used, too. + (dlclose is handled by the existing reentrancy guard.) + + After _dl_init_called_list traversal, two more loops follow. The + processing order changes to the original link map order in the + namespace. Previously, dependency order was used. The difference + should not matter because relocation dependencies could already + reorder link maps in the old code. + + The changes to _dl_fini remove the sorting step and replace it with + a traversal of _dl_init_called_list. The l_direct_opencount + decrement outside the loader lock is removed because it appears + incorrect: the counter manipulation could race with other dynamic + loader operations. + + tst-audit23 needs adjustments to the changes in LA_ACT_DELETE + notifications. The new approach for checking la_activity should + make it clearer that la_activty calls come in pairs around namespace + updates. + + The dependency sorting test cases need updates because the destructor + order is always the opposite order of constructor order, even with + relocation dependencies or cycles present. + + There is a future cleanup opportunity to remove the now-constant + force_first and for_fini arguments from the _dl_sort_maps function. + + Fixes commit 1df71d32fe5f5905ffd5d100e5e9ca8ad62 ("elf: Implement + force_first handling in _dl_sort_maps_dfs (bug 28937)"). + + Reviewed-by: DJ Delorie + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 26ea51dfbadc5b85..6b134f66628cfc03 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -137,30 +137,31 @@ _dl_close_worker (struct link_map *map, bool force) + + bool any_tls = false; + const unsigned int nloaded = ns->_ns_nloaded; +- struct link_map *maps[nloaded]; + +- /* Run over the list and assign indexes to the link maps and enter +- them into the MAPS array. */ ++ /* Run over the list and assign indexes to the link maps. */ + int idx = 0; + for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next) + { + l->l_map_used = 0; + l->l_map_done = 0; + l->l_idx = idx; +- maps[idx] = l; + ++idx; + } + assert (idx == nloaded); + +- /* Keep track of the lowest index link map we have covered already. */ +- int done_index = -1; +- while (++done_index < nloaded) ++ /* Keep marking link maps until no new link maps are found. */ ++ for (struct link_map *l = ns->_ns_loaded; l != NULL; ) + { +- struct link_map *l = maps[done_index]; ++ /* next is reset to earlier link maps for remarking. */ ++ struct link_map *next = l->l_next; ++ int next_idx = l->l_idx + 1; /* next->l_idx, but covers next == NULL. */ + + if (l->l_map_done) +- /* Already handled. */ +- continue; ++ { ++ /* Already handled. */ ++ l = next; ++ continue; ++ } + + /* Check whether this object is still used. */ + if (l->l_type == lt_loaded +@@ -170,7 +171,10 @@ _dl_close_worker (struct link_map *map, bool force) + acquire is sufficient and correct. */ + && atomic_load_acquire (&l->l_tls_dtor_count) == 0 + && !l->l_map_used) +- continue; ++ { ++ l = next; ++ continue; ++ } + + /* We need this object and we handle it now. */ + l->l_map_used = 1; +@@ -197,8 +201,11 @@ _dl_close_worker (struct link_map *map, bool force) + already processed it, then we need to go back + and process again from that point forward to + ensure we keep all of its dependencies also. */ +- if ((*lp)->l_idx - 1 < done_index) +- done_index = (*lp)->l_idx - 1; ++ if ((*lp)->l_idx < next_idx) ++ { ++ next = *lp; ++ next_idx = next->l_idx; ++ } + } + } + +@@ -218,44 +225,65 @@ _dl_close_worker (struct link_map *map, bool force) + if (!jmap->l_map_used) + { + jmap->l_map_used = 1; +- if (jmap->l_idx - 1 < done_index) +- done_index = jmap->l_idx - 1; ++ if (jmap->l_idx < next_idx) ++ { ++ next = jmap; ++ next_idx = next->l_idx; ++ } + } + } + } +- } + +- /* Sort the entries. We can skip looking for the binary itself which is +- at the front of the search list for the main namespace. */ +- _dl_sort_maps (maps, nloaded, (nsid == LM_ID_BASE), true); ++ l = next; ++ } + +- /* Call all termination functions at once. */ +- bool unload_any = false; +- bool scope_mem_left = false; +- unsigned int unload_global = 0; +- unsigned int first_loaded = ~0; +- for (unsigned int i = 0; i < nloaded; ++i) ++ /* Call the destructors in reverse constructor order, and remove the ++ closed link maps from the list. */ ++ for (struct link_map **init_called_head = &_dl_init_called_list; ++ *init_called_head != NULL; ) + { +- struct link_map *imap = maps[i]; ++ struct link_map *imap = *init_called_head; + +- /* All elements must be in the same namespace. */ +- assert (imap->l_ns == nsid); +- +- if (!imap->l_map_used) ++ /* _dl_init_called_list is global, to produce a global odering. ++ Ignore the other namespaces (and link maps that are still used). */ ++ if (imap->l_ns != nsid || imap->l_map_used) ++ init_called_head = &imap->l_init_called_next; ++ else + { + assert (imap->l_type == lt_loaded && !imap->l_nodelete_active); + +- /* Call its termination function. Do not do it for +- half-cooked objects. Temporarily disable exception +- handling, so that errors are fatal. */ +- if (imap->l_init_called) ++ /* _dl_init_called_list is updated at the same time as ++ l_init_called. */ ++ assert (imap->l_init_called); ++ ++ if (imap->l_info[DT_FINI_ARRAY] != NULL ++ || imap->l_info[DT_FINI] != NULL) + _dl_catch_exception (NULL, _dl_call_fini, imap); + + #ifdef SHARED + /* Auditing checkpoint: we remove an object. */ + _dl_audit_objclose (imap); + #endif ++ /* Unlink this link map. */ ++ *init_called_head = imap->l_init_called_next; ++ } ++ } ++ ++ ++ bool unload_any = false; ++ bool scope_mem_left = false; ++ unsigned int unload_global = 0; ++ ++ /* For skipping un-unloadable link maps in the second loop. */ ++ struct link_map *first_loaded = ns->_ns_loaded; + ++ /* Iterate over the namespace to find objects to unload. Some ++ unloadable objects may not be on _dl_init_called_list due to ++ dlopen failure. */ ++ for (struct link_map *imap = first_loaded; imap != NULL; imap = imap->l_next) ++ { ++ if (!imap->l_map_used) ++ { + /* This object must not be used anymore. */ + imap->l_removed = 1; + +@@ -266,8 +294,8 @@ _dl_close_worker (struct link_map *map, bool force) + ++unload_global; + + /* Remember where the first dynamically loaded object is. */ +- if (i < first_loaded) +- first_loaded = i; ++ if (first_loaded == NULL) ++ first_loaded = imap; + } + /* Else imap->l_map_used. */ + else if (imap->l_type == lt_loaded) +@@ -403,8 +431,8 @@ _dl_close_worker (struct link_map *map, bool force) + imap->l_loader = NULL; + + /* Remember where the first dynamically loaded object is. */ +- if (i < first_loaded) +- first_loaded = i; ++ if (first_loaded == NULL) ++ first_loaded = imap; + } + } + +@@ -475,10 +503,11 @@ _dl_close_worker (struct link_map *map, bool force) + + /* Check each element of the search list to see if all references to + it are gone. */ +- for (unsigned int i = first_loaded; i < nloaded; ++i) ++ for (struct link_map *imap = first_loaded; imap != NULL; ) + { +- struct link_map *imap = maps[i]; +- if (!imap->l_map_used) ++ if (imap->l_map_used) ++ imap = imap->l_next; ++ else + { + assert (imap->l_type == lt_loaded); + +@@ -686,7 +715,9 @@ _dl_close_worker (struct link_map *map, bool force) + if (imap == GL(dl_initfirst)) + GL(dl_initfirst) = NULL; + ++ struct link_map *next = imap->l_next; + free (imap); ++ imap = next; + } + } + +diff --git a/elf/dl-fini.c b/elf/dl-fini.c +index 2d34658d4c3a470c..25a7767d707721d5 100644 +--- a/elf/dl-fini.c ++++ b/elf/dl-fini.c +@@ -23,116 +23,68 @@ + void + _dl_fini (void) + { +- /* Lots of fun ahead. We have to call the destructors for all still +- loaded objects, in all namespaces. The problem is that the ELF +- specification now demands that dependencies between the modules +- are taken into account. I.e., the destructor for a module is +- called before the ones for any of its dependencies. +- +- To make things more complicated, we cannot simply use the reverse +- order of the constructors. Since the user might have loaded objects +- using `dlopen' there are possibly several other modules with its +- dependencies to be taken into account. Therefore we have to start +- determining the order of the modules once again from the beginning. */ +- +- /* We run the destructors of the main namespaces last. As for the +- other namespaces, we pick run the destructors in them in reverse +- order of the namespace ID. */ +-#ifdef SHARED +- int do_audit = 0; +- again: +-#endif +- for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns) +- { +- /* Protect against concurrent loads and unloads. */ +- __rtld_lock_lock_recursive (GL(dl_load_lock)); +- +- unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; +- /* No need to do anything for empty namespaces or those used for +- auditing DSOs. */ +- if (nloaded == 0 +-#ifdef SHARED +- || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit +-#endif +- ) +- __rtld_lock_unlock_recursive (GL(dl_load_lock)); +- else +- { ++ /* Call destructors strictly in the reverse order of constructors. ++ This causes fewer surprises than some arbitrary reordering based ++ on new (relocation) dependencies. None of the objects are ++ unmapped, so applications can deal with this if their DSOs remain ++ in a consistent state after destructors have run. */ ++ ++ /* Protect against concurrent loads and unloads. */ ++ __rtld_lock_lock_recursive (GL(dl_load_lock)); ++ ++ /* Ignore objects which are opened during shutdown. */ ++ struct link_map *local_init_called_list = _dl_init_called_list; ++ ++ for (struct link_map *l = local_init_called_list; l != NULL; ++ l = l->l_init_called_next) ++ /* Bump l_direct_opencount of all objects so that they ++ are not dlclose()ed from underneath us. */ ++ ++l->l_direct_opencount; ++ ++ /* After this point, everything linked from local_init_called_list ++ cannot be unloaded because of the reference counter update. */ ++ __rtld_lock_unlock_recursive (GL(dl_load_lock)); ++ ++ /* Perform two passes: One for non-audit modules, one for audit ++ modules. This way, audit modules receive unload notifications ++ for non-audit objects, and the destructors for audit modules ++ still run. */ + #ifdef SHARED +- _dl_audit_activity_nsid (ns, LA_ACT_DELETE); ++ int last_pass = GLRO(dl_naudit) > 0; ++ Lmid_t last_ns = -1; ++ for (int do_audit = 0; do_audit <= last_pass; ++do_audit) + #endif +- +- /* Now we can allocate an array to hold all the pointers and +- copy the pointers in. */ +- struct link_map *maps[nloaded]; +- +- unsigned int i; +- struct link_map *l; +- assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); +- for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) +- /* Do not handle ld.so in secondary namespaces. */ +- if (l == l->l_real) +- { +- assert (i < nloaded); +- +- maps[i] = l; +- l->l_idx = i; +- ++i; +- +- /* Bump l_direct_opencount of all objects so that they +- are not dlclose()ed from underneath us. */ +- ++l->l_direct_opencount; +- } +- assert (ns != LM_ID_BASE || i == nloaded); +- assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); +- unsigned int nmaps = i; +- +- /* Now we have to do the sorting. We can skip looking for the +- binary itself which is at the front of the search list for +- the main namespace. */ +- _dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE), true); +- +- /* We do not rely on the linked list of loaded object anymore +- from this point on. We have our own list here (maps). The +- various members of this list cannot vanish since the open +- count is too high and will be decremented in this loop. So +- we release the lock so that some code which might be called +- from a destructor can directly or indirectly access the +- lock. */ +- __rtld_lock_unlock_recursive (GL(dl_load_lock)); +- +- /* 'maps' now contains the objects in the right order. Now +- call the destructors. We have to process this array from +- the front. */ +- for (i = 0; i < nmaps; ++i) +- { +- struct link_map *l = maps[i]; +- +- if (l->l_init_called) +- { +- _dl_call_fini (l); ++ for (struct link_map *l = local_init_called_list; l != NULL; ++ l = l->l_init_called_next) ++ { + #ifdef SHARED +- /* Auditing checkpoint: another object closed. */ +- _dl_audit_objclose (l); ++ if (GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing != do_audit) ++ continue; ++ ++ /* Avoid back-to-back calls of _dl_audit_activity_nsid for the ++ same namespace. */ ++ if (last_ns != l->l_ns) ++ { ++ if (last_ns >= 0) ++ _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT); ++ _dl_audit_activity_nsid (l->l_ns, LA_ACT_DELETE); ++ last_ns = l->l_ns; ++ } + #endif +- } + +- /* Correct the previous increment. */ +- --l->l_direct_opencount; +- } ++ /* There is no need to re-enable exceptions because _dl_fini ++ is not called from a context where exceptions are caught. */ ++ _dl_call_fini (l); + + #ifdef SHARED +- _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT); ++ /* Auditing checkpoint: another object closed. */ ++ _dl_audit_objclose (l); + #endif +- } +- } ++ } + + #ifdef SHARED +- if (! do_audit && GLRO(dl_naudit) > 0) +- { +- do_audit = 1; +- goto again; +- } ++ if (last_ns >= 0) ++ _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT); + + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS)) + _dl_debug_printf ("\nruntime linker statistics:\n" +diff --git a/elf/dl-init.c b/elf/dl-init.c +index 73c0259fbe6d19af..eb6c83d180c3f1a1 100644 +--- a/elf/dl-init.c ++++ b/elf/dl-init.c +@@ -24,6 +24,7 @@ + /* Type of the initializer. */ + typedef void (*init_t) (int, char **, char **); + ++struct link_map *_dl_init_called_list; + + static void + call_init (struct link_map *l, int argc, char **argv, char **env) +@@ -45,6 +46,21 @@ call_init (struct link_map *l, int argc, char **argv, char **env) + dependency. */ + l->l_init_called = 1; + ++ /* Help an already-running dlclose: The just-loaded object must not ++ be removed during the current pass. (No effect if no dlclose in ++ progress.) */ ++ l->l_map_used = 1; ++ ++ /* Record execution before starting any initializers. This way, if ++ the initializers themselves call dlopen, their ELF destructors ++ will eventually be run before this object is destructed, matching ++ that their ELF constructors have run before this object was ++ constructed. _dl_fini uses this list for audit callbacks, so ++ register objects on the list even if they do not have a ++ constructor. */ ++ l->l_init_called_next = _dl_init_called_list; ++ _dl_init_called_list = l; ++ + /* Check for object which constructors we do not run here. */ + if (__builtin_expect (l->l_name[0], 'a') == '\0' + && l->l_type == lt_executable) +diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def +index 4bf9052db16fb352..61dc54f8ae06d465 100644 +--- a/elf/dso-sort-tests-1.def ++++ b/elf/dso-sort-tests-1.def +@@ -53,21 +53,14 @@ tst-dso-ordering10: {}->a->b->c;soname({})=c + output: b>a>{}b->c->d order). +-# The older dynamic_sort=1 algorithm does not achieve this, while the DFS-based +-# dynamic_sort=2 algorithm does, although it is still arguable whether going +-# beyond spec to do this is the right thing to do. +-# The below expected outputs are what the two algorithms currently produce +-# respectively, for regression testing purposes. ++# relocation(dynamic) dependencies. For both sorting algorithms, the ++# destruction order is the reverse of the construction order, and ++# relocation dependencies are not taken into account. + tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c +-output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[a1;a->a2;a2->a;b->b1;c->a1;c=>a1 +-output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[];%c(a1());}a1>a>];+b[b1>b>];-b[];%c(a1());}a1>a>];+b[b1>b>];-b[];%c(a1());} +Date: Fri Sep 8 13:02:06 2023 +0200 + + elf: Remove unused l_text_end field from struct link_map + + It is a left-over from commit 52a01100ad011293197637e42b5be1a479a2 + ("elf: Remove ad-hoc restrictions on dlopen callers [BZ #22787]"). + + When backporting commmit 6985865bc3ad5b23147ee73466583dd7fdf65892 + ("elf: Always call destructors in reverse constructor order + (bug 30785)"), we can move the l_init_called_next field to this + place, so that the internal GLIBC_PRIVATE ABI does not change. + + Reviewed-by: Carlos O'Donell + Tested-by: Carlos O'Donell + +Conflicts: + elf/dl-load.h + (Missing commit "Avoid "inline" after return type in function + definitions.") + elf/rtld.c + (Missing rtld_setup_main_map function. Re-did the l_text_end + removal from scratch.) + +diff --git a/elf/dl-load.c b/elf/dl-load.c +index 0b45e6e3db31c70d..52dc564af9e95878 100644 +--- a/elf/dl-load.c ++++ b/elf/dl-load.c +@@ -1176,7 +1176,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, + + /* Now process the load commands and map segments into memory. + This is responsible for filling in: +- l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr ++ l_map_start, l_map_end, l_addr, l_contiguous, l_phdr + */ + errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds, + maplength, has_holes, loader); +diff --git a/elf/dl-load.h b/elf/dl-load.h +index 66ea2e9237ab6321..ebf2604e044c3bde 100644 +--- a/elf/dl-load.h ++++ b/elf/dl-load.h +@@ -82,14 +82,11 @@ struct loadcmd + + /* This is a subroutine of _dl_map_segments. It should be called for each + load command, some time after L->l_addr has been set correctly. It is +- responsible for setting up the l_text_end and l_phdr fields. */ +-static void __always_inline ++ responsible for setting the l_phdr fields */ ++static __always_inline void + _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header, + const struct loadcmd *c) + { +- if (c->prot & PROT_EXEC) +- l->l_text_end = l->l_addr + c->mapend; +- + if (l->l_phdr == 0 + && c->mapoff <= header->e_phoff + && ((size_t) (c->mapend - c->mapstart + c->mapoff) +@@ -102,7 +99,7 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header, + + /* This is a subroutine of _dl_map_object_from_fd. It is responsible + for filling in several fields in *L: l_map_start, l_map_end, l_addr, +- l_contiguous, l_text_end, l_phdr. On successful return, all the ++ l_contiguous, l_phdr. On successful return, all the + segments are mapped (or copied, or whatever) from the file into their + final places in the address space, with the correct page permissions, + and any bss-like regions already zeroed. It returns a null pointer +diff --git a/elf/rtld.c b/elf/rtld.c +index cd2cc4024a3581c2..c2edd0bfdc27f207 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -472,7 +472,6 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) + GL(dl_rtld_map).l_real = &GL(dl_rtld_map); + GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; + GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; +- GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext; + /* Copy the TLS related data if necessary. */ + #ifndef DONT_USE_BOOTSTRAP_MAP + # if NO_TLS_OFFSET != 0 +@@ -1520,7 +1519,6 @@ dl_main (const ElfW(Phdr) *phdr, + } + + main_map->l_map_end = 0; +- main_map->l_text_end = 0; + /* Perhaps the executable has no PT_LOAD header entries at all. */ + main_map->l_map_start = ~0; + /* And it was opened directly. */ +@@ -1591,8 +1589,6 @@ dl_main (const ElfW(Phdr) *phdr, + allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; + if (main_map->l_map_end < allocend) + main_map->l_map_end = allocend; +- if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) +- main_map->l_text_end = allocend; + } + break; + +@@ -1641,8 +1637,6 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]); + = (char *) main_map->l_tls_initimage + main_map->l_addr; + if (! main_map->l_map_end) + main_map->l_map_end = ~0; +- if (! main_map->l_text_end) +- main_map->l_text_end = ~0; + if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) + { + /* We were invoked directly, so the program might not have a +diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h +index d2b35a080b57c183..352e992c578e416f 100644 +--- a/elf/setup-vdso.h ++++ b/elf/setup-vdso.h +@@ -52,9 +52,6 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)), + l->l_addr = ph->p_vaddr; + if (ph->p_vaddr + ph->p_memsz >= l->l_map_end) + l->l_map_end = ph->p_vaddr + ph->p_memsz; +- if ((ph->p_flags & PF_X) +- && ph->p_vaddr + ph->p_memsz >= l->l_text_end) +- l->l_text_end = ph->p_vaddr + ph->p_memsz; + } + else + /* There must be no TLS segment. */ +@@ -63,7 +60,6 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)), + l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso); + l->l_addr = l->l_map_start - l->l_addr; + l->l_map_end += l->l_addr; +- l->l_text_end += l->l_addr; + l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr); + elf_get_dynamic_info (l, dyn_temp); + _dl_setup_hash (l); +diff --git a/include/link.h b/include/link.h +index a37b2cf3133eefd5..88683e7a747b86e0 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -251,8 +251,6 @@ struct link_map + /* Start and finish of memory map for this object. l_map_start + need not be the same as l_addr. */ + ElfW(Addr) l_map_start, l_map_end; +- /* End of the executable part of the mapping. */ +- ElfW(Addr) l_text_end; + + /* Default array for 'l_scope'. */ + struct r_scope_elem *l_scope_mem[4]; diff --git a/SOURCES/glibc-rh2233338-6.patch b/SOURCES/glibc-rh2233338-6.patch new file mode 100644 index 0000000..74aca35 --- /dev/null +++ b/SOURCES/glibc-rh2233338-6.patch @@ -0,0 +1,35 @@ +commit d3ba6c1333b10680ce5900a628108507d9d4b844 +Author: Florian Weimer +Date: Mon Sep 11 09:17:52 2023 +0200 + + elf: Move l_init_called_next to old place of l_text_end in link map + + This preserves all member offsets and the GLIBC_PRIVATE ABI + for backporting. + +diff --git a/include/link.h b/include/link.h +index 88683e7a747b86e0..a464dd8e86cf89d0 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -252,6 +252,10 @@ struct link_map + need not be the same as l_addr. */ + ElfW(Addr) l_map_start, l_map_end; + ++ /* Linked list of objects in reverse ELF constructor execution ++ order. Head of list is stored in _dl_init_called_list. */ ++ struct link_map *l_init_called_next; ++ + /* Default array for 'l_scope'. */ + struct r_scope_elem *l_scope_mem[4]; + /* Size of array allocated for 'l_scope'. */ +@@ -274,10 +278,6 @@ struct link_map + /* List of object in order of the init and fini calls. */ + struct link_map **l_initfini; + +- /* Linked list of objects in reverse ELF constructor execution +- order. Head of list is stored in _dl_init_called_list. */ +- struct link_map *l_init_called_next; +- + /* List of the dependencies introduced through symbol binding. */ + struct link_map_reldeps + { diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index c1e7d19..1b03ea5 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.28 %define glibcversion 2.28 -%define glibcrelease 225%{?dist} +%define glibcrelease 236%{?dist} # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -132,7 +132,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: %{glibcrelease} +Release: %{glibcrelease}.1 # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1031,6 +1031,28 @@ Patch838: glibc-rh2142937-3.patch Patch839: glibc-rh2144568.patch Patch840: glibc-rh2154914-1.patch Patch841: glibc-rh2154914-2.patch +Patch842: glibc-rh2183081-1.patch +Patch843: glibc-rh2183081-2.patch +Patch844: glibc-rh2172949.patch +Patch845: glibc-rh2180155-1.patch +Patch846: glibc-rh2180155-2.patch +Patch847: glibc-rh2180155-3.patch +Patch848: glibc-rh2213909.patch +Patch849: glibc-rh2176707-1.patch +Patch850: glibc-rh2176707-2.patch +Patch851: glibc-rh2186781.patch +Patch852: glibc-rh2224348.patch +Patch853: glibc-rh2176707-3.patch +Patch854: glibc-rh2180462-1.patch +Patch855: glibc-rh2180462-2.patch +Patch856: glibc-rh2180462-3.patch +Patch857: glibc-rh2180462-4.patch +Patch858: glibc-rh2233338-1.patch +Patch859: glibc-rh2233338-2.patch +Patch860: glibc-rh2233338-3.patch +Patch861: glibc-rh2233338-4.patch +Patch862: glibc-rh2233338-5.patch +Patch863: glibc-rh2233338-6.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2778,7 +2800,7 @@ fi %ifarch s390x /lib/ld64.so.1 %endif -%verify(not md5 size mtime) %config(noreplace) /etc/nsswitch.conf +%verify(not md5 size mtime link) %config(noreplace) /etc/nsswitch.conf %verify(not md5 size mtime) %config(noreplace) /etc/ld.so.conf %verify(not md5 size mtime) %config(noreplace) /etc/rpc %dir /etc/ld.so.conf.d @@ -2861,6 +2883,42 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Mon Sep 11 2023 Florian Weimer - 2.28-236.1 +- Always call destructors in reverse constructor order (#2233338) + +* Tue Aug 15 2023 Carlos O'Donell - 2.28-236 +- Fix string and memory function tuning on small systems (#2180462) + +* Tue Aug 8 2023 DJ Delorie - 2.28-235 +- Fix temporal threshold calculations (#2180462) + +* Mon Aug 7 2023 Florian Weimer - 2.28-234 +- Ignore symbolic link change on /etc/nsswitch.conf (#2229709) + +* Wed Jul 26 2023 DJ Delorie - 2.28-233 +- Update test to closer match upstream. (#2176707) + +* Fri Jul 21 2023 Florian Weimer - 2.28-232 +- Make libSegFault.so NODELETE (#2224348) + +* Sun Jul 9 2023 Carlos O'Donell - 2.28-231 +- Update ESTALE error message translations (#2186781) + +* Fri Jul 7 2023 DJ Delorie - 2.28-230 +- Don't block SIGCHILD when system() is called concurrently (#2176707) + +* Mon Jul 3 2023 DJ Delorie - 2.28-229 +- resolv_conf: release lock on allocation failure (#2213909) + +* Mon May 22 2023 Florian Weimer - 2.28-228 +- gmon: Various bug fixes (#2180155) + +* Thu May 18 2023 Patsy Griffin - 2.28-227 +- Change sgetsgent_r to set errno. (#2172949) + +* Wed May 3 2023 Florian Weimer - 2.28-226 +- Fix incorrect inline feraiseexcept on i686, x86-64 (#2183081) + * Fri Jan 20 2023 Florian Weimer - 2.28-225 - Enforce a specififc internal ordering for tunables (#2154914)