forked from rpms/glibc
		
	import CS glibc-2.28-236.el8.1
This commit is contained in:
		
							parent
							
								
									7db8dcfedc
								
							
						
					
					
						commit
						3f52bf0a36
					
				
							
								
								
									
										121
									
								
								SOURCES/glibc-rh2172949.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								SOURCES/glibc-rh2172949.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| commit 969e9733c7d17edf1e239a73fa172f357561f440 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <siddhesh@sourceware.org> | ||||
| 
 | ||||
| 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
 | ||||
| +   <https://www.gnu.org/licenses/>.  */
 | ||||
| +
 | ||||
| +#include <gshadow.h>
 | ||||
| +#include <stddef.h>
 | ||||
| +#include <support/check.h>
 | ||||
| +#include <support/support.h>
 | ||||
| +#include <support/xmemstream.h>
 | ||||
| +#include <stdlib.h>
 | ||||
| +
 | ||||
| +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 <support/test-driver.c>
 | ||||
							
								
								
									
										144
									
								
								SOURCES/glibc-rh2176707-1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								SOURCES/glibc-rh2176707-1.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | ||||
| From 436a604b7dc741fc76b5a6704c6cd8bb178518e7 Mon Sep 17 00:00:00 2001 | ||||
| From: Adam Yi <ayi@janestreet.com> | ||||
| 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 <ayi@janestreet.com> | ||||
| Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org> | ||||
| 
 | ||||
| [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 <support/check.h> | ||||
|  #include <support/temp_file.h> | ||||
|  #include <support/support.h> | ||||
| +#include <support/xthread.h>
 | ||||
|  #include <support/xunistd.h> | ||||
|   | ||||
|  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); | ||||
							
								
								
									
										27
									
								
								SOURCES/glibc-rh2176707-2.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								SOURCES/glibc-rh2176707-2.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| From d03094649d39949a30513bf3ffb03a28fecbccd8 Mon Sep 17 00:00:00 2001 | ||||
| From: Adam Yi <ayi@janestreet.com> | ||||
| 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 <ayi@janestreet.com> | ||||
| Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org> | ||||
| 
 | ||||
| [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) | ||||
							
								
								
									
										42
									
								
								SOURCES/glibc-rh2176707-3.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								SOURCES/glibc-rh2176707-3.patch
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
							
								
								
									
										70
									
								
								SOURCES/glibc-rh2180155-1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								SOURCES/glibc-rh2180155-1.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| commit 801af9fafd4689337ebf27260aa115335a0cb2bc | ||||
| Author: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru> | ||||
| 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) <leo@yuriev.ru> | ||||
|      | ||||
|     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 <dj@redhat.com> | ||||
|     Reviewed-by: Carlos O'Donell <carlos@redhat.com> | ||||
| 
 | ||||
| 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; | ||||
							
								
								
									
										477
									
								
								SOURCES/glibc-rh2180155-2.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										477
									
								
								SOURCES/glibc-rh2180155-2.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,477 @@ | ||||
| This patch adds the required @order directives to preserve the | ||||
| GLIBC_PRIVATE ABI. | ||||
| 
 | ||||
| commit 31be941e4367c001b2009308839db5c67bf9dcbc | ||||
| Author: Simon Kissane <skissane@gmail.com> | ||||
| 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 <skissane@gmail.com> | ||||
|     Reviewed-by: DJ Delorie <dj@redhat.com> | ||||
| 
 | ||||
| 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<minarcs to check warning is printed
 | ||||
| +tst-mcount-overflow-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcount-overflow.data \
 | ||||
| +                           GLIBC_TUNABLES=glibc.gmon.minarcs=51:glibc.gmon.maxarcs=50
 | ||||
| +# Send stderr into output file because we make sure expected messages are printed
 | ||||
| +tst-mcount-overflow-ARGS := 2>&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 <libc-internal.h> | ||||
|  #include <not-cancel.h> | ||||
|   | ||||
| +#if HAVE_TUNABLES
 | ||||
| +# define TUNABLE_NAMESPACE gmon
 | ||||
| +# include <elf/dl-tunables.h>
 | ||||
| +#endif
 | ||||
| +
 | ||||
|  #ifdef PIC | ||||
|  # include <link.h> | ||||
|   | ||||
| @@ -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 <atomic.h> | ||||
|   | ||||
| +#include <not-cancel.h>
 | ||||
| +#include <unistd.h>
 | ||||
| +#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
 | ||||
| +# <https://www.gnu.org/licenses/>.
 | ||||
| +
 | ||||
| +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
 | ||||
| +   <https://www.gnu.org/licenses/>.  */
 | ||||
| +
 | ||||
| +/* 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
 | ||||
							
								
								
									
										191
									
								
								SOURCES/glibc-rh2180155-3.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								SOURCES/glibc-rh2180155-3.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,191 @@ | ||||
| commit bde121872001d8f3224eeafa5b7effb871c3fbca | ||||
| Author: Simon Kissane <skissane@gmail.com> | ||||
| 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 <skissane@gmail.com> | ||||
|     Reviewed-by: DJ Delorie <dj@redhat.com> | ||||
| 
 | ||||
| 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
 | ||||
| +   <https://www.gnu.org/licenses/>.  */
 | ||||
| +
 | ||||
| +/* 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 <sys/gmon.h>
 | ||||
| +
 | ||||
| +int
 | ||||
| +main (void)
 | ||||
| +{
 | ||||
| +  _mcleanup();
 | ||||
| +  return 0;
 | ||||
| +}
 | ||||
							
								
								
									
										216
									
								
								SOURCES/glibc-rh2180462-1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								SOURCES/glibc-rh2180462-1.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,216 @@ | ||||
| From af992e7abdc9049714da76cae1e5e18bc4838fb8 Mon Sep 17 00:00:00 2001 | ||||
| From: Noah Goldstein <goldstein.w.n@gmail.com> | ||||
| 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 <dj@redhat.com> | ||||
| Reviewed-by: Carlos O'Donell <carlos@redhat.com> | ||||
| ---
 | ||||
|  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~ | ||||
							
								
								
									
										47
									
								
								SOURCES/glibc-rh2180462-2.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								SOURCES/glibc-rh2180462-2.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| From 47f747217811db35854ea06741be3685e8bbd44d Mon Sep 17 00:00:00 2001 | ||||
| From: Noah Goldstein <goldstein.w.n@gmail.com> | ||||
| 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 <goldstein.w.n@gmail.com> | ||||
|     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 <dj@redhat.com> | ||||
| ---
 | ||||
|  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; | ||||
|      } | ||||
|   | ||||
							
								
								
									
										44
									
								
								SOURCES/glibc-rh2180462-3.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								SOURCES/glibc-rh2180462-3.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| From 8b9a0af8ca012217bf90d1dc0694f85b49ae09da Mon Sep 17 00:00:00 2001 | ||||
| From: Noah Goldstein <goldstein.w.n@gmail.com> | ||||
| 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 <dj@redhat.com> | ||||
| ---
 | ||||
|  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 | ||||
							
								
								
									
										39
									
								
								SOURCES/glibc-rh2180462-4.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								SOURCES/glibc-rh2180462-4.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| Adjusted for backport to c8s by modifying sysdeps/x86/cacheinfo.h. | ||||
| 
 | ||||
| commit 885a7f0feee951f514a121788f46f33b2867110f | ||||
| Author: Noah Goldstein <goldstein.w.n@gmail.com> | ||||
| 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.  */ | ||||
							
								
								
									
										35
									
								
								SOURCES/glibc-rh2183081-1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								SOURCES/glibc-rh2183081-1.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| commit 5d1ccdda7b0c625751661d50977f3dfbc73f8eae | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <jakub@redhat.com> | ||||
|     Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com> | ||||
| 
 | ||||
| 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; | ||||
|      } | ||||
|   | ||||
							
								
								
									
										28
									
								
								SOURCES/glibc-rh2183081-2.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								SOURCES/glibc-rh2183081-2.patch
									
									
									
									
									
										Normal file
									
								
							| @ -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)"); | ||||
							
								
								
									
										46
									
								
								SOURCES/glibc-rh2186781.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								SOURCES/glibc-rh2186781.patch
									
									
									
									
									
										Normal file
									
								
							| @ -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 <carlos@redhat.com> | ||||
| 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. | ||||
							
								
								
									
										26
									
								
								SOURCES/glibc-rh2213909.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								SOURCES/glibc-rh2213909.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| From abcf8db7fa46b73fd5b8193ce11f9312301b84c7 Mon Sep 17 00:00:00 2001 | ||||
| From: Andreas Schwab <schwab@suse.de> | ||||
| 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); | ||||
|      } | ||||
							
								
								
									
										27
									
								
								SOURCES/glibc-rh2224348.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								SOURCES/glibc-rh2224348.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| commit 0fda2a41baf7e978d07322aa278e964f4dce8802 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <carlos@redhat.com> | ||||
|     (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)) | ||||
							
								
								
									
										37
									
								
								SOURCES/glibc-rh2233338-1.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								SOURCES/glibc-rh2233338-1.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| commit 7b5bfe77836442b9aeb75cc520f0d1eb7f82be67 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <carlos@redhat.com> | ||||
| 
 | ||||
| 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 | ||||
|     <http://www.gnu.org/licenses/>.  */ | ||||
|   | ||||
| +#include <assert.h>
 | ||||
|  #include <stddef.h> | ||||
|  #include <ldsodefs.h> | ||||
|   | ||||
| @@ -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; | ||||
							
								
								
									
										237
									
								
								SOURCES/glibc-rh2233338-2.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								SOURCES/glibc-rh2233338-2.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,237 @@ | ||||
| commit 6f360366f7f76b158a0f4bf20d42f2854ad56264 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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  <adhemerval.zanella@linaro.org> | ||||
| 
 | ||||
| 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
 | ||||
| +   <https://www.gnu.org/licenses/>.  */
 | ||||
| +
 | ||||
| +#include <ldsodefs.h>
 | ||||
| +#include <sysdep.h>
 | ||||
| +
 | ||||
| +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 <dl-unmap-segments.h> | ||||
|   | ||||
| -
 | ||||
| -/* 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 <string.h> | ||||
|  #include <ldsodefs.h> | ||||
|   | ||||
| -
 | ||||
| -/* 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.  */ | ||||
							
								
								
									
										30
									
								
								SOURCES/glibc-rh2233338-3.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								SOURCES/glibc-rh2233338-3.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| commit f6c8204fd7fabf0cf4162eaf10ccf23258e4d10e | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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.  */ | ||||
							
								
								
									
										637
									
								
								SOURCES/glibc-rh2233338-4.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										637
									
								
								SOURCES/glibc-rh2233338-4.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,637 @@ | ||||
| commit 6985865bc3ad5b23147ee73466583dd7fdf65892 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <dj@redhat.com> | ||||
| 
 | ||||
| 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>{}<a<b | ||||
|   | ||||
|  # Complex example from Bugzilla #15311, under-linked and with circular | ||||
| -# relocation(dynamic) dependencies. While this is technically unspecified, the
 | ||||
| -# presumed reasonable practical behavior is for the destructor order to respect
 | ||||
| -# the static DT_NEEDED links (here this means the 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[<a<c<d<g<f<b<e];}
 | ||||
| -output(glibc.rtld.dynamic_sort=2): {+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[<g<f<a<b<c<d<e];}
 | ||||
| +output: {+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[<g<f<e<a<b<c<d];}
 | ||||
|   | ||||
|  # Test that even in the presence of dependency loops involving dlopen'ed | ||||
|  # object, that object is initialized last (and not unloaded prematurely). | ||||
| -# Final destructor order is indeterminate due to the cycle.
 | ||||
| +# Final destructor order is the opposite of constructor order.
 | ||||
|  tst-bz28937: {+a;+b;-b;+c;%c};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[<b<b1];+c[c>];%c(a1());}<a<a2<c<a1
 | ||||
| -output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a2<a<c<a1
 | ||||
| +output: {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<c<a<a1<a2
 | ||||
| diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
 | ||||
| index 4904cf1340a97ee1..f40760bd702e87c1 100644
 | ||||
| --- a/elf/tst-audit23.c
 | ||||
| +++ b/elf/tst-audit23.c
 | ||||
| @@ -98,6 +98,8 @@ do_test (int argc, char *argv[])
 | ||||
|      char *lname; | ||||
|      uintptr_t laddr; | ||||
|      Lmid_t lmid; | ||||
| +    uintptr_t cookie;
 | ||||
| +    uintptr_t namespace;
 | ||||
|      bool closed; | ||||
|    } objs[max_objs] = { [0 ... max_objs-1] = { .closed = false } }; | ||||
|    size_t nobjs = 0; | ||||
| @@ -117,6 +119,9 @@ do_test (int argc, char *argv[])
 | ||||
|    size_t buffer_length = 0; | ||||
|    while (xgetline (&buffer, &buffer_length, out)) | ||||
|      { | ||||
| +      *strchrnul (buffer, '\n') = '\0';
 | ||||
| +      printf ("info: subprocess output: %s\n", buffer);
 | ||||
| +
 | ||||
|        if (startswith (buffer, "la_activity: ")) | ||||
|  	{ | ||||
|  	  uintptr_t cookie; | ||||
| @@ -125,29 +130,26 @@ do_test (int argc, char *argv[])
 | ||||
|  			  &cookie); | ||||
|  	  TEST_COMPARE (r, 2); | ||||
|   | ||||
| -	  /* The cookie identifies the object at the head of the link map,
 | ||||
| -	     so we only add a new namespace if it changes from the previous
 | ||||
| -	     one.  This works since dlmopen is the last in the test body.  */
 | ||||
| -	  if (cookie != last_act_cookie && last_act_cookie != -1)
 | ||||
| -	    TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
 | ||||
| -
 | ||||
|  	  if (this_act == LA_ACT_ADD && acts[nacts] != cookie) | ||||
|  	    { | ||||
| +	      /* The cookie identifies the object at the head of the
 | ||||
| +		 link map, so we only add a new namespace if it
 | ||||
| +		 changes from the previous one.  This works since
 | ||||
| +		 dlmopen is the last in the test body.  */
 | ||||
| +	      if (cookie != last_act_cookie && last_act_cookie != -1)
 | ||||
| +		TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
 | ||||
| +
 | ||||
|  	      acts[nacts++] = cookie; | ||||
|  	      last_act_cookie = cookie; | ||||
|  	    } | ||||
| -	  /* The LA_ACT_DELETE is called in the reverse order of LA_ACT_ADD
 | ||||
| -	     at program termination (if the tests adds a dlclose or a library
 | ||||
| -	     with extra dependencies this will need to be adapted).  */
 | ||||
| +	  /* LA_ACT_DELETE is called multiple times for each
 | ||||
| +	     namespace, depending on destruction order.  */
 | ||||
|  	  else if (this_act == LA_ACT_DELETE) | ||||
| -	    {
 | ||||
| -	      last_act_cookie = acts[--nacts];
 | ||||
| -	      TEST_COMPARE (acts[nacts], cookie);
 | ||||
| -	      acts[nacts] = 0;
 | ||||
| -	    }
 | ||||
| +	    last_act_cookie = cookie;
 | ||||
|  	  else if (this_act == LA_ACT_CONSISTENT) | ||||
|  	    { | ||||
|  	      TEST_COMPARE (cookie, last_act_cookie); | ||||
| +	      last_act_cookie = -1;
 | ||||
|   | ||||
|  	      /* LA_ACT_DELETE must always be followed by an la_objclose.  */ | ||||
|  	      if (last_act == LA_ACT_DELETE) | ||||
| @@ -179,6 +181,8 @@ do_test (int argc, char *argv[])
 | ||||
|  	  objs[nobjs].lname = lname; | ||||
|  	  objs[nobjs].laddr = laddr; | ||||
|  	  objs[nobjs].lmid = lmid; | ||||
| +	  objs[nobjs].cookie = cookie;
 | ||||
| +	  objs[nobjs].namespace = last_act_cookie;
 | ||||
|  	  objs[nobjs].closed = false; | ||||
|  	  nobjs++; | ||||
|   | ||||
| @@ -201,6 +205,12 @@ do_test (int argc, char *argv[])
 | ||||
|  	      if (strcmp (lname, objs[i].lname) == 0 && lmid == objs[i].lmid) | ||||
|  		{ | ||||
|  		  TEST_COMPARE (objs[i].closed, false); | ||||
| +		  TEST_COMPARE (objs[i].cookie, cookie);
 | ||||
| +		  if (objs[i].namespace == -1)
 | ||||
| +		    /* No LA_ACT_ADD before the first la_objopen call.  */
 | ||||
| +		    TEST_COMPARE (acts[0], last_act_cookie);
 | ||||
| +		  else
 | ||||
| +		    TEST_COMPARE (objs[i].namespace, last_act_cookie);
 | ||||
|  		  objs[i].closed = true; | ||||
|  		  break; | ||||
|  		} | ||||
| @@ -209,11 +219,7 @@ do_test (int argc, char *argv[])
 | ||||
|  	  /* la_objclose should be called after la_activity(LA_ACT_DELETE) for | ||||
|  	     the closed object's namespace.  */ | ||||
|  	  TEST_COMPARE (last_act, LA_ACT_DELETE); | ||||
| -	  if (!seen_first_objclose)
 | ||||
| -	    {
 | ||||
| -	      TEST_COMPARE (last_act_cookie, cookie);
 | ||||
| -	      seen_first_objclose = true;
 | ||||
| -	    }
 | ||||
| +	  seen_first_objclose = true;
 | ||||
|  	} | ||||
|      } | ||||
|   | ||||
| diff --git a/include/link.h b/include/link.h
 | ||||
| index 041ff5f753a9ee11..a37b2cf3133eefd5 100644
 | ||||
| --- a/include/link.h
 | ||||
| +++ b/include/link.h
 | ||||
| @@ -276,6 +276,10 @@ 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/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
 | ||||
| index 0bad34d44a5685d9..906447e677a4ca40 100644
 | ||||
| --- a/sysdeps/generic/ldsodefs.h
 | ||||
| +++ b/sysdeps/generic/ldsodefs.h
 | ||||
| @@ -1046,6 +1046,10 @@ extern int _dl_check_map_versions (struct link_map *map, int verbose,
 | ||||
|  extern void _dl_init (struct link_map *main_map, int argc, char **argv, | ||||
|  		      char **env) attribute_hidden; | ||||
|   | ||||
| +/* List of ELF objects in reverse order of their constructor
 | ||||
| +   invocation.  */
 | ||||
| +extern struct link_map *_dl_init_called_list attribute_hidden;
 | ||||
| +
 | ||||
|  /* Call the finalizer functions of all shared objects whose | ||||
|     initializer functions have completed.  */ | ||||
|  extern void _dl_fini (void) attribute_hidden; | ||||
							
								
								
									
										141
									
								
								SOURCES/glibc-rh2233338-5.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								SOURCES/glibc-rh2233338-5.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | ||||
| commit 53df2ce6885da3d0e89e87dca7b095622296014f | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 <carlos@redhat.com> | ||||
|     Tested-by: Carlos O'Donell <carlos@redhat.com> | ||||
| 
 | ||||
| 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]; | ||||
							
								
								
									
										35
									
								
								SOURCES/glibc-rh2233338-6.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								SOURCES/glibc-rh2233338-6.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| commit d3ba6c1333b10680ce5900a628108507d9d4b844 | ||||
| Author: Florian Weimer <fweimer@redhat.com> | ||||
| 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 | ||||
|        { | ||||
| @ -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 <fweimer@redhat.com> - 2.28-236.1 | ||||
| - Always call destructors in reverse constructor order (#2233338) | ||||
| 
 | ||||
| * Tue Aug 15 2023 Carlos O'Donell <carlos@redhat.com> - 2.28-236 | ||||
| - Fix string and memory function tuning on small systems (#2180462) | ||||
| 
 | ||||
| * Tue Aug  8 2023 DJ Delorie <dj@redhat.com> - 2.28-235 | ||||
| - Fix temporal threshold calculations (#2180462) | ||||
| 
 | ||||
| * Mon Aug  7 2023 Florian Weimer <fweimer@redhat.com> - 2.28-234 | ||||
| - Ignore symbolic link change on /etc/nsswitch.conf (#2229709) | ||||
| 
 | ||||
| * Wed Jul 26 2023 DJ Delorie <dj@redhat.com> - 2.28-233 | ||||
| - Update test to closer match upstream. (#2176707) | ||||
| 
 | ||||
| * Fri Jul 21 2023 Florian Weimer <fweimer@redhat.com> - 2.28-232 | ||||
| - Make libSegFault.so NODELETE (#2224348) | ||||
| 
 | ||||
| * Sun Jul  9 2023 Carlos O'Donell <carlos@redhat.com> - 2.28-231 | ||||
| - Update ESTALE error message translations (#2186781) | ||||
| 
 | ||||
| * Fri Jul 7 2023 DJ Delorie <dj@redhat.com> - 2.28-230 | ||||
| - Don't block SIGCHILD when system() is called concurrently (#2176707) | ||||
| 
 | ||||
| * Mon Jul 3 2023 DJ Delorie <dj@redhat.com> - 2.28-229 | ||||
| - resolv_conf: release lock on allocation failure (#2213909) | ||||
| 
 | ||||
| * Mon May 22 2023 Florian Weimer <fweimer@redhat.com> - 2.28-228 | ||||
| - gmon: Various bug fixes (#2180155) | ||||
| 
 | ||||
| * Thu May 18 2023 Patsy Griffin <patsy@redhat.com> - 2.28-227 | ||||
| - Change sgetsgent_r to set errno. (#2172949) | ||||
| 
 | ||||
| * Wed May  3 2023 Florian Weimer <fweimer@redhat.com> - 2.28-226 | ||||
| - Fix incorrect inline feraiseexcept on i686, x86-64 (#2183081) | ||||
| 
 | ||||
| * Fri Jan 20 2023 Florian Weimer <fweimer@redhat.com> - 2.28-225 | ||||
| - Enforce a specififc internal ordering for tunables (#2154914) | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user