]> git.ipfire.org Git - people/mlorenz/ipfire-2.x.git/commitdiff
glibc: Import patches from upstream master
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Oct 2023 12:54:17 +0000 (12:54 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Oct 2023 12:54:17 +0000 (12:54 +0000)
This patch imports the latest patches from the 2.38 branch:

  https://git.ipfire.org/?p=thirdparty/glibc.git;a=shortlog;h=refs/heads/release/2.38/master

This includes a fix for a buffer overflow in the tunables code
(CVE-2023-4911) as well as  CVE-2023-4806 and CVE-2023-5156.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
28 files changed:
lfs/glibc
src/patches/glibc-2.38/0001-stdlib-Improve-tst-realpath-compatibility-with-sourc.patch [new file with mode: 0644]
src/patches/glibc-2.38/0002-x86-Fix-for-cache-computation-on-AMD-legacy-cpus.patch [new file with mode: 0644]
src/patches/glibc-2.38/0003-nscd-Do-not-rebuild-getaddrinfo-bug-30709.patch [new file with mode: 0644]
src/patches/glibc-2.38/0004-x86-Fix-incorrect-scope-of-setting-shared_per_thread.patch [new file with mode: 0644]
src/patches/glibc-2.38/0005-x86_64-Fix-build-with-disable-multiarch-BZ-30721.patch [new file with mode: 0644]
src/patches/glibc-2.38/0006-i686-Fix-build-with-disable-multiarch.patch [new file with mode: 0644]
src/patches/glibc-2.38/0007-malloc-Enable-merging-of-remainders-in-memalign-bug-.patch [new file with mode: 0644]
src/patches/glibc-2.38/0008-malloc-Remove-bin-scanning-from-memalign-bug-30723.patch [new file with mode: 0644]
src/patches/glibc-2.38/0009-sysdeps-tst-bz21269-fix-test-parameter.patch [new file with mode: 0644]
src/patches/glibc-2.38/0010-sysdeps-tst-bz21269-handle-ENOSYS-skip-appropriately.patch [new file with mode: 0644]
src/patches/glibc-2.38/0011-sysdeps-tst-bz21269-fix-Wreturn-type.patch [new file with mode: 0644]
src/patches/glibc-2.38/0012-io-Fix-record-locking-contants-for-powerpc64-with-__.patch [new file with mode: 0644]
src/patches/glibc-2.38/0013-libio-Fix-oversized-__io_vtables.patch [new file with mode: 0644]
src/patches/glibc-2.38/0014-elf-Do-not-run-constructors-for-proxy-objects.patch [new file with mode: 0644]
src/patches/glibc-2.38/0015-elf-Always-call-destructors-in-reverse-constructor-o.patch [new file with mode: 0644]
src/patches/glibc-2.38/0016-elf-Remove-unused-l_text_end-field-from-struct-link_.patch [new file with mode: 0644]
src/patches/glibc-2.38/0017-elf-Move-l_init_called_next-to-old-place-of-l_text_e.patch [new file with mode: 0644]
src/patches/glibc-2.38/0018-NEWS-Add-the-2.38.1-bug-list.patch [new file with mode: 0644]
src/patches/glibc-2.38/0019-CVE-2023-4527-Stack-read-overflow-with-large-TCP-res.patch [new file with mode: 0644]
src/patches/glibc-2.38/0020-getaddrinfo-Fix-use-after-free-in-getcanonname-CVE-2.patch [new file with mode: 0644]
src/patches/glibc-2.38/0021-iconv-restore-verbosity-with-unrecognized-encoding-n.patch [new file with mode: 0644]
src/patches/glibc-2.38/0022-string-Fix-tester-build-with-fortify-enable-with-gcc.patch [new file with mode: 0644]
src/patches/glibc-2.38/0023-manual-jobs.texi-Add-missing-item-EPERM-for-getpgid.patch [new file with mode: 0644]
src/patches/glibc-2.38/0024-Fix-leak-in-getaddrinfo-introduced-by-the-fix-for-CV.patch [new file with mode: 0644]
src/patches/glibc-2.38/0025-Document-CVE-2023-4806-and-CVE-2023-5156-in-NEWS.patch [new file with mode: 0644]
src/patches/glibc-2.38/0026-Propagate-GLIBC_TUNABLES-in-setxid-binaries.patch [new file with mode: 0644]
src/patches/glibc-2.38/0027-tunables-Terminate-if-end-of-input-is-reached-CVE-20.patch [new file with mode: 0644]

index 13f6cf16d0fb4c3330a633adde13f6d082acc4f8..cf124bcfc6b716ca9c73984505ee6ca022a9906b 100644 (file)
--- a/lfs/glibc
+++ b/lfs/glibc
@@ -114,6 +114,35 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        @rm -rf $(DIR_APP) $(DIR_SRC)/glibc-build && cd $(DIR_SRC) && tar axf $(DIR_DL)/$(DL_FILE)
        @mkdir $(DIR_SRC)/glibc-build
 
+       # Patches from upstream
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0001-stdlib-Improve-tst-realpath-compatibility-with-sourc.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0002-x86-Fix-for-cache-computation-on-AMD-legacy-cpus.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0003-nscd-Do-not-rebuild-getaddrinfo-bug-30709.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0004-x86-Fix-incorrect-scope-of-setting-shared_per_thread.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0005-x86_64-Fix-build-with-disable-multiarch-BZ-30721.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0006-i686-Fix-build-with-disable-multiarch.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0007-malloc-Enable-merging-of-remainders-in-memalign-bug-.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0008-malloc-Remove-bin-scanning-from-memalign-bug-30723.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0009-sysdeps-tst-bz21269-fix-test-parameter.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0010-sysdeps-tst-bz21269-handle-ENOSYS-skip-appropriately.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0011-sysdeps-tst-bz21269-fix-Wreturn-type.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0012-io-Fix-record-locking-contants-for-powerpc64-with-__.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0013-libio-Fix-oversized-__io_vtables.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0014-elf-Do-not-run-constructors-for-proxy-objects.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0015-elf-Always-call-destructors-in-reverse-constructor-o.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0016-elf-Remove-unused-l_text_end-field-from-struct-link_.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0017-elf-Move-l_init_called_next-to-old-place-of-l_text_e.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0018-NEWS-Add-the-2.38.1-bug-list.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0019-CVE-2023-4527-Stack-read-overflow-with-large-TCP-res.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0020-getaddrinfo-Fix-use-after-free-in-getcanonname-CVE-2.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0021-iconv-restore-verbosity-with-unrecognized-encoding-n.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0022-string-Fix-tester-build-with-fortify-enable-with-gcc.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0023-manual-jobs.texi-Add-missing-item-EPERM-for-getpgid.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0024-Fix-leak-in-getaddrinfo-introduced-by-the-fix-for-CV.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0025-Document-CVE-2023-4806-and-CVE-2023-5156-in-NEWS.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0026-Propagate-GLIBC_TUNABLES-in-setxid-binaries.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.38/0027-tunables-Terminate-if-end-of-input-is-reached-CVE-20.patch
+
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-localedef-no-archive.patch
 
 ifneq "$(TOOLCHAIN)" "1"
diff --git a/src/patches/glibc-2.38/0001-stdlib-Improve-tst-realpath-compatibility-with-sourc.patch b/src/patches/glibc-2.38/0001-stdlib-Improve-tst-realpath-compatibility-with-sourc.patch
new file mode 100644 (file)
index 0000000..1cef353
--- /dev/null
@@ -0,0 +1,43 @@
+From d97cca1e5df812be0e4de1e38091f02bb1e7ec4e Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Tue, 1 Aug 2023 10:27:15 +0200
+Subject: [PATCH 01/27] stdlib: Improve tst-realpath compatibility with source
+ fortification
+
+On GCC before 11, IPA can make the fortified realpath aware that the
+buffer size is not large enough (8 bytes instead of PATH_MAX bytes).
+Fix this by using a buffer that is large enough.
+
+(cherry picked from commit 510fc20d73de12c85823d9996faac74666e9c2e7)
+---
+ stdlib/tst-realpath.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/stdlib/tst-realpath.c b/stdlib/tst-realpath.c
+index f325c95a44..3694ecd8af 100644
+--- a/stdlib/tst-realpath.c
++++ b/stdlib/tst-realpath.c
+@@ -24,6 +24,7 @@
+    License along with the GNU C Library; if not, see
+    <https://www.gnu.org/licenses/>.  */
++#include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <malloc.h>
+@@ -50,7 +51,11 @@ void dealloc (void *p)
+ char* alloc (void)
+ {
+-  return (char *)malloc (8);
++#ifdef PATH_MAX
++  return (char *)malloc (PATH_MAX);
++#else
++  return (char *)malloc (4096);
++#endif
+ }
+ static int
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0002-x86-Fix-for-cache-computation-on-AMD-legacy-cpus.patch b/src/patches/glibc-2.38/0002-x86-Fix-for-cache-computation-on-AMD-legacy-cpus.patch
new file mode 100644 (file)
index 0000000..e5cc746
--- /dev/null
@@ -0,0 +1,286 @@
+From ced101ed9d3b7cfd12d97ef24940cb00b8658c81 Mon Sep 17 00:00:00 2001
+From: Sajan Karumanchi <sajan.karumanchi@amd.com>
+Date: Tue, 1 Aug 2023 15:20:55 +0000
+Subject: [PATCH 02/27] x86: Fix for cache computation on AMD legacy cpus.
+
+Some legacy AMD CPUs and hypervisors have the _cpuid_ '0x8000_001D'
+set to Zero, thus resulting in zeroed-out computed cache values.
+This patch reintroduces the old way of cache computation as a
+fail-safe option to handle these exceptions.
+Fixed 'level4_cache_size' value through handle_amd().
+
+Reviewed-by: Premachandra Mallappa <premachandra.mallappa@amd.com>
+Tested-by: Florian Weimer <fweimer@redhat.com>
+---
+ sysdeps/x86/dl-cacheinfo.h | 226 ++++++++++++++++++++++++++++++++-----
+ 1 file changed, 199 insertions(+), 27 deletions(-)
+
+diff --git a/sysdeps/x86/dl-cacheinfo.h b/sysdeps/x86/dl-cacheinfo.h
+index cd4d0351ae..285773039f 100644
+--- a/sysdeps/x86/dl-cacheinfo.h
++++ b/sysdeps/x86/dl-cacheinfo.h
+@@ -315,40 +315,206 @@ handle_amd (int name)
+ {
+   unsigned int eax;
+   unsigned int ebx;
+-  unsigned int ecx;
++  unsigned int ecx = 0;
+   unsigned int edx;
+-  unsigned int count = 0x1;
++  unsigned int max_cpuid = 0;
++  unsigned int fn = 0;
+   /* No level 4 cache (yet).  */
+   if (name > _SC_LEVEL3_CACHE_LINESIZE)
+     return 0;
+-  if (name >= _SC_LEVEL3_CACHE_SIZE)
+-    count = 0x3;
+-  else if (name >= _SC_LEVEL2_CACHE_SIZE)
+-    count = 0x2;
+-  else if (name >= _SC_LEVEL1_DCACHE_SIZE)
+-    count = 0x0;
++  __cpuid (0x80000000, max_cpuid, ebx, ecx, edx);
++
++  if (max_cpuid >= 0x8000001D)
++    /* Use __cpuid__ '0x8000_001D' to compute cache details.  */
++    {
++      unsigned int count = 0x1;
++
++      if (name >= _SC_LEVEL3_CACHE_SIZE)
++        count = 0x3;
++      else if (name >= _SC_LEVEL2_CACHE_SIZE)
++        count = 0x2;
++      else if (name >= _SC_LEVEL1_DCACHE_SIZE)
++        count = 0x0;
++
++      __cpuid_count (0x8000001D, count, eax, ebx, ecx, edx);
++
++      if (ecx != 0)
++        {
++          switch (name)
++            {
++            case _SC_LEVEL1_ICACHE_ASSOC:
++            case _SC_LEVEL1_DCACHE_ASSOC:
++            case _SC_LEVEL2_CACHE_ASSOC:
++            case _SC_LEVEL3_CACHE_ASSOC:
++              return ((ebx >> 22) & 0x3ff) + 1;
++            case _SC_LEVEL1_ICACHE_LINESIZE:
++            case _SC_LEVEL1_DCACHE_LINESIZE:
++            case _SC_LEVEL2_CACHE_LINESIZE:
++            case _SC_LEVEL3_CACHE_LINESIZE:
++              return (ebx & 0xfff) + 1;
++            case _SC_LEVEL1_ICACHE_SIZE:
++            case _SC_LEVEL1_DCACHE_SIZE:
++            case _SC_LEVEL2_CACHE_SIZE:
++            case _SC_LEVEL3_CACHE_SIZE:
++              return (((ebx >> 22) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1);
++            default:
++              __builtin_unreachable ();
++            }
++          return -1;
++        }
++    }
++
++  /* Legacy cache computation for CPUs prior to Bulldozer family.
++     This is also a fail-safe mechanism for some hypervisors that
++     accidentally configure __cpuid__ '0x8000_001D' to Zero.  */
+-  __cpuid_count (0x8000001D, count, eax, ebx, ecx, edx);
++  fn = 0x80000005 + (name >= _SC_LEVEL2_CACHE_SIZE);
++
++  if (max_cpuid < fn)
++    return 0;
++
++  __cpuid (fn, eax, ebx, ecx, edx);
++
++  if (name < _SC_LEVEL1_DCACHE_SIZE)
++    {
++      name += _SC_LEVEL1_DCACHE_SIZE - _SC_LEVEL1_ICACHE_SIZE;
++      ecx = edx;
++    }
+   switch (name)
+     {
+-    case _SC_LEVEL1_ICACHE_ASSOC:
+-    case _SC_LEVEL1_DCACHE_ASSOC:
+-    case _SC_LEVEL2_CACHE_ASSOC:
++      case _SC_LEVEL1_DCACHE_SIZE:
++        return (ecx >> 14) & 0x3fc00;
++
++      case _SC_LEVEL1_DCACHE_ASSOC:
++        ecx >>= 16;
++        if ((ecx & 0xff) == 0xff)
++        {
++          /* Fully associative.  */
++          return (ecx << 2) & 0x3fc00;
++        }
++        return ecx & 0xff;
++
++      case _SC_LEVEL1_DCACHE_LINESIZE:
++        return ecx & 0xff;
++
++      case _SC_LEVEL2_CACHE_SIZE:
++        return (ecx & 0xf000) == 0 ? 0 : (ecx >> 6) & 0x3fffc00;
++
++      case _SC_LEVEL2_CACHE_ASSOC:
++        switch ((ecx >> 12) & 0xf)
++          {
++            case 0:
++            case 1:
++            case 2:
++            case 4:
++              return (ecx >> 12) & 0xf;
++            case 6:
++              return 8;
++            case 8:
++              return 16;
++            case 10:
++              return 32;
++            case 11:
++              return 48;
++            case 12:
++              return 64;
++            case 13:
++              return 96;
++            case 14:
++              return 128;
++            case 15:
++              return ((ecx >> 6) & 0x3fffc00) / (ecx & 0xff);
++            default:
++              return 0;
++          }
++
++      case _SC_LEVEL2_CACHE_LINESIZE:
++        return (ecx & 0xf000) == 0 ? 0 : ecx & 0xff;
++
++      case _SC_LEVEL3_CACHE_SIZE:
++        {
++        long int total_l3_cache = 0, l3_cache_per_thread = 0;
++        unsigned int threads = 0;
++        const struct cpu_features *cpu_features;
++
++        if ((edx & 0xf000) == 0)
++          return 0;
++
++        total_l3_cache = (edx & 0x3ffc0000) << 1;
++        cpu_features = __get_cpu_features ();
++
++        /* Figure out the number of logical threads that share L3.  */
++        if (max_cpuid >= 0x80000008)
++          {
++            /* Get width of APIC ID.  */
++            __cpuid (0x80000008, eax, ebx, ecx, edx);
++            threads = (ecx & 0xff) + 1;
++          }
++
++        if (threads == 0)
++          {
++            /* If APIC ID width is not available, use logical
++            processor count.  */
++            __cpuid (0x00000001, eax, ebx, ecx, edx);
++            if ((edx & (1 << 28)) != 0)
++              threads = (ebx >> 16) & 0xff;
++          }
++
++        /* Cap usage of highest cache level to the number of
++           supported threads.  */
++        if (threads > 0)
++          l3_cache_per_thread = total_l3_cache/threads;
++
++        /* Get shared cache per ccx for Zen architectures.  */
++        if (cpu_features->basic.family >= 0x17)
++          {
++            long int l3_cache_per_ccx = 0;
++            /* Get number of threads share the L3 cache in CCX.  */
++            __cpuid_count (0x8000001D, 0x3, eax, ebx, ecx, edx);
++            unsigned int threads_per_ccx = ((eax >> 14) & 0xfff) + 1;
++            l3_cache_per_ccx = l3_cache_per_thread * threads_per_ccx;
++            return l3_cache_per_ccx;
++          }
++        else
++          {
++            return l3_cache_per_thread;
++          }
++      }
++
+     case _SC_LEVEL3_CACHE_ASSOC:
+-      return ecx ? ((ebx >> 22) & 0x3ff) + 1 : 0;
+-    case _SC_LEVEL1_ICACHE_LINESIZE:
+-    case _SC_LEVEL1_DCACHE_LINESIZE:
+-    case _SC_LEVEL2_CACHE_LINESIZE:
++      switch ((edx >> 12) & 0xf)
++      {
++        case 0:
++        case 1:
++        case 2:
++        case 4:
++          return (edx >> 12) & 0xf;
++        case 6:
++          return 8;
++        case 8:
++          return 16;
++        case 10:
++          return 32;
++        case 11:
++          return 48;
++        case 12:
++          return 64;
++        case 13:
++          return 96;
++        case 14:
++          return 128;
++        case 15:
++          return ((edx & 0x3ffc0000) << 1) / (edx & 0xff);
++        default:
++          return 0;
++      }
++
+     case _SC_LEVEL3_CACHE_LINESIZE:
+-      return ecx ? (ebx & 0xfff) + 1 : 0;
+-    case _SC_LEVEL1_ICACHE_SIZE:
+-    case _SC_LEVEL1_DCACHE_SIZE:
+-    case _SC_LEVEL2_CACHE_SIZE:
+-    case _SC_LEVEL3_CACHE_SIZE:
+-      return ecx ? (((ebx >> 22) & 0x3ff) + 1) * ((ebx & 0xfff) + 1) * (ecx + 1): 0;
++      return (edx & 0xf000) == 0 ? 0 : edx & 0xff;
++
+     default:
+       __builtin_unreachable ();
+     }
+@@ -703,7 +869,6 @@ dl_init_cacheinfo (struct cpu_features *cpu_features)
+       data = handle_amd (_SC_LEVEL1_DCACHE_SIZE);
+       core = handle_amd (_SC_LEVEL2_CACHE_SIZE);
+       shared = handle_amd (_SC_LEVEL3_CACHE_SIZE);
+-      shared_per_thread = shared;
+       level1_icache_size = handle_amd (_SC_LEVEL1_ICACHE_SIZE);
+       level1_icache_linesize = handle_amd (_SC_LEVEL1_ICACHE_LINESIZE);
+@@ -716,13 +881,20 @@ dl_init_cacheinfo (struct cpu_features *cpu_features)
+       level3_cache_size = shared;
+       level3_cache_assoc = handle_amd (_SC_LEVEL3_CACHE_ASSOC);
+       level3_cache_linesize = handle_amd (_SC_LEVEL3_CACHE_LINESIZE);
++      level4_cache_size = handle_amd (_SC_LEVEL4_CACHE_SIZE);
+       if (shared <= 0)
+-        /* No shared L3 cache.  All we have is the L2 cache.  */
+-      shared = core;
++        {
++           /* No shared L3 cache.  All we have is the L2 cache.  */
++           shared = core;
++        }
++      else if (cpu_features->basic.family < 0x17)
++        {
++           /* Account for exclusive L2 and L3 caches.  */
++           shared += core;
++        }
+-      if (shared_per_thread <= 0)
+-      shared_per_thread = shared;
++      shared_per_thread = shared;
+     }
+   cpu_features->level1_icache_size = level1_icache_size;
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0003-nscd-Do-not-rebuild-getaddrinfo-bug-30709.patch b/src/patches/glibc-2.38/0003-nscd-Do-not-rebuild-getaddrinfo-bug-30709.patch
new file mode 100644 (file)
index 0000000..6963cd7
--- /dev/null
@@ -0,0 +1,185 @@
+From 6b99458d197ab779ebb6ff632c168e2cbfa4f543 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Fri, 11 Aug 2023 10:10:16 +0200
+Subject: [PATCH 03/27] nscd: Do not rebuild getaddrinfo (bug 30709)
+
+The nscd daemon caches hosts data from NSS modules verbatim, without
+filtering protocol families or sorting them (otherwise separate caches
+would be needed for certain ai_flags combinations).  The cache
+implementation is complete separate from the getaddrinfo code.  This
+means that rebuilding getaddrinfo is not needed.  The only function
+actually used is __bump_nl_timestamp from check_pf.c, and this change
+moves it into nscd/connections.c.
+
+Tested on x86_64-linux-gnu with -fexceptions, built with
+build-many-glibcs.py.  I also backported this patch into a distribution
+that still supports nscd and verified manually that caching still works.
+
+Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+(cherry picked from commit 039ff51ac7e02db1cfc0c23e38ac7bfbb00221d1)
+---
+ include/ifaddrs.h                  |  4 ---
+ inet/check_pf.c                    |  9 ------
+ nscd/Makefile                      |  2 +-
+ nscd/connections.c                 | 11 +++++++
+ nscd/gai.c                         | 50 ------------------------------
+ sysdeps/unix/sysv/linux/check_pf.c | 17 +---------
+ 6 files changed, 13 insertions(+), 80 deletions(-)
+ delete mode 100644 nscd/gai.c
+
+diff --git a/include/ifaddrs.h b/include/ifaddrs.h
+index 416118f1b3..19a3afb19f 100644
+--- a/include/ifaddrs.h
++++ b/include/ifaddrs.h
+@@ -34,9 +34,5 @@ extern void __check_native (uint32_t a1_index, int *a1_native,
+                           uint32_t a2_index, int *a2_native)
+   attribute_hidden;
+-#if IS_IN (nscd)
+-extern uint32_t __bump_nl_timestamp (void) attribute_hidden;
+-#endif
+-
+ # endif /* !_ISOMAC */
+ #endif        /* ifaddrs.h */
+diff --git a/inet/check_pf.c b/inet/check_pf.c
+index 5310c99121..6d1475920f 100644
+--- a/inet/check_pf.c
++++ b/inet/check_pf.c
+@@ -60,12 +60,3 @@ __free_in6ai (struct in6addrinfo *in6ai)
+ {
+   /* Nothing to do.  */
+ }
+-
+-
+-#if IS_IN (nscd)
+-uint32_t
+-__bump_nl_timestamp (void)
+-{
+-  return 0;
+-}
+-#endif
+diff --git a/nscd/Makefile b/nscd/Makefile
+index 2a0489f4cf..16b6460ee9 100644
+--- a/nscd/Makefile
++++ b/nscd/Makefile
+@@ -35,7 +35,7 @@ nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
+               getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \
+               getsrvbynm_r getsrvbypt_r servicescache \
+               dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
+-              xmalloc xstrdup aicache initgrcache gai res_hconf \
++              xmalloc xstrdup aicache initgrcache res_hconf \
+               netgroupcache cachedumper
+ ifeq ($(build-nscd)$(have-thread-library),yesyes)
+diff --git a/nscd/connections.c b/nscd/connections.c
+index a405a44a9b..15693e5090 100644
+--- a/nscd/connections.c
++++ b/nscd/connections.c
+@@ -256,6 +256,17 @@ int inotify_fd = -1;
+ #ifdef HAVE_NETLINK
+ /* Descriptor for netlink status updates.  */
+ static int nl_status_fd = -1;
++
++static uint32_t
++__bump_nl_timestamp (void)
++{
++  static uint32_t nl_timestamp;
++
++  if (atomic_fetch_add_relaxed (&nl_timestamp, 1) + 1 == 0)
++    atomic_fetch_add_relaxed (&nl_timestamp, 1);
++
++  return nl_timestamp;
++}
+ #endif
+ /* Number of times clients had to wait.  */
+diff --git a/nscd/gai.c b/nscd/gai.c
+deleted file mode 100644
+index e29f3fe583..0000000000
+--- a/nscd/gai.c
++++ /dev/null
+@@ -1,50 +0,0 @@
+-/* Copyright (C) 2004-2023 Free Software Foundation, Inc.
+-   This file is part of the GNU C Library.
+-
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published
+-   by the Free Software Foundation; version 2 of the License, or
+-   (at your option) any later version.
+-
+-   This program 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 General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License
+-   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+-
+-#include <alloca.h>
+-#include <sys/stat.h>
+-
+-/* This file uses the getaddrinfo code but it compiles it without NSCD
+-   support.  We just need a few symbol renames.  */
+-#define __ioctl ioctl
+-#define __getsockname getsockname
+-#define __socket socket
+-#define __recvmsg recvmsg
+-#define __bind bind
+-#define __sendto sendto
+-#define __strchrnul strchrnul
+-#define __getline getline
+-#define __qsort_r qsort_r
+-/* nscd uses 1MB or 2MB thread stacks.  */
+-#define __libc_use_alloca(size) (size <= __MAX_ALLOCA_CUTOFF)
+-#define __getifaddrs getifaddrs
+-#define __freeifaddrs freeifaddrs
+-#undef __fstat64
+-#define __fstat64 fstat64
+-#undef __stat64
+-#define __stat64 stat64
+-
+-/* We are nscd, so we don't want to be talking to ourselves.  */
+-#undef  USE_NSCD
+-
+-#include <getaddrinfo.c>
+-
+-/* Support code.  */
+-#include <check_pf.c>
+-#include <check_native.c>
+-
+-/* Some variables normally defined in libc.  */
+-nss_action_list __nss_hosts_database attribute_hidden;
+diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
+index 2b0b8b6368..3aa6a00348 100644
+--- a/sysdeps/unix/sysv/linux/check_pf.c
++++ b/sysdeps/unix/sysv/linux/check_pf.c
+@@ -66,25 +66,10 @@ static struct cached_data *cache;
+ __libc_lock_define_initialized (static, lock);
+-#if IS_IN (nscd)
+-static uint32_t nl_timestamp;
+-
+-uint32_t
+-__bump_nl_timestamp (void)
+-{
+-  if (atomic_fetch_add_relaxed (&nl_timestamp, 1) + 1 == 0)
+-    atomic_fetch_add_relaxed (&nl_timestamp, 1);
+-
+-  return nl_timestamp;
+-}
+-#endif
+-
+ static inline uint32_t
+ get_nl_timestamp (void)
+ {
+-#if IS_IN (nscd)
+-  return nl_timestamp;
+-#elif defined USE_NSCD
++#if defined USE_NSCD
+   return __nscd_get_nl_timestamp ();
+ #else
+   return 0;
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0004-x86-Fix-incorrect-scope-of-setting-shared_per_thread.patch b/src/patches/glibc-2.38/0004-x86-Fix-incorrect-scope-of-setting-shared_per_thread.patch
new file mode 100644 (file)
index 0000000..a359273
--- /dev/null
@@ -0,0 +1,45 @@
+From 5ea70cc02626d9b85f1570153873d8648a47bf95 Mon Sep 17 00:00:00 2001
+From: Noah Goldstein <goldstein.w.n@gmail.com>
+Date: Thu, 10 Aug 2023 19:28:24 -0500
+Subject: [PATCH 04/27] 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.
+
+(cherry picked from commit 084fb31bc2c5f95ae0b9e6df4d3cf0ff43471ede)
+---
+ sysdeps/x86/dl-cacheinfo.h | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/sysdeps/x86/dl-cacheinfo.h b/sysdeps/x86/dl-cacheinfo.h
+index 285773039f..5ddb35c9d9 100644
+--- a/sysdeps/x86/dl-cacheinfo.h
++++ b/sysdeps/x86/dl-cacheinfo.h
+@@ -770,11 +770,10 @@ get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, u
+            level.  */
+         threads = ((cpu_features->features[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.  */
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0005-x86_64-Fix-build-with-disable-multiarch-BZ-30721.patch b/src/patches/glibc-2.38/0005-x86_64-Fix-build-with-disable-multiarch-BZ-30721.patch
new file mode 100644 (file)
index 0000000..e506318
--- /dev/null
@@ -0,0 +1,60 @@
+From 6135d50e44233d8c89ca788f78c669941ad09fb9 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 8 Aug 2023 09:27:54 -0300
+Subject: [PATCH 05/27] x86_64: Fix build with --disable-multiarch (BZ 30721)
+
+With multiarch disabled, the default memmove implementation provides
+the fortify routines for memcpy, mempcpy, and memmove.  However, it
+does not provide the internal hidden definitions used when building
+with fortify enabled.  The memset has a similar issue.
+
+Checked on x86_64-linux-gnu building with different options:
+default and --disable-multi-arch plus default, --disable-default-pie,
+--enable-fortify-source={2,3}, and --enable-fortify-source={2,3}
+with --disable-default-pie.
+Tested-by: Andreas K. Huettel <dilfridge@gentoo.org>
+Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+(cherry picked from commit 51cb52214fcd72849c640b12f5099ed3ac776181)
+---
+ sysdeps/x86_64/memcpy.S  | 2 +-
+ sysdeps/x86_64/memmove.S | 3 +++
+ sysdeps/x86_64/memset.S  | 1 +
+ 3 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/sysdeps/x86_64/memcpy.S b/sysdeps/x86_64/memcpy.S
+index d98500a78a..4922cba657 100644
+--- a/sysdeps/x86_64/memcpy.S
++++ b/sysdeps/x86_64/memcpy.S
+@@ -1 +1 @@
+-/* Implemented in memcpy.S.  */
++/* Implemented in memmove.S.  */
+diff --git a/sysdeps/x86_64/memmove.S b/sysdeps/x86_64/memmove.S
+index f0b84e3b52..c3c08165e1 100644
+--- a/sysdeps/x86_64/memmove.S
++++ b/sysdeps/x86_64/memmove.S
+@@ -46,6 +46,9 @@ weak_alias (__mempcpy, mempcpy)
+ #ifndef USE_MULTIARCH
+ libc_hidden_builtin_def (memmove)
++libc_hidden_builtin_def (__memmove_chk)
++libc_hidden_builtin_def (__memcpy_chk)
++libc_hidden_builtin_def (__mempcpy_chk)
+ # if defined SHARED && IS_IN (libc)
+ strong_alias (memmove, __memcpy)
+ libc_hidden_ver (memmove, memcpy)
+diff --git a/sysdeps/x86_64/memset.S b/sysdeps/x86_64/memset.S
+index 7c99df36db..c6df24e8de 100644
+--- a/sysdeps/x86_64/memset.S
++++ b/sysdeps/x86_64/memset.S
+@@ -32,6 +32,7 @@
+ #include "isa-default-impl.h"
+ libc_hidden_builtin_def (memset)
++libc_hidden_builtin_def (__memset_chk)
+ #if IS_IN (libc)
+ libc_hidden_def (__wmemset)
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0006-i686-Fix-build-with-disable-multiarch.patch b/src/patches/glibc-2.38/0006-i686-Fix-build-with-disable-multiarch.patch
new file mode 100644 (file)
index 0000000..13176ac
--- /dev/null
@@ -0,0 +1,100 @@
+From 7ac405a74c6069b0627dc2d8449a82a621f8ff06 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 8 Aug 2023 09:27:55 -0300
+Subject: [PATCH 06/27] i686: Fix build with --disable-multiarch
+
+Since i686 provides the fortified wrappers for memcpy, mempcpy,
+memmove, and memset on the same string implementation, the static
+build tries to optimized it by not tying the fortified wrappers
+to string routine (to avoid pulling the fortify function if
+they are not required).
+
+Checked on i686-linux-gnu building with different option:
+default and --disable-multi-arch plus default, --disable-default-pie,
+--enable-fortify-source={2,3}, and --enable-fortify-source={2,3}
+with --disable-default-pie.
+Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+(cherry picked from commit c73c96a4a1af1326df7f96eec58209e1e04066d8)
+---
+ sysdeps/i386/i686/memcpy.S                | 2 +-
+ sysdeps/i386/i686/mempcpy.S               | 2 +-
+ sysdeps/i386/i686/multiarch/memcpy_chk.c  | 2 ++
+ sysdeps/i386/i686/multiarch/memmove_chk.c | 2 ++
+ sysdeps/i386/i686/multiarch/mempcpy_chk.c | 2 ++
+ sysdeps/i386/i686/multiarch/memset_chk.c  | 2 ++
+ 6 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/sysdeps/i386/i686/memcpy.S b/sysdeps/i386/i686/memcpy.S
+index 9b48ec0ea1..b86af4aac9 100644
+--- a/sysdeps/i386/i686/memcpy.S
++++ b/sysdeps/i386/i686/memcpy.S
+@@ -27,7 +27,7 @@
+ #define LEN   SRC+4
+       .text
+-#if defined PIC && IS_IN (libc)
++#if defined SHARED && IS_IN (libc)
+ ENTRY_CHK (__memcpy_chk)
+       movl    12(%esp), %eax
+       cmpl    %eax, 16(%esp)
+diff --git a/sysdeps/i386/i686/mempcpy.S b/sysdeps/i386/i686/mempcpy.S
+index 26f8501e7d..14d9dd681a 100644
+--- a/sysdeps/i386/i686/mempcpy.S
++++ b/sysdeps/i386/i686/mempcpy.S
+@@ -27,7 +27,7 @@
+ #define LEN   SRC+4
+       .text
+-#if defined PIC && IS_IN (libc)
++#if defined SHARED && IS_IN (libc)
+ ENTRY_CHK (__mempcpy_chk)
+       movl    12(%esp), %eax
+       cmpl    %eax, 16(%esp)
+diff --git a/sysdeps/i386/i686/multiarch/memcpy_chk.c b/sysdeps/i386/i686/multiarch/memcpy_chk.c
+index ec945dc91f..c3a8aeaf18 100644
+--- a/sysdeps/i386/i686/multiarch/memcpy_chk.c
++++ b/sysdeps/i386/i686/multiarch/memcpy_chk.c
+@@ -32,4 +32,6 @@ libc_ifunc_redirected (__redirect_memcpy_chk, __memcpy_chk,
+ __hidden_ver1 (__memcpy_chk, __GI___memcpy_chk, __redirect_memcpy_chk)
+   __attribute__ ((visibility ("hidden"))) __attribute_copy__ (__memcpy_chk);
+ # endif
++#else
++# include <debug/memcpy_chk.c>
+ #endif
+diff --git a/sysdeps/i386/i686/multiarch/memmove_chk.c b/sysdeps/i386/i686/multiarch/memmove_chk.c
+index 55c7601d5d..070dde083a 100644
+--- a/sysdeps/i386/i686/multiarch/memmove_chk.c
++++ b/sysdeps/i386/i686/multiarch/memmove_chk.c
+@@ -32,4 +32,6 @@ libc_ifunc_redirected (__redirect_memmove_chk, __memmove_chk,
+ __hidden_ver1 (__memmove_chk, __GI___memmove_chk, __redirect_memmove_chk)
+   __attribute__ ((visibility ("hidden"))) __attribute_copy__ (__memmove_chk);
+ # endif
++#else
++# include <debug/memmove_chk.c>
+ #endif
+diff --git a/sysdeps/i386/i686/multiarch/mempcpy_chk.c b/sysdeps/i386/i686/multiarch/mempcpy_chk.c
+index 83569cf9d9..14360f1828 100644
+--- a/sysdeps/i386/i686/multiarch/mempcpy_chk.c
++++ b/sysdeps/i386/i686/multiarch/mempcpy_chk.c
+@@ -32,4 +32,6 @@ libc_ifunc_redirected (__redirect_mempcpy_chk, __mempcpy_chk,
+ __hidden_ver1 (__mempcpy_chk, __GI___mempcpy_chk, __redirect_mempcpy_chk)
+   __attribute__ ((visibility ("hidden"))) __attribute_copy__ (__mempcpy_chk);
+ # endif
++#else
++# include <debug/mempcpy_chk.c>
+ #endif
+diff --git a/sysdeps/i386/i686/multiarch/memset_chk.c b/sysdeps/i386/i686/multiarch/memset_chk.c
+index 1a7503858d..8179ef7c0b 100644
+--- a/sysdeps/i386/i686/multiarch/memset_chk.c
++++ b/sysdeps/i386/i686/multiarch/memset_chk.c
+@@ -32,4 +32,6 @@ libc_ifunc_redirected (__redirect_memset_chk, __memset_chk,
+ __hidden_ver1 (__memset_chk, __GI___memset_chk, __redirect_memset_chk)
+   __attribute__ ((visibility ("hidden"))) __attribute_copy__ (__memset_chk);
+ # endif
++#else
++# include <debug/memset_chk.c>
+ #endif
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0007-malloc-Enable-merging-of-remainders-in-memalign-bug-.patch b/src/patches/glibc-2.38/0007-malloc-Enable-merging-of-remainders-in-memalign-bug-.patch
new file mode 100644 (file)
index 0000000..22f2e83
--- /dev/null
@@ -0,0 +1,301 @@
+From 98c293c61f770b6b7a22f89a6ea81b711ecb1952 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Fri, 11 Aug 2023 11:18:17 +0200
+Subject: [PATCH 07/27] malloc: Enable merging of remainders in memalign (bug
+ 30723)
+
+Previously, calling _int_free from _int_memalign could put remainders
+into the tcache or into fastbins, where they are invisible to the
+low-level allocator.  This results in missed merge opportunities
+because once these freed chunks become available to the low-level
+allocator, further memalign allocations (even of the same size are)
+likely obstructing merges.
+
+Furthermore, during forwards merging in _int_memalign, do not
+completely give up when the remainder is too small to serve as a
+chunk on its own.  We can still give it back if it can be merged
+with the following unused chunk.  This makes it more likely that
+memalign calls in a loop achieve a compact memory layout,
+independently of initial heap layout.
+
+Drop some useless (unsigned long) casts along the way, and tweak
+the style to more closely match GNU on changed lines.
+
+Reviewed-by: DJ Delorie <dj@redhat.com>
+(cherry picked from commit 542b1105852568c3ebc712225ae78b8c8ba31a78)
+---
+ malloc/malloc.c | 197 +++++++++++++++++++++++++++++-------------------
+ 1 file changed, 121 insertions(+), 76 deletions(-)
+
+diff --git a/malloc/malloc.c b/malloc/malloc.c
+index e2f1a615a4..948f9759af 100644
+--- a/malloc/malloc.c
++++ b/malloc/malloc.c
+@@ -1086,6 +1086,11 @@ typedef struct malloc_chunk* mchunkptr;
+ static void*  _int_malloc(mstate, size_t);
+ static void     _int_free(mstate, mchunkptr, int);
++static void _int_free_merge_chunk (mstate, mchunkptr, INTERNAL_SIZE_T);
++static INTERNAL_SIZE_T _int_free_create_chunk (mstate,
++                                             mchunkptr, INTERNAL_SIZE_T,
++                                             mchunkptr, INTERNAL_SIZE_T);
++static void _int_free_maybe_consolidate (mstate, INTERNAL_SIZE_T);
+ static void*  _int_realloc(mstate, mchunkptr, INTERNAL_SIZE_T,
+                          INTERNAL_SIZE_T);
+ static void*  _int_memalign(mstate, size_t, size_t);
+@@ -4637,31 +4642,52 @@ _int_free (mstate av, mchunkptr p, int have_lock)
+     if (!have_lock)
+       __libc_lock_lock (av->mutex);
+-    nextchunk = chunk_at_offset(p, size);
+-
+-    /* Lightweight tests: check whether the block is already the
+-       top block.  */
+-    if (__glibc_unlikely (p == av->top))
+-      malloc_printerr ("double free or corruption (top)");
+-    /* Or whether the next chunk is beyond the boundaries of the arena.  */
+-    if (__builtin_expect (contiguous (av)
+-                        && (char *) nextchunk
+-                        >= ((char *) av->top + chunksize(av->top)), 0))
+-      malloc_printerr ("double free or corruption (out)");
+-    /* Or whether the block is actually not marked used.  */
+-    if (__glibc_unlikely (!prev_inuse(nextchunk)))
+-      malloc_printerr ("double free or corruption (!prev)");
+-
+-    nextsize = chunksize(nextchunk);
+-    if (__builtin_expect (chunksize_nomask (nextchunk) <= CHUNK_HDR_SZ, 0)
+-      || __builtin_expect (nextsize >= av->system_mem, 0))
+-      malloc_printerr ("free(): invalid next size (normal)");
++    _int_free_merge_chunk (av, p, size);
+-    free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);
++    if (!have_lock)
++      __libc_lock_unlock (av->mutex);
++  }
++  /*
++    If the chunk was allocated via mmap, release via munmap().
++  */
++
++  else {
++    munmap_chunk (p);
++  }
++}
++
++/* Try to merge chunk P of SIZE bytes with its neighbors.  Put the
++   resulting chunk on the appropriate bin list.  P must not be on a
++   bin list yet, and it can be in use.  */
++static void
++_int_free_merge_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T size)
++{
++  mchunkptr nextchunk = chunk_at_offset(p, size);
++
++  /* Lightweight tests: check whether the block is already the
++     top block.  */
++  if (__glibc_unlikely (p == av->top))
++    malloc_printerr ("double free or corruption (top)");
++  /* Or whether the next chunk is beyond the boundaries of the arena.  */
++  if (__builtin_expect (contiguous (av)
++                      && (char *) nextchunk
++                      >= ((char *) av->top + chunksize(av->top)), 0))
++    malloc_printerr ("double free or corruption (out)");
++  /* Or whether the block is actually not marked used.  */
++  if (__glibc_unlikely (!prev_inuse(nextchunk)))
++    malloc_printerr ("double free or corruption (!prev)");
++
++  INTERNAL_SIZE_T nextsize = chunksize(nextchunk);
++  if (__builtin_expect (chunksize_nomask (nextchunk) <= CHUNK_HDR_SZ, 0)
++      || __builtin_expect (nextsize >= av->system_mem, 0))
++    malloc_printerr ("free(): invalid next size (normal)");
++
++  free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);
+-    /* consolidate backward */
+-    if (!prev_inuse(p)) {
+-      prevsize = prev_size (p);
++  /* Consolidate backward.  */
++  if (!prev_inuse(p))
++    {
++      INTERNAL_SIZE_T prevsize = prev_size (p);
+       size += prevsize;
+       p = chunk_at_offset(p, -((long) prevsize));
+       if (__glibc_unlikely (chunksize(p) != prevsize))
+@@ -4669,9 +4695,25 @@ _int_free (mstate av, mchunkptr p, int have_lock)
+       unlink_chunk (av, p);
+     }
+-    if (nextchunk != av->top) {
++  /* Write the chunk header, maybe after merging with the following chunk.  */
++  size = _int_free_create_chunk (av, p, size, nextchunk, nextsize);
++  _int_free_maybe_consolidate (av, size);
++}
++
++/* Create a chunk at P of SIZE bytes, with SIZE potentially increased
++   to cover the immediately following chunk NEXTCHUNK of NEXTSIZE
++   bytes (if NEXTCHUNK is unused).  The chunk at P is not actually
++   read and does not have to be initialized.  After creation, it is
++   placed on the appropriate bin list.  The function returns the size
++   of the new chunk.  */
++static INTERNAL_SIZE_T
++_int_free_create_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T size,
++                      mchunkptr nextchunk, INTERNAL_SIZE_T nextsize)
++{
++  if (nextchunk != av->top)
++    {
+       /* get and clear inuse bit */
+-      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
++      bool nextinuse = inuse_bit_at_offset (nextchunk, nextsize);
+       /* consolidate forward */
+       if (!nextinuse) {
+@@ -4686,8 +4728,8 @@ _int_free (mstate av, mchunkptr p, int have_lock)
+       been given one chance to be used in malloc.
+       */
+-      bck = unsorted_chunks(av);
+-      fwd = bck->fd;
++      mchunkptr bck = unsorted_chunks (av);
++      mchunkptr fwd = bck->fd;
+       if (__glibc_unlikely (fwd->bk != bck))
+       malloc_printerr ("free(): corrupted unsorted chunks");
+       p->fd = fwd;
+@@ -4706,61 +4748,52 @@ _int_free (mstate av, mchunkptr p, int have_lock)
+       check_free_chunk(av, p);
+     }
+-    /*
+-      If the chunk borders the current high end of memory,
+-      consolidate into top
+-    */
+-
+-    else {
++  else
++    {
++      /* If the chunk borders the current high end of memory,
++       consolidate into top.  */
+       size += nextsize;
+       set_head(p, size | PREV_INUSE);
+       av->top = p;
+       check_chunk(av, p);
+     }
+-    /*
+-      If freeing a large space, consolidate possibly-surrounding
+-      chunks. Then, if the total unused topmost memory exceeds trim
+-      threshold, ask malloc_trim to reduce top.
+-
+-      Unless max_fast is 0, we don't know if there are fastbins
+-      bordering top, so we cannot tell for sure whether threshold
+-      has been reached unless fastbins are consolidated.  But we
+-      don't want to consolidate on each free.  As a compromise,
+-      consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD
+-      is reached.
+-    */
++  return size;
++}
+-    if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
++/* If freeing a large space, consolidate possibly-surrounding
++   chunks.  Then, if the total unused topmost memory exceeds trim
++   threshold, ask malloc_trim to reduce top.  */
++static void
++_int_free_maybe_consolidate (mstate av, INTERNAL_SIZE_T size)
++{
++  /* Unless max_fast is 0, we don't know if there are fastbins
++     bordering top, so we cannot tell for sure whether threshold has
++     been reached unless fastbins are consolidated.  But we don't want
++     to consolidate on each free.  As a compromise, consolidation is
++     performed if FASTBIN_CONSOLIDATION_THRESHOLD is reached.  */
++  if (size >= FASTBIN_CONSOLIDATION_THRESHOLD)
++    {
+       if (atomic_load_relaxed (&av->have_fastchunks))
+       malloc_consolidate(av);
+-      if (av == &main_arena) {
++      if (av == &main_arena)
++      {
+ #ifndef MORECORE_CANNOT_TRIM
+-      if ((unsigned long)(chunksize(av->top)) >=
+-          (unsigned long)(mp_.trim_threshold))
+-        systrim(mp_.top_pad, av);
++        if (chunksize (av->top) >= mp_.trim_threshold)
++          systrim (mp_.top_pad, av);
+ #endif
+-      } else {
+-      /* Always try heap_trim(), even if the top chunk is not
+-         large, because the corresponding heap might go away.  */
+-      heap_info *heap = heap_for_ptr(top(av));
++      }
++      else
++      {
++        /* Always try heap_trim, even if the top chunk is not large,
++           because the corresponding heap might go away.  */
++        heap_info *heap = heap_for_ptr (top (av));
+-      assert(heap->ar_ptr == av);
+-      heap_trim(heap, mp_.top_pad);
+-      }
++        assert (heap->ar_ptr == av);
++        heap_trim (heap, mp_.top_pad);
++      }
+     }
+-
+-    if (!have_lock)
+-      __libc_lock_unlock (av->mutex);
+-  }
+-  /*
+-    If the chunk was allocated via mmap, release via munmap().
+-  */
+-
+-  else {
+-    munmap_chunk (p);
+-  }
+ }
+ /*
+@@ -5221,7 +5254,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
+                 (av != &main_arena ? NON_MAIN_ARENA : 0));
+       set_inuse_bit_at_offset (newp, newsize);
+       set_head_size (p, leadsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
+-      _int_free (av, p, 1);
++      _int_free_merge_chunk (av, p, leadsize);
+       p = newp;
+       assert (newsize >= nb &&
+@@ -5232,15 +5265,27 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
+   if (!chunk_is_mmapped (p))
+     {
+       size = chunksize (p);
+-      if ((unsigned long) (size) > (unsigned long) (nb + MINSIZE))
++      mchunkptr nextchunk = chunk_at_offset(p, size);
++      INTERNAL_SIZE_T nextsize = chunksize(nextchunk);
++      if (size > nb)
+         {
+           remainder_size = size - nb;
+-          remainder = chunk_at_offset (p, nb);
+-          set_head (remainder, remainder_size | PREV_INUSE |
+-                    (av != &main_arena ? NON_MAIN_ARENA : 0));
+-          set_head_size (p, nb);
+-          _int_free (av, remainder, 1);
+-        }
++        if (remainder_size >= MINSIZE
++            || nextchunk == av->top
++            || !inuse_bit_at_offset (nextchunk, nextsize))
++          {
++            /* We can only give back the tail if it is larger than
++               MINSIZE, or if the following chunk is unused (top
++               chunk or unused in-heap chunk).  Otherwise we would
++               create a chunk that is smaller than MINSIZE.  */
++            remainder = chunk_at_offset (p, nb);
++            set_head_size (p, nb);
++            remainder_size = _int_free_create_chunk (av, remainder,
++                                                     remainder_size,
++                                                     nextchunk, nextsize);
++            _int_free_maybe_consolidate (av, remainder_size);
++          }
++      }
+     }
+   check_inuse_chunk (av, p);
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0008-malloc-Remove-bin-scanning-from-memalign-bug-30723.patch b/src/patches/glibc-2.38/0008-malloc-Remove-bin-scanning-from-memalign-bug-30723.patch
new file mode 100644 (file)
index 0000000..997082e
--- /dev/null
@@ -0,0 +1,269 @@
+From 2af141bda3cd407abd4bedf615f9e45fe79518e2 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Thu, 10 Aug 2023 19:36:56 +0200
+Subject: [PATCH 08/27] malloc: Remove bin scanning from memalign (bug 30723)
+
+On the test workload (mpv --cache=yes with VP9 video decoding), the
+bin scanning has a very poor success rate (less than 2%).  The tcache
+scanning has about 50% success rate, so keep that.
+
+Update comments in malloc/tst-memalign-2 to indicate the purpose
+of the tests.  Even with the scanning removed, the additional
+merging opportunities since commit 542b1105852568c3ebc712225ae78b
+("malloc: Enable merging of remainders in memalign (bug 30723)")
+are sufficient to pass the existing large bins test.
+
+Remove leftover variables from _int_free from refactoring in the
+same commit.
+
+Reviewed-by: DJ Delorie <dj@redhat.com>
+(cherry picked from commit 0dc7fc1cf094406a138e4d1bcf9553e59edcf89d)
+---
+ NEWS                    |   1 +
+ malloc/malloc.c         | 169 ++--------------------------------------
+ malloc/tst-memalign-2.c |   7 +-
+ 3 files changed, 11 insertions(+), 166 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index 872bc8907b..c339cb444e 100644
+--- a/NEWS
++++ b/NEWS
+@@ -132,6 +132,7 @@ The following bugs are resolved with this release:
+   [30555] string: strerror can incorrectly return NULL
+   [30579] malloc: trim_threshold in realloc lead to high memory usage
+   [30662] nscd: Group and password cache use errno in place of errval
++  [30723] posix_memalign repeatedly scans long bin lists
\f
+ Version 2.37
+diff --git a/malloc/malloc.c b/malloc/malloc.c
+index 948f9759af..d0bbbf3710 100644
+--- a/malloc/malloc.c
++++ b/malloc/malloc.c
+@@ -4488,12 +4488,6 @@ _int_free (mstate av, mchunkptr p, int have_lock)
+ {
+   INTERNAL_SIZE_T size;        /* its size */
+   mfastbinptr *fb;             /* associated fastbin */
+-  mchunkptr nextchunk;         /* next contiguous chunk */
+-  INTERNAL_SIZE_T nextsize;    /* its size */
+-  int nextinuse;               /* true if nextchunk is used */
+-  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
+-  mchunkptr bck;               /* misc temp for linking */
+-  mchunkptr fwd;               /* misc temp for linking */
+   size = chunksize (p);
+@@ -5032,42 +5026,6 @@ _int_realloc (mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
+    ------------------------------ memalign ------------------------------
+  */
+-/* Returns 0 if the chunk is not and does not contain the requested
+-   aligned sub-chunk, else returns the amount of "waste" from
+-   trimming.  NB is the *chunk* byte size, not the user byte
+-   size.  */
+-static size_t
+-chunk_ok_for_memalign (mchunkptr p, size_t alignment, size_t nb)
+-{
+-  void *m = chunk2mem (p);
+-  INTERNAL_SIZE_T size = chunksize (p);
+-  void *aligned_m = m;
+-
+-  if (__glibc_unlikely (misaligned_chunk (p)))
+-    malloc_printerr ("_int_memalign(): unaligned chunk detected");
+-
+-  aligned_m = PTR_ALIGN_UP (m, alignment);
+-
+-  INTERNAL_SIZE_T front_extra = (intptr_t) aligned_m - (intptr_t) m;
+-
+-  /* We can't trim off the front as it's too small.  */
+-  if (front_extra > 0 && front_extra < MINSIZE)
+-    return 0;
+-
+-  /* If it's a perfect fit, it's an exception to the return value rule
+-     (we would return zero waste, which looks like "not usable"), so
+-     handle it here by returning a small non-zero value instead.  */
+-  if (size == nb && front_extra == 0)
+-    return 1;
+-
+-  /* If the block we need fits in the chunk, calculate total waste.  */
+-  if (size > nb + front_extra)
+-    return size - nb;
+-
+-  /* Can't use this chunk.  */
+-  return 0;
+-}
+-
+ /* BYTES is user requested bytes, not requested chunksize bytes.  */
+ static void *
+ _int_memalign (mstate av, size_t alignment, size_t bytes)
+@@ -5082,7 +5040,6 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
+   mchunkptr remainder;            /* spare room at end to split off */
+   unsigned long remainder_size;   /* its size */
+   INTERNAL_SIZE_T size;
+-  mchunkptr victim;
+   nb = checked_request2size (bytes);
+   if (nb == 0)
+@@ -5101,129 +5058,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
+      we don't find anything in those bins, the common malloc code will
+      scan starting at 2x.  */
+-  /* This will be set if we found a candidate chunk.  */
+-  victim = NULL;
+-
+-  /* Fast bins are singly-linked, hard to remove a chunk from the middle
+-     and unlikely to meet our alignment requirements.  We have not done
+-     any experimentation with searching for aligned fastbins.  */
+-
+-  if (av != NULL)
+-    {
+-      int first_bin_index;
+-      int first_largebin_index;
+-      int last_bin_index;
+-
+-      if (in_smallbin_range (nb))
+-      first_bin_index = smallbin_index (nb);
+-      else
+-      first_bin_index = largebin_index (nb);
+-
+-      if (in_smallbin_range (nb * 2))
+-      last_bin_index = smallbin_index (nb * 2);
+-      else
+-      last_bin_index = largebin_index (nb * 2);
+-
+-      first_largebin_index = largebin_index (MIN_LARGE_SIZE);
+-
+-      int victim_index;                 /* its bin index */
+-
+-      for (victim_index = first_bin_index;
+-         victim_index < last_bin_index;
+-         victim_index ++)
+-      {
+-        victim = NULL;
+-
+-        if (victim_index < first_largebin_index)
+-          {
+-            /* Check small bins.  Small bin chunks are doubly-linked despite
+-               being the same size.  */
+-
+-            mchunkptr fwd;                    /* misc temp for linking */
+-            mchunkptr bck;                    /* misc temp for linking */
+-
+-            bck = bin_at (av, victim_index);
+-            fwd = bck->fd;
+-            while (fwd != bck)
+-              {
+-                if (chunk_ok_for_memalign (fwd, alignment, nb) > 0)
+-                  {
+-                    victim = fwd;
+-
+-                    /* Unlink it */
+-                    victim->fd->bk = victim->bk;
+-                    victim->bk->fd = victim->fd;
+-                    break;
+-                  }
+-
+-                fwd = fwd->fd;
+-              }
+-          }
+-        else
+-          {
+-            /* Check large bins.  */
+-            mchunkptr fwd;                    /* misc temp for linking */
+-            mchunkptr bck;                    /* misc temp for linking */
+-            mchunkptr best = NULL;
+-            size_t best_size = 0;
+-
+-            bck = bin_at (av, victim_index);
+-            fwd = bck->fd;
++  /* Call malloc with worst case padding to hit alignment. */
++  m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
+-            while (fwd != bck)
+-              {
+-                int extra;
+-
+-                if (chunksize (fwd) < nb)
+-                  break;
+-                extra = chunk_ok_for_memalign (fwd, alignment, nb);
+-                if (extra > 0
+-                    && (extra <= best_size || best == NULL))
+-                  {
+-                    best = fwd;
+-                    best_size = extra;
+-                  }
++  if (m == 0)
++    return 0;           /* propagate failure */
+-                fwd = fwd->fd;
+-              }
+-            victim = best;
+-
+-            if (victim != NULL)
+-              {
+-                unlink_chunk (av, victim);
+-                break;
+-              }
+-          }
+-
+-        if (victim != NULL)
+-          break;
+-      }
+-    }
+-
+-  /* Strategy: find a spot within that chunk that meets the alignment
+-     request, and then possibly free the leading and trailing space.
+-     This strategy is incredibly costly and can lead to external
+-     fragmentation if header and footer chunks are unused.  */
+-
+-  if (victim != NULL)
+-    {
+-      p = victim;
+-      m = chunk2mem (p);
+-      set_inuse (p);
+-      if (av != &main_arena)
+-      set_non_main_arena (p);
+-    }
+-  else
+-    {
+-      /* Call malloc with worst case padding to hit alignment. */
+-
+-      m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
+-
+-      if (m == 0)
+-      return 0;           /* propagate failure */
+-
+-      p = mem2chunk (m);
+-    }
++  p = mem2chunk (m);
+   if ((((unsigned long) (m)) % alignment) != 0)   /* misaligned */
+     {
+diff --git a/malloc/tst-memalign-2.c b/malloc/tst-memalign-2.c
+index f229283dbf..ecd6fa249e 100644
+--- a/malloc/tst-memalign-2.c
++++ b/malloc/tst-memalign-2.c
+@@ -86,7 +86,8 @@ do_test (void)
+       TEST_VERIFY (tcache_allocs[i].ptr1 == tcache_allocs[i].ptr2);
+     }
+-  /* Test for non-head tcache hits.  */
++  /* Test for non-head tcache hits.  This exercises the memalign
++     scanning code to find matching allocations.  */
+   for (i = 0; i < array_length (ptr); ++ i)
+     {
+       if (i == 4)
+@@ -113,7 +114,9 @@ do_test (void)
+   free (p);
+   TEST_VERIFY (count > 0);
+-  /* Large bins test.  */
++  /* Large bins test.  This verifies that the over-allocated parts
++     that memalign releases for future allocations can be reused by
++     memalign itself at least in some cases.  */
+   for (i = 0; i < LN; ++ i)
+     {
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0009-sysdeps-tst-bz21269-fix-test-parameter.patch b/src/patches/glibc-2.38/0009-sysdeps-tst-bz21269-fix-test-parameter.patch
new file mode 100644 (file)
index 0000000..1b04df2
--- /dev/null
@@ -0,0 +1,31 @@
+From c8ecda6251dd4a0dfe074e0a6011211cadeef742 Mon Sep 17 00:00:00 2001
+From: Sam James <sam@gentoo.org>
+Date: Fri, 4 Aug 2023 23:58:27 +0100
+Subject: [PATCH 09/27] sysdeps: tst-bz21269: fix test parameter
+
+All callers pass 1 or 0x11 anyway (same meaning according to man page),
+but still.
+
+Reviewed-by: DJ Delorie <dj@redhat.com>
+Signed-off-by: Sam James <sam@gentoo.org>
+(cherry picked from commit e0b712dd9183d527aae4506cd39564c14af3bb28)
+---
+ sysdeps/unix/sysv/linux/i386/tst-bz21269.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+index 51d4a1b082..f508ef8f16 100644
+--- a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
++++ b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+@@ -52,7 +52,7 @@ xset_thread_area (struct user_desc *u_info)
+ static void
+ xmodify_ldt (int func, const void *ptr, unsigned long bytecount)
+ {
+-  TEST_VERIFY_EXIT (syscall (SYS_modify_ldt, 1, ptr, bytecount) == 0);
++  TEST_VERIFY_EXIT (syscall (SYS_modify_ldt, func, ptr, bytecount) == 0);
+ }
+ static int
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0010-sysdeps-tst-bz21269-handle-ENOSYS-skip-appropriately.patch b/src/patches/glibc-2.38/0010-sysdeps-tst-bz21269-handle-ENOSYS-skip-appropriately.patch
new file mode 100644 (file)
index 0000000..fbc0b40
--- /dev/null
@@ -0,0 +1,42 @@
+From ad9b8399537670a990572c4b0c4da5411e3b68cf Mon Sep 17 00:00:00 2001
+From: Sam James <sam@gentoo.org>
+Date: Sat, 5 Aug 2023 00:04:33 +0100
+Subject: [PATCH 10/27] sysdeps: tst-bz21269: handle ENOSYS & skip
+ appropriately
+
+SYS_modify_ldt requires CONFIG_MODIFY_LDT_SYSCALL to be set in the kernel, which
+some distributions may disable for hardening. Check if that's the case (unset)
+and mark the test as UNSUPPORTED if so.
+
+Reviewed-by: DJ Delorie <dj@redhat.com>
+Signed-off-by: Sam James <sam@gentoo.org>
+(cherry picked from commit 652b9fdb77d9fd056d4dd26dad2c14142768ab49)
+---
+ sysdeps/unix/sysv/linux/i386/tst-bz21269.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+index f508ef8f16..28f5359bea 100644
+--- a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
++++ b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+@@ -52,7 +52,16 @@ xset_thread_area (struct user_desc *u_info)
+ static void
+ xmodify_ldt (int func, const void *ptr, unsigned long bytecount)
+ {
+-  TEST_VERIFY_EXIT (syscall (SYS_modify_ldt, func, ptr, bytecount) == 0);
++  long ret = syscall (SYS_modify_ldt, func, ptr, bytecount);
++
++  if (ret == -1)
++    {
++      if (errno == ENOSYS)
++      FAIL_UNSUPPORTED ("modify_ldt not supported");
++      FAIL_EXIT1 ("modify_ldt failed (errno=%d)", errno);
++    }
++
++  return 0;
+ }
+ static int
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0011-sysdeps-tst-bz21269-fix-Wreturn-type.patch b/src/patches/glibc-2.38/0011-sysdeps-tst-bz21269-fix-Wreturn-type.patch
new file mode 100644 (file)
index 0000000..51b79c1
--- /dev/null
@@ -0,0 +1,30 @@
+From 1aed90c9c8f8be9f68b58e96b6e4cd0fc08eb2b1 Mon Sep 17 00:00:00 2001
+From: Sam James <sam@gentoo.org>
+Date: Thu, 17 Aug 2023 09:30:29 +0100
+Subject: [PATCH 11/27] sysdeps: tst-bz21269: fix -Wreturn-type
+
+Thanks to Andreas Schwab for reporting.
+
+Fixes: 652b9fdb77d9fd056d4dd26dad2c14142768ab49
+Signed-off-by: Sam James <sam@gentoo.org>
+(cherry picked from commit 369f373057073c307938da91af16922bda3dff6a)
+---
+ sysdeps/unix/sysv/linux/i386/tst-bz21269.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+index 28f5359bea..822c41fceb 100644
+--- a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
++++ b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
+@@ -60,8 +60,6 @@ xmodify_ldt (int func, const void *ptr, unsigned long bytecount)
+       FAIL_UNSUPPORTED ("modify_ldt not supported");
+       FAIL_EXIT1 ("modify_ldt failed (errno=%d)", errno);
+     }
+-
+-  return 0;
+ }
+ static int
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0012-io-Fix-record-locking-contants-for-powerpc64-with-__.patch b/src/patches/glibc-2.38/0012-io-Fix-record-locking-contants-for-powerpc64-with-__.patch
new file mode 100644 (file)
index 0000000..5adfd3b
--- /dev/null
@@ -0,0 +1,91 @@
+From 5bdef6f27c91f45505ed5444147be4ed0e9bc3c7 Mon Sep 17 00:00:00 2001
+From: Aurelien Jarno <aurelien@aurel32.net>
+Date: Mon, 28 Aug 2023 23:30:37 +0200
+Subject: [PATCH 12/27] io: Fix record locking contants for powerpc64 with
+ __USE_FILE_OFFSET64
+
+Commit 5f828ff824e3b7cd1 ("io: Fix F_GETLK, F_SETLK, and F_SETLKW for
+powerpc64") fixed an issue with the value of the lock constants on
+powerpc64 when not using __USE_FILE_OFFSET64, but it ended-up also
+changing the value when using __USE_FILE_OFFSET64 causing an API change.
+
+Fix that by also checking that define, restoring the pre
+4d0fe291aed3a476a commit values:
+
+Default values:
+- F_GETLK: 5
+- F_SETLK: 6
+- F_SETLKW: 7
+
+With -D_FILE_OFFSET_BITS=64:
+- F_GETLK: 12
+- F_SETLK: 13
+- F_SETLKW: 14
+
+At the same time, it has been noticed that there was no test for io lock
+with __USE_FILE_OFFSET64, so just add one.
+
+Tested on x86_64-linux-gnu, i686-linux-gnu and
+powerpc64le-unknown-linux-gnu.
+
+Resolves: BZ #30804.
+Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
+(cherry picked from commit 434bf72a94de68f0cc7fbf3c44bf38c1911b70cb)
+---
+ NEWS                                         | 2 ++
+ io/Makefile                                  | 1 +
+ io/tst-fcntl-lock-lfs.c                      | 2 ++
+ sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h | 2 +-
+ 4 files changed, 6 insertions(+), 1 deletion(-)
+ create mode 100644 io/tst-fcntl-lock-lfs.c
+
+diff --git a/NEWS b/NEWS
+index c339cb444e..8156572cdf 100644
+--- a/NEWS
++++ b/NEWS
+@@ -133,6 +133,8 @@ The following bugs are resolved with this release:
+   [30579] malloc: trim_threshold in realloc lead to high memory usage
+   [30662] nscd: Group and password cache use errno in place of errval
+   [30723] posix_memalign repeatedly scans long bin lists
++  [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with
++    -D_FILE_OFFSET_BITS=64
\f
+ Version 2.37
+diff --git a/io/Makefile b/io/Makefile
+index 6ccc0e8691..8a3c83a3bb 100644
+--- a/io/Makefile
++++ b/io/Makefile
+@@ -192,6 +192,7 @@ tests := \
+   tst-fchownat \
+   tst-fcntl \
+   tst-fcntl-lock \
++  tst-fcntl-lock-lfs \
+   tst-fstatat \
+   tst-fts \
+   tst-fts-lfs \
+diff --git a/io/tst-fcntl-lock-lfs.c b/io/tst-fcntl-lock-lfs.c
+new file mode 100644
+index 0000000000..f2a909fb02
+--- /dev/null
++++ b/io/tst-fcntl-lock-lfs.c
+@@ -0,0 +1,2 @@
++#define _FILE_OFFSET_BITS 64
++#include <io/tst-fcntl-lock.c>
+diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h
+index f7615a447e..d8a291a331 100644
+--- a/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h
++++ b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h
+@@ -33,7 +33,7 @@
+ # define __O_LARGEFILE        0200000
+ #endif
+-#if __WORDSIZE == 64
++#if __WORDSIZE == 64 && !defined __USE_FILE_OFFSET64
+ # define F_GETLK      5
+ # define F_SETLK      6
+ # define F_SETLKW     7
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0013-libio-Fix-oversized-__io_vtables.patch b/src/patches/glibc-2.38/0013-libio-Fix-oversized-__io_vtables.patch
new file mode 100644 (file)
index 0000000..ef95483
--- /dev/null
@@ -0,0 +1,51 @@
+From 92201f16cbcfd9eafe314ef6654be2ea7ba25675 Mon Sep 17 00:00:00 2001
+From: Adam Jackson <ajax@redhat.com>
+Date: Fri, 8 Sep 2023 15:55:19 -0400
+Subject: [PATCH 13/27] libio: Fix oversized __io_vtables
+
+IO_VTABLES_LEN is the size of the struct array in bytes, not the number
+of __IO_jump_t's in the array. Drops just under 384kb from .rodata on
+LP64 machines.
+
+Fixes: 3020f72618e ("libio: Remove the usage of __libc_IO_vtables")
+Signed-off-by: Adam Jackson <ajax@redhat.com>
+Reviewed-by: Florian Weimer <fweimer@redhat.com>
+Tested-by: Florian Weimer <fweimer@redhat.com>
+(cherry picked from commit 8cb69e054386f980f9ff4d93b157861d72b2019e)
+---
+ libio/vtables.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/libio/vtables.c b/libio/vtables.c
+index 1d8ad612e9..34f7e15f1c 100644
+--- a/libio/vtables.c
++++ b/libio/vtables.c
+@@ -20,6 +20,7 @@
+ #include <libioP.h>
+ #include <stdio.h>
+ #include <ldsodefs.h>
++#include <array_length.h>
+ #include <pointer_guard.h>
+ #include <libio-macros.h>
+@@ -88,7 +89,7 @@
+ # pragma weak __wprintf_buffer_as_file_xsputn
+ #endif
+-const struct _IO_jump_t __io_vtables[IO_VTABLES_LEN] attribute_relro =
++const struct _IO_jump_t __io_vtables[] attribute_relro =
+ {
+   /* _IO_str_jumps  */
+   [IO_STR_JUMPS] =
+@@ -485,6 +486,8 @@ const struct _IO_jump_t __io_vtables[IO_VTABLES_LEN] attribute_relro =
+   },
+ #endif
+ };
++_Static_assert (array_length (__io_vtables) == IO_VTABLES_NUM,
++                "initializer count");
+ #ifdef SHARED
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0014-elf-Do-not-run-constructors-for-proxy-objects.patch b/src/patches/glibc-2.38/0014-elf-Do-not-run-constructors-for-proxy-objects.patch
new file mode 100644 (file)
index 0000000..70e18b6
--- /dev/null
@@ -0,0 +1,37 @@
+From 7ae211a01b085d0bde54bd13b887ce8f9d57c2b4 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Tue, 22 Aug 2023 13:56:25 +0200
+Subject: [PATCH 14/27] elf: Do not run constructors for proxy objects
+
+Otherwise, the ld.so constructor runs for each audit namespace
+and each dlmopen namespace.
+
+(cherry picked from commit f6c8204fd7fabf0cf4162eaf10ccf23258e4d10e)
+---
+ elf/dl-init.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/elf/dl-init.c b/elf/dl-init.c
+index 5b0732590f..ba4d2fdc85 100644
+--- a/elf/dl-init.c
++++ b/elf/dl-init.c
+@@ -25,10 +25,14 @@
+ 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.  */
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0015-elf-Always-call-destructors-in-reverse-constructor-o.patch b/src/patches/glibc-2.38/0015-elf-Always-call-destructors-in-reverse-constructor-o.patch
new file mode 100644 (file)
index 0000000..dd7b4e9
--- /dev/null
@@ -0,0 +1,669 @@
+From a3189f66a5f2fe86568286fa025fa153be04c6c0 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Fri, 8 Sep 2023 12:32:14 +0200
+Subject: [PATCH 15/27] 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>
+(cherry picked from commit 6985865bc3ad5b23147ee73466583dd7fdf65892)
+---
+ NEWS                       |   7 ++
+ elf/dl-close.c             | 113 +++++++++++++++++----------
+ elf/dl-fini.c              | 152 +++++++++++++------------------------
+ elf/dl-init.c              |  16 ++++
+ elf/dso-sort-tests-1.def   |  19 ++---
+ elf/tst-audit23.c          |  44 ++++++-----
+ include/link.h             |   4 +
+ sysdeps/generic/ldsodefs.h |   4 +
+ 8 files changed, 186 insertions(+), 173 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index 8156572cdf..f1a14f45dd 100644
+--- a/NEWS
++++ b/NEWS
+@@ -4,6 +4,13 @@ See the end for copying conditions.
+ Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
+ using `glibc' in the "product" field.
++\f
++Version 2.38.1
++
++The following bugs are resolved with this release:
++
++  [30785] Always call destructors in reverse constructor order
++
\f
+ Version 2.38
+diff --git a/elf/dl-close.c b/elf/dl-close.c
+index b887a44888..ea62d0e601 100644
+--- a/elf/dl-close.c
++++ b/elf/dl-close.c
+@@ -138,30 +138,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
+@@ -171,7 +172,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;
+@@ -198,8 +202,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;
++                      }
+                   }
+               }
+@@ -219,44 +226,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;
+@@ -267,8 +295,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)
+@@ -404,8 +432,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;
+       }
+     }
+@@ -476,10 +504,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);
+@@ -690,7 +719,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 9acb64f47c..e201d36651 100644
+--- a/elf/dl-fini.c
++++ b/elf/dl-fini.c
+@@ -24,116 +24,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 ba4d2fdc85..ffd05b7806 100644
+--- a/elf/dl-init.c
++++ b/elf/dl-init.c
+@@ -21,6 +21,7 @@
+ #include <ldsodefs.h>
+ #include <elf-initfini.h>
++struct link_map *_dl_init_called_list;
+ static void
+ call_init (struct link_map *l, int argc, char **argv, char **env)
+@@ -42,6 +43,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 4bf9052db1..61dc54f8ae 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 bb7d66c385..503699c36a 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 1d74feb2bd..69bda3ed17 100644
+--- a/include/link.h
++++ b/include/link.h
+@@ -278,6 +278,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 e8b7359b04..9ea9389a39 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1037,6 +1037,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;
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0016-elf-Remove-unused-l_text_end-field-from-struct-link_.patch b/src/patches/glibc-2.38/0016-elf-Remove-unused-l_text_end-field-from-struct-link_.patch
new file mode 100644 (file)
index 0000000..c674f8b
--- /dev/null
@@ -0,0 +1,143 @@
+From 750f19526ae71aac801c77a3f7ef5374890c09b7 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Fri, 8 Sep 2023 13:02:06 +0200
+Subject: [PATCH 16/27] 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>
+(cherry picked from commit 53df2ce6885da3d0e89e87dca7b095622296014f)
+---
+ elf/dl-load.c    | 2 +-
+ elf/dl-load.h    | 7 ++-----
+ elf/rtld.c       | 6 ------
+ elf/setup-vdso.h | 4 ----
+ include/link.h   | 2 --
+ 5 files changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index 9a87fda9c9..2923b1141d 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -1253,7 +1253,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 ecf6910c68..1d5207694b 100644
+--- a/elf/dl-load.h
++++ b/elf/dl-load.h
+@@ -83,14 +83,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.  */
++   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)
+@@ -103,7 +100,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 a91e2a4471..5107d16fe3 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -477,7 +477,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)) &__ehdr_start;
+   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
+@@ -1119,7 +1118,6 @@ rtld_setup_main_map (struct link_map *main_map)
+   bool has_interp = false;
+   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.  */
+@@ -1211,8 +1209,6 @@ rtld_setup_main_map (struct link_map *main_map)
+         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;
+         /* The next expected address is the page following this load
+            segment.  */
+@@ -1272,8 +1268,6 @@ rtld_setup_main_map (struct link_map *main_map)
+       = (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 0079842d1f..d92b12a7aa 100644
+--- a/elf/setup-vdso.h
++++ b/elf/setup-vdso.h
+@@ -51,9 +51,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.  */
+@@ -62,7 +59,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, false, false);
+       _dl_setup_hash (l);
+diff --git a/include/link.h b/include/link.h
+index 69bda3ed17..c6af095d87 100644
+--- a/include/link.h
++++ b/include/link.h
+@@ -253,8 +253,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];
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0017-elf-Move-l_init_called_next-to-old-place-of-l_text_e.patch b/src/patches/glibc-2.38/0017-elf-Move-l_init_called_next-to-old-place-of-l_text_e.patch
new file mode 100644 (file)
index 0000000..680fde9
--- /dev/null
@@ -0,0 +1,41 @@
+From d3ba6c1333b10680ce5900a628108507d9d4b844 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Mon, 11 Sep 2023 09:17:52 +0200
+Subject: [PATCH 17/27] 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.
+---
+ include/link.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/include/link.h b/include/link.h
+index c6af095d87..686813f281 100644
+--- a/include/link.h
++++ b/include/link.h
+@@ -254,6 +254,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'.  */
+@@ -276,10 +280,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
+       {
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0018-NEWS-Add-the-2.38.1-bug-list.patch b/src/patches/glibc-2.38/0018-NEWS-Add-the-2.38.1-bug-list.patch
new file mode 100644 (file)
index 0000000..1b5651f
--- /dev/null
@@ -0,0 +1,37 @@
+From 89da8bc588c2296252543b049bf6d9272321f90d Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Mon, 11 Sep 2023 10:06:15 +0200
+Subject: [PATCH 18/27] NEWS: Add the 2.38.1 bug list
+
+---
+ NEWS | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index f1a14f45dd..64596d5d09 100644
+--- a/NEWS
++++ b/NEWS
+@@ -9,7 +9,10 @@ Version 2.38.1
+ The following bugs are resolved with this release:
++  [30723] posix_memalign repeatedly scans long bin lists
+   [30785] Always call destructors in reverse constructor order
++  [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with
++    -D_FILE_OFFSET_BITS=64
\f
+ Version 2.38
+@@ -139,9 +142,6 @@ The following bugs are resolved with this release:
+   [30555] string: strerror can incorrectly return NULL
+   [30579] malloc: trim_threshold in realloc lead to high memory usage
+   [30662] nscd: Group and password cache use errno in place of errval
+-  [30723] posix_memalign repeatedly scans long bin lists
+-  [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with
+-    -D_FILE_OFFSET_BITS=64
\f
+ Version 2.37
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0019-CVE-2023-4527-Stack-read-overflow-with-large-TCP-res.patch b/src/patches/glibc-2.38/0019-CVE-2023-4527-Stack-read-overflow-with-large-TCP-res.patch
new file mode 100644 (file)
index 0000000..a32ddb8
--- /dev/null
@@ -0,0 +1,221 @@
+From b25508dd774b617f99419bdc3cf2ace4560cd2d6 Mon Sep 17 00:00:00 2001
+From: Florian Weimer <fweimer@redhat.com>
+Date: Wed, 13 Sep 2023 14:10:56 +0200
+Subject: [PATCH 19/27] CVE-2023-4527: Stack read overflow with large TCP
+ responses in no-aaaa mode
+
+Without passing alt_dns_packet_buffer, __res_context_search can only
+store 2048 bytes (what fits into dns_packet_buffer).  However,
+the function returns the total packet size, and the subsequent
+DNS parsing code in _nss_dns_gethostbyname4_r reads beyond the end
+of the stack-allocated buffer.
+
+Fixes commit f282cdbe7f436c75864e5640a4 ("resolv: Implement no-aaaa
+stub resolver option") and bug 30842.
+
+(cherry picked from commit bd77dd7e73e3530203be1c52c8a29d08270cb25d)
+---
+ NEWS                          |   9 +++
+ resolv/Makefile               |   2 +
+ resolv/nss_dns/dns-host.c     |   2 +-
+ resolv/tst-resolv-noaaaa-vc.c | 129 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 141 insertions(+), 1 deletion(-)
+ create mode 100644 resolv/tst-resolv-noaaaa-vc.c
+
+diff --git a/NEWS b/NEWS
+index 64596d5d09..dfee278a9c 100644
+--- a/NEWS
++++ b/NEWS
+@@ -7,12 +7,21 @@ using `glibc' in the "product" field.
\f
+ Version 2.38.1
++Security related changes:
++
++  CVE-2023-4527: If the system is configured in no-aaaa mode via
++  /etc/resolv.conf, getaddrinfo is called for the AF_UNSPEC address
++  family, and a DNS response is received over TCP that is larger than
++  2048 bytes, getaddrinfo may potentially disclose stack contents via
++  the returned address data, or crash.
++
+ The following bugs are resolved with this release:
+   [30723] posix_memalign repeatedly scans long bin lists
+   [30785] Always call destructors in reverse constructor order
+   [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with
+     -D_FILE_OFFSET_BITS=64
++  [30842] Stack read overflow in getaddrinfo in no-aaaa mode (CVE-2023-4527)
\f
+ Version 2.38
+diff --git a/resolv/Makefile b/resolv/Makefile
+index 054b1fa36c..2f99eb3862 100644
+--- a/resolv/Makefile
++++ b/resolv/Makefile
+@@ -102,6 +102,7 @@ tests += \
+   tst-resolv-invalid-cname \
+   tst-resolv-network \
+   tst-resolv-noaaaa \
++  tst-resolv-noaaaa-vc \
+   tst-resolv-nondecimal \
+   tst-resolv-res_init-multi \
+   tst-resolv-search \
+@@ -293,6 +294,7 @@ $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
+ $(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \
+   $(shared-thread-library)
+ $(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
++$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
+diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
+index 1d60c51f5e..5d0ab30de6 100644
+--- a/resolv/nss_dns/dns-host.c
++++ b/resolv/nss_dns/dns-host.c
+@@ -427,7 +427,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+     {
+       n = __res_context_search (ctx, name, C_IN, T_A,
+                               dns_packet_buffer, sizeof (dns_packet_buffer),
+-                              NULL, NULL, NULL, NULL, NULL);
++                              &alt_dns_packet_buffer, NULL, NULL, NULL, NULL);
+       if (n >= 0)
+       status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n,
+                                       &abuf, pat, errnop, herrnop, ttlp);
+diff --git a/resolv/tst-resolv-noaaaa-vc.c b/resolv/tst-resolv-noaaaa-vc.c
+new file mode 100644
+index 0000000000..9f5aebd99f
+--- /dev/null
++++ b/resolv/tst-resolv-noaaaa-vc.c
+@@ -0,0 +1,129 @@
++/* Test the RES_NOAAAA resolver option with a large response.
++   Copyright (C) 2022-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 <errno.h>
++#include <netdb.h>
++#include <resolv.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <support/check.h>
++#include <support/check_nss.h>
++#include <support/resolv_test.h>
++#include <support/support.h>
++#include <support/xmemstream.h>
++
++/* Used to keep track of the number of queries.  */
++static volatile unsigned int queries;
++
++/* If true, add a large TXT record at the start of the answer section.  */
++static volatile bool stuff_txt;
++
++static void
++response (const struct resolv_response_context *ctx,
++          struct resolv_response_builder *b,
++          const char *qname, uint16_t qclass, uint16_t qtype)
++{
++  /* If not using TCP, just force its use.  */
++  if (!ctx->tcp)
++    {
++      struct resolv_response_flags flags = {.tc = true};
++      resolv_response_init (b, flags);
++      resolv_response_add_question (b, qname, qclass, qtype);
++      return;
++    }
++
++  /* The test needs to send four queries, the first three are used to
++     grow the NSS buffer via the ERANGE handshake.  */
++  ++queries;
++  TEST_VERIFY (queries <= 4);
++
++  /* AAAA queries are supposed to be disabled.  */
++  TEST_COMPARE (qtype, T_A);
++  TEST_COMPARE (qclass, C_IN);
++  TEST_COMPARE_STRING (qname, "example.com");
++
++  struct resolv_response_flags flags = {};
++  resolv_response_init (b, flags);
++  resolv_response_add_question (b, qname, qclass, qtype);
++
++  resolv_response_section (b, ns_s_an);
++
++  if (stuff_txt)
++    {
++      resolv_response_open_record (b, qname, qclass, T_TXT, 60);
++      int zero = 0;
++      for (int i = 0; i <= 15000; ++i)
++        resolv_response_add_data (b, &zero, sizeof (zero));
++      resolv_response_close_record (b);
++    }
++
++  for (int i = 0; i < 200; ++i)
++    {
++      resolv_response_open_record (b, qname, qclass, qtype, 60);
++      char ipv4[4] = {192, 0, 2, i + 1};
++      resolv_response_add_data (b, &ipv4, sizeof (ipv4));
++      resolv_response_close_record (b);
++    }
++}
++
++static int
++do_test (void)
++{
++  struct resolv_test *obj = resolv_test_start
++    ((struct resolv_redirect_config)
++     {
++       .response_callback = response
++     });
++
++  _res.options |= RES_NOAAAA;
++
++  for (int do_stuff_txt = 0; do_stuff_txt < 2; ++do_stuff_txt)
++    {
++      queries = 0;
++      stuff_txt = do_stuff_txt;
++
++      struct addrinfo *ai = NULL;
++      int ret;
++      ret = getaddrinfo ("example.com", "80",
++                         &(struct addrinfo)
++                         {
++                           .ai_family = AF_UNSPEC,
++                           .ai_socktype = SOCK_STREAM,
++                         }, &ai);
++
++      char *expected_result;
++      {
++        struct xmemstream mem;
++        xopen_memstream (&mem);
++        for (int i = 0; i < 200; ++i)
++          fprintf (mem.out, "address: STREAM/TCP 192.0.2.%d 80\n", i + 1);
++        xfclose_memstream (&mem);
++        expected_result = mem.buffer;
++      }
++
++      check_addrinfo ("example.com", ai, ret, expected_result);
++
++      free (expected_result);
++      freeaddrinfo (ai);
++    }
++
++  resolv_test_end (obj);
++  return 0;
++}
++
++#include <support/test-driver.c>
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0020-getaddrinfo-Fix-use-after-free-in-getcanonname-CVE-2.patch b/src/patches/glibc-2.38/0020-getaddrinfo-Fix-use-after-free-in-getcanonname-CVE-2.patch
new file mode 100644 (file)
index 0000000..0ace485
--- /dev/null
@@ -0,0 +1,338 @@
+From 00ae4f10b504bc4564e9f22f00907093f1ab9338 Mon Sep 17 00:00:00 2001
+From: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date: Fri, 15 Sep 2023 13:51:12 -0400
+Subject: [PATCH 20/27] getaddrinfo: Fix use after free in getcanonname
+ (CVE-2023-4806)
+
+When an NSS plugin only implements the _gethostbyname2_r and
+_getcanonname_r callbacks, getaddrinfo could use memory that was freed
+during tmpbuf resizing, through h_name in a previous query response.
+
+The backing store for res->at->name when doing a query with
+gethostbyname3_r or gethostbyname2_r is tmpbuf, which is reallocated in
+gethosts during the query.  For AF_INET6 lookup with AI_ALL |
+AI_V4MAPPED, gethosts gets called twice, once for a v6 lookup and second
+for a v4 lookup.  In this case, if the first call reallocates tmpbuf
+enough number of times, resulting in a malloc, th->h_name (that
+res->at->name refers to) ends up on a heap allocated storage in tmpbuf.
+Now if the second call to gethosts also causes the plugin callback to
+return NSS_STATUS_TRYAGAIN, tmpbuf will get freed, resulting in a UAF
+reference in res->at->name.  This then gets dereferenced in the
+getcanonname_r plugin call, resulting in the use after free.
+
+Fix this by copying h_name over and freeing it at the end.  This
+resolves BZ #30843, which is assigned CVE-2023-4806.
+
+Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+(cherry picked from commit 973fe93a5675c42798b2161c6f29c01b0e243994)
+---
+ nss/Makefile                                  | 15 ++++-
+ nss/nss_test_gai_hv2_canonname.c              | 56 +++++++++++++++++
+ nss/tst-nss-gai-hv2-canonname.c               | 63 +++++++++++++++++++
+ nss/tst-nss-gai-hv2-canonname.h               |  1 +
+ .../postclean.req                             |  0
+ .../tst-nss-gai-hv2-canonname.script          |  2 +
+ sysdeps/posix/getaddrinfo.c                   | 25 +++++---
+ 7 files changed, 152 insertions(+), 10 deletions(-)
+ create mode 100644 nss/nss_test_gai_hv2_canonname.c
+ create mode 100644 nss/tst-nss-gai-hv2-canonname.c
+ create mode 100644 nss/tst-nss-gai-hv2-canonname.h
+ create mode 100644 nss/tst-nss-gai-hv2-canonname.root/postclean.req
+ create mode 100644 nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script
+
+diff --git a/nss/Makefile b/nss/Makefile
+index 06fcdc450f..8a5126ecf3 100644
+--- a/nss/Makefile
++++ b/nss/Makefile
+@@ -82,6 +82,7 @@ tests-container := \
+   tst-nss-test3 \
+   tst-reload1 \
+   tst-reload2 \
++  tst-nss-gai-hv2-canonname \
+ # tests-container
+ # Tests which need libdl
+@@ -145,7 +146,8 @@ libnss_compat-inhibit-o    = $(filter-out .os,$(object-suffixes))
+ ifeq ($(build-static-nss),yes)
+ tests-static          += tst-nss-static
+ endif
+-extra-test-objs               += nss_test1.os nss_test2.os nss_test_errno.os
++extra-test-objs               += nss_test1.os nss_test2.os nss_test_errno.os \
++                         nss_test_gai_hv2_canonname.os
+ include ../Rules
+@@ -180,12 +182,16 @@ rtld-tests-LDFLAGS += -Wl,--dynamic-list=nss_test.ver
+ libof-nss_test1 = extramodules
+ libof-nss_test2 = extramodules
+ libof-nss_test_errno = extramodules
++libof-nss_test_gai_hv2_canonname = extramodules
+ $(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps)
+       $(build-module)
+ $(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps)
+       $(build-module)
+ $(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps)
+       $(build-module)
++$(objpfx)/libnss_test_gai_hv2_canonname.so: \
++  $(objpfx)nss_test_gai_hv2_canonname.os $(link-libc-deps)
++      $(build-module)
+ $(objpfx)nss_test2.os : nss_test1.c
+ # Use the nss_files suffix for these objects as well.
+ $(objpfx)/libnss_test1.so$(libnss_files.so-version): $(objpfx)/libnss_test1.so
+@@ -195,10 +201,14 @@ $(objpfx)/libnss_test2.so$(libnss_files.so-version): $(objpfx)/libnss_test2.so
+ $(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \
+   $(objpfx)/libnss_test_errno.so
+       $(make-link)
++$(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version): \
++  $(objpfx)/libnss_test_gai_hv2_canonname.so
++      $(make-link)
+ $(patsubst %,$(objpfx)%.out,$(tests) $(tests-container)) : \
+       $(objpfx)/libnss_test1.so$(libnss_files.so-version) \
+       $(objpfx)/libnss_test2.so$(libnss_files.so-version) \
+-      $(objpfx)/libnss_test_errno.so$(libnss_files.so-version)
++      $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) \
++      $(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version)
+ ifeq (yes,$(have-thread-library))
+ $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
+@@ -215,3 +225,4 @@ LDFLAGS-tst-nss-test3 = -Wl,--disable-new-dtags
+ LDFLAGS-tst-nss-test4 = -Wl,--disable-new-dtags
+ LDFLAGS-tst-nss-test5 = -Wl,--disable-new-dtags
+ LDFLAGS-tst-nss-test_errno = -Wl,--disable-new-dtags
++LDFLAGS-tst-nss-test_gai_hv2_canonname = -Wl,--disable-new-dtags
+diff --git a/nss/nss_test_gai_hv2_canonname.c b/nss/nss_test_gai_hv2_canonname.c
+new file mode 100644
+index 0000000000..4439c83c9f
+--- /dev/null
++++ b/nss/nss_test_gai_hv2_canonname.c
+@@ -0,0 +1,56 @@
++/* NSS service provider that only provides gethostbyname2_r.
++   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/>.  */
++
++#include <nss.h>
++#include <stdlib.h>
++#include <string.h>
++#include "nss/tst-nss-gai-hv2-canonname.h"
++
++/* Catch misnamed and functions.  */
++#pragma GCC diagnostic error "-Wmissing-prototypes"
++NSS_DECLARE_MODULE_FUNCTIONS (test_gai_hv2_canonname)
++
++extern enum nss_status _nss_files_gethostbyname2_r (const char *, int,
++                                                  struct hostent *, char *,
++                                                  size_t, int *, int *);
++
++enum nss_status
++_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *name, int af,
++                                            struct hostent *result,
++                                            char *buffer, size_t buflen,
++                                            int *errnop, int *herrnop)
++{
++  return _nss_files_gethostbyname2_r (name, af, result, buffer, buflen, errnop,
++                                    herrnop);
++}
++
++enum nss_status
++_nss_test_gai_hv2_canonname_getcanonname_r (const char *name, char *buffer,
++                                          size_t buflen, char **result,
++                                          int *errnop, int *h_errnop)
++{
++  /* We expect QUERYNAME, which is a small enough string that it shouldn't fail
++     the test.  */
++  if (memcmp (QUERYNAME, name, sizeof (QUERYNAME))
++      || buflen < sizeof (QUERYNAME))
++    abort ();
++
++  strncpy (buffer, name, buflen);
++  *result = buffer;
++  return NSS_STATUS_SUCCESS;
++}
+diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c
+new file mode 100644
+index 0000000000..d5f10c07d6
+--- /dev/null
++++ b/nss/tst-nss-gai-hv2-canonname.c
+@@ -0,0 +1,63 @@
++/* Test NSS query path for plugins that only implement gethostbyname2
++   (#30843).
++   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/>.  */
++
++#include <nss.h>
++#include <netdb.h>
++#include <stdlib.h>
++#include <string.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include "nss/tst-nss-gai-hv2-canonname.h"
++
++#define PREPARE do_prepare
++
++static void do_prepare (int a, char **av)
++{
++  FILE *hosts = xfopen ("/etc/hosts", "w");
++  for (unsigned i = 2; i < 255; i++)
++    {
++      fprintf (hosts, "ff01::ff02:ff03:%u:2\ttest.example.com\n", i);
++      fprintf (hosts, "192.168.0.%u\ttest.example.com\n", i);
++    }
++  xfclose (hosts);
++}
++
++static int
++do_test (void)
++{
++  __nss_configure_lookup ("hosts", "test_gai_hv2_canonname");
++
++  struct addrinfo hints = {};
++  struct addrinfo *result = NULL;
++
++  hints.ai_family = AF_INET6;
++  hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_CANONNAME;
++
++  int ret = getaddrinfo (QUERYNAME, NULL, &hints, &result);
++
++  if (ret != 0)
++    FAIL_EXIT1 ("getaddrinfo failed: %s\n", gai_strerror (ret));
++
++  TEST_COMPARE_STRING (result->ai_canonname, QUERYNAME);
++
++  freeaddrinfo(result);
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/nss/tst-nss-gai-hv2-canonname.h b/nss/tst-nss-gai-hv2-canonname.h
+new file mode 100644
+index 0000000000..14f2a9cb08
+--- /dev/null
++++ b/nss/tst-nss-gai-hv2-canonname.h
+@@ -0,0 +1 @@
++#define QUERYNAME "test.example.com"
+diff --git a/nss/tst-nss-gai-hv2-canonname.root/postclean.req b/nss/tst-nss-gai-hv2-canonname.root/postclean.req
+new file mode 100644
+index 0000000000..e69de29bb2
+diff --git a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script
+new file mode 100644
+index 0000000000..31848b4a28
+--- /dev/null
++++ b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script
+@@ -0,0 +1,2 @@
++cp $B/nss/libnss_test_gai_hv2_canonname.so $L/libnss_test_gai_hv2_canonname.so.2
++su
+diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
+index 0356b622be..b2236b105c 100644
+--- a/sysdeps/posix/getaddrinfo.c
++++ b/sysdeps/posix/getaddrinfo.c
+@@ -120,6 +120,7 @@ struct gaih_result
+ {
+   struct gaih_addrtuple *at;
+   char *canon;
++  char *h_name;
+   bool free_at;
+   bool got_ipv6;
+ };
+@@ -165,6 +166,7 @@ gaih_result_reset (struct gaih_result *res)
+   if (res->free_at)
+     free (res->at);
+   free (res->canon);
++  free (res->h_name);
+   memset (res, 0, sizeof (*res));
+ }
+@@ -203,9 +205,8 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
+   return 0;
+ }
+-/* Convert struct hostent to a list of struct gaih_addrtuple objects.  h_name
+-   is not copied, and the struct hostent object must not be deallocated
+-   prematurely.  The new addresses are appended to the tuple array in RES.  */
++/* Convert struct hostent to a list of struct gaih_addrtuple objects.  The new
++   addresses are appended to the tuple array in RES.  */
+ static bool
+ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+                                  struct hostent *h, struct gaih_result *res)
+@@ -238,6 +239,15 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+   res->at = array;
+   res->free_at = true;
++  /* Duplicate h_name because it may get reclaimed when the underlying storage
++     is freed.  */
++  if (res->h_name == NULL)
++    {
++      res->h_name = __strdup (h->h_name);
++      if (res->h_name == NULL)
++      return false;
++    }
++
+   /* Update the next pointers on reallocation.  */
+   for (size_t i = 0; i < old; i++)
+     array[i].next = array + i + 1;
+@@ -262,7 +272,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+       }
+       array[i].next = array + i + 1;
+     }
+-  array[0].name = h->h_name;
+   array[count - 1].next = NULL;
+   return true;
+@@ -324,15 +333,15 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name,
+    memory allocation failure.  The returned string is allocated on the
+    heap; the caller has to free it.  */
+ static char *
+-getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name)
++getcanonname (nss_action_list nip, const char *hname, const char *name)
+ {
+   nss_getcanonname_r *cfct = __nss_lookup_function (nip, "getcanonname_r");
+   char *s = (char *) name;
+   if (cfct != NULL)
+     {
+       char buf[256];
+-      if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf),
+-                            &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS)
++      if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno,
++                            &h_errno)) != NSS_STATUS_SUCCESS)
+       /* If the canonical name cannot be determined, use the passed
+          string.  */
+       s = (char *) name;
+@@ -771,7 +780,7 @@ get_nss_addresses (const char *name, const struct addrinfo *req,
+                 if ((req->ai_flags & AI_CANONNAME) != 0
+                     && res->canon == NULL)
+                   {
+-                    char *canonbuf = getcanonname (nip, res->at, name);
++                    char *canonbuf = getcanonname (nip, res->h_name, name);
+                     if (canonbuf == NULL)
+                       {
+                         __resolv_context_put (res_ctx);
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0021-iconv-restore-verbosity-with-unrecognized-encoding-n.patch b/src/patches/glibc-2.38/0021-iconv-restore-verbosity-with-unrecognized-encoding-n.patch
new file mode 100644 (file)
index 0000000..662604f
--- /dev/null
@@ -0,0 +1,32 @@
+From 63250e9c571314b6daa2c949ea0af335ee766751 Mon Sep 17 00:00:00 2001
+From: Andreas Schwab <schwab@suse.de>
+Date: Tue, 1 Aug 2023 17:01:37 +0200
+Subject: [PATCH 21/27] iconv: restore verbosity with unrecognized encoding
+ names (bug 30694)
+
+Commit 91927b7c76 ("Rewrite iconv option parsing [BZ #19519]") changed the
+iconv program to call __gconv_open directly instead of the iconv_open
+wrapper, but the former does not set errno.  Update the caller to
+interpret the return codes like iconv_open does.
+
+(cherry picked from commit fc72b6d7d818ab2868920af956d1542d03342a4d)
+---
+ iconv/iconv_prog.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/iconv/iconv_prog.c b/iconv/iconv_prog.c
+index bee898c63c..cf32cf9b44 100644
+--- a/iconv/iconv_prog.c
++++ b/iconv/iconv_prog.c
+@@ -187,7 +187,7 @@ main (int argc, char *argv[])
+       if (res != __GCONV_OK)
+       {
+-        if (errno == EINVAL)
++        if (res == __GCONV_NOCONV || res == __GCONV_NODB)
+           {
+             /* Try to be nice with the user and tell her which of the
+                two encoding names is wrong.  This is possible because
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0022-string-Fix-tester-build-with-fortify-enable-with-gcc.patch b/src/patches/glibc-2.38/0022-string-Fix-tester-build-with-fortify-enable-with-gcc.patch
new file mode 100644 (file)
index 0000000..d357c99
--- /dev/null
@@ -0,0 +1,50 @@
+From d94461bb86ba176b9390c0015bb612a528e22d95 Mon Sep 17 00:00:00 2001
+From: Mahesh Bodapati <bmahi496@linux.ibm.com>
+Date: Fri, 11 Aug 2023 10:38:25 -0500
+Subject: [PATCH 22/27] string: Fix tester build with fortify enable with gcc <
+ 12
+
+When building with fortify enabled, GCC < 12 issues a warning on the
+fortify strncat wrapper might overflow the destination buffer (the
+failure is tied to -Werror).
+
+Checked on ppc64 and x86_64.
+Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+(cherry picked from commit f1c7ed0859a45929136836341741c7cd70f428cb)
+---
+ string/tester.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/string/tester.c b/string/tester.c
+index f7d4bac5a8..824cf315ff 100644
+--- a/string/tester.c
++++ b/string/tester.c
+@@ -34,6 +34,14 @@
+ DIAG_IGNORE_NEEDS_COMMENT (8, "-Wstringop-truncation");
+ #endif
++/* When building with fortify enabled, GCC < 12 issues a warning on the
++   fortify strncat wrapper might overflow the destination buffer (the
++   failure is tied to -Werror).
++   Triggered by strncat fortify wrapper when it is enabled.  */
++#if __GNUC_PREREQ (11, 0)
++DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread");
++#endif
++
+ #include <errno.h>
+ #include <stdint.h>
+ #include <stdio.h>
+@@ -52,9 +60,6 @@ DIAG_IGNORE_NEEDS_COMMENT (5.0, "-Wmemset-transposed-args");
+ DIAG_IGNORE_NEEDS_COMMENT (9, "-Wrestrict");
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Wstringop-overflow=");
+ #endif
+-#if __GNUC_PREREQ (11, 0)
+-DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread");
+-#endif
+ #define       STREQ(a, b)     (strcmp((a), (b)) == 0)
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0023-manual-jobs.texi-Add-missing-item-EPERM-for-getpgid.patch b/src/patches/glibc-2.38/0023-manual-jobs.texi-Add-missing-item-EPERM-for-getpgid.patch
new file mode 100644 (file)
index 0000000..444aaf6
--- /dev/null
@@ -0,0 +1,30 @@
+From 0e1ef6779a90bc0f8a05bc367796df2793deecaa Mon Sep 17 00:00:00 2001
+From: Mark Wielaard <mark@klomp.org>
+Date: Thu, 24 Aug 2023 21:36:34 +0200
+Subject: [PATCH 23/27] manual/jobs.texi: Add missing @item EPERM for getpgid
+
+The missing @item makes it look like errno will be set to ESRCH
+if a cross-session getpgid is not permitted.
+
+Found by ulfvonbelow on irc.
+
+(cherry picked from commit 5a21cefd5abab1b99eda1fbf84204a9bf41662ab)
+---
+ manual/job.texi | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/manual/job.texi b/manual/job.texi
+index 42cb9fb26d..8157f13a1c 100644
+--- a/manual/job.texi
++++ b/manual/job.texi
+@@ -1133,6 +1133,7 @@ following @code{errno} error conditions are defined for this function:
+ @table @code
+ @item ESRCH
+ There is no process with the given process ID @var{pid}.
++@item EPERM
+ The calling process and the process specified by @var{pid} are in
+ different sessions, and the implementation doesn't allow to access the
+ process group ID of the process with ID @var{pid} from the calling
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0024-Fix-leak-in-getaddrinfo-introduced-by-the-fix-for-CV.patch b/src/patches/glibc-2.38/0024-Fix-leak-in-getaddrinfo-introduced-by-the-fix-for-CV.patch
new file mode 100644 (file)
index 0000000..dc41d35
--- /dev/null
@@ -0,0 +1,98 @@
+From 5ee59ca371b99984232d7584fe2b1a758b4421d3 Mon Sep 17 00:00:00 2001
+From: Romain Geissler <romain.geissler@amadeus.com>
+Date: Mon, 25 Sep 2023 01:21:51 +0100
+Subject: [PATCH 24/27] Fix leak in getaddrinfo introduced by the fix for
+ CVE-2023-4806 [BZ #30843]
+
+This patch fixes a very recently added leak in getaddrinfo.
+
+This was assigned CVE-2023-5156.
+
+Resolves: BZ #30884
+Related: BZ #30842
+
+Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+(cherry picked from commit ec6b95c3303c700eb89eebeda2d7264cc184a796)
+---
+ nss/Makefile                    | 20 ++++++++++++++++++++
+ nss/tst-nss-gai-hv2-canonname.c |  3 +++
+ sysdeps/posix/getaddrinfo.c     |  4 +---
+ 3 files changed, 24 insertions(+), 3 deletions(-)
+
+diff --git a/nss/Makefile b/nss/Makefile
+index 8a5126ecf3..668ba34b18 100644
+--- a/nss/Makefile
++++ b/nss/Makefile
+@@ -149,6 +149,15 @@ endif
+ extra-test-objs               += nss_test1.os nss_test2.os nss_test_errno.os \
+                          nss_test_gai_hv2_canonname.os
++ifeq ($(run-built-tests),yes)
++ifneq (no,$(PERL))
++tests-special += $(objpfx)mtrace-tst-nss-gai-hv2-canonname.out
++endif
++endif
++
++generated += mtrace-tst-nss-gai-hv2-canonname.out \
++              tst-nss-gai-hv2-canonname.mtrace
++
+ include ../Rules
+ ifeq (yes,$(have-selinux))
+@@ -217,6 +226,17 @@ endif
+ $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)/libnss_files.so
+ $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)/libnss_files.so
++tst-nss-gai-hv2-canonname-ENV = \
++              MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \
++              LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
++$(objpfx)mtrace-tst-nss-gai-hv2-canonname.out: \
++  $(objpfx)tst-nss-gai-hv2-canonname.out
++      { test -r $(objpfx)tst-nss-gai-hv2-canonname.mtrace \
++      || ( echo "tst-nss-gai-hv2-canonname.mtrace does not exist"; exit 77; ) \
++      && $(common-objpfx)malloc/mtrace \
++      $(objpfx)tst-nss-gai-hv2-canonname.mtrace; } > $@; \
++      $(evaluate-test)
++
+ # Disable DT_RUNPATH on NSS tests so that the glibc internal NSS
+ # functions can load testing NSS modules via DT_RPATH.
+ LDFLAGS-tst-nss-test1 = -Wl,--disable-new-dtags
+diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c
+index d5f10c07d6..7db53cf09d 100644
+--- a/nss/tst-nss-gai-hv2-canonname.c
++++ b/nss/tst-nss-gai-hv2-canonname.c
+@@ -21,6 +21,7 @@
+ #include <netdb.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <mcheck.h>
+ #include <support/check.h>
+ #include <support/xstdio.h>
+ #include "nss/tst-nss-gai-hv2-canonname.h"
+@@ -41,6 +42,8 @@ static void do_prepare (int a, char **av)
+ static int
+ do_test (void)
+ {
++  mtrace ();
++
+   __nss_configure_lookup ("hosts", "test_gai_hv2_canonname");
+   struct addrinfo hints = {};
+diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
+index b2236b105c..13082305d3 100644
+--- a/sysdeps/posix/getaddrinfo.c
++++ b/sysdeps/posix/getaddrinfo.c
+@@ -1196,9 +1196,7 @@ free_and_return:
+   if (malloc_name)
+     free ((char *) name);
+   free (addrmem);
+-  if (res.free_at)
+-    free (res.at);
+-  free (res.canon);
++  gaih_result_reset (&res);
+   return result;
+ }
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0025-Document-CVE-2023-4806-and-CVE-2023-5156-in-NEWS.patch b/src/patches/glibc-2.38/0025-Document-CVE-2023-4806-and-CVE-2023-5156-in-NEWS.patch
new file mode 100644 (file)
index 0000000..82d061e
--- /dev/null
@@ -0,0 +1,36 @@
+From f6445dc94da185b3d1ee283f0ca0a34c4e1986cc Mon Sep 17 00:00:00 2001
+From: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date: Tue, 26 Sep 2023 07:38:07 -0400
+Subject: [PATCH 25/27] Document CVE-2023-4806 and CVE-2023-5156 in NEWS
+
+These are tracked in BZ #30884 and BZ #30843.
+
+Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+(cherry picked from commit fd134feba35fa839018965733b34d28a09a075dd)
+---
+ NEWS | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/NEWS b/NEWS
+index dfee278a9c..f1b1b0a3b4 100644
+--- a/NEWS
++++ b/NEWS
+@@ -15,6 +15,15 @@ Security related changes:
+   2048 bytes, getaddrinfo may potentially disclose stack contents via
+   the returned address data, or crash.
++  CVE-2023-4806: When an NSS plugin only implements the
++  _gethostbyname2_r and _getcanonname_r callbacks, getaddrinfo could use
++  memory that was freed during buffer resizing, potentially causing a
++  crash or read or write to arbitrary memory.
++
++  CVE-2023-5156: The fix for CVE-2023-4806 introduced a memory leak when
++  an application calls getaddrinfo for AF_INET6 with AI_CANONNAME,
++  AI_ALL and AI_V4MAPPED flags set.
++
+ The following bugs are resolved with this release:
+   [30723] posix_memalign repeatedly scans long bin lists
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0026-Propagate-GLIBC_TUNABLES-in-setxid-binaries.patch b/src/patches/glibc-2.38/0026-Propagate-GLIBC_TUNABLES-in-setxid-binaries.patch
new file mode 100644 (file)
index 0000000..d67de05
--- /dev/null
@@ -0,0 +1,32 @@
+From 73e3fcd1a552783e66ff1f65c5f322e2f17a81d1 Mon Sep 17 00:00:00 2001
+From: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date: Tue, 19 Sep 2023 13:25:40 -0400
+Subject: [PATCH 26/27] Propagate GLIBC_TUNABLES in setxid binaries
+
+GLIBC_TUNABLES scrubbing happens earlier than envvar scrubbing and some
+tunables are required to propagate past setxid boundary, like their
+env_alias.  Rely on tunable scrubbing to clean out GLIBC_TUNABLES like
+before, restoring behaviour in glibc 2.37 and earlier.
+
+Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+(cherry picked from commit 0d5f9ea97f1b39f2a855756078771673a68497e1)
+---
+ sysdeps/generic/unsecvars.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
+index 81397fb90b..8278c50a84 100644
+--- a/sysdeps/generic/unsecvars.h
++++ b/sysdeps/generic/unsecvars.h
+@@ -4,7 +4,6 @@
+ #define UNSECURE_ENVVARS \
+   "GCONV_PATH\0"                                                            \
+   "GETCONF_DIR\0"                                                           \
+-  "GLIBC_TUNABLES\0"                                                        \
+   "HOSTALIASES\0"                                                           \
+   "LD_AUDIT\0"                                                                      \
+   "LD_DEBUG\0"                                                                      \
+-- 
+2.39.2
+
diff --git a/src/patches/glibc-2.38/0027-tunables-Terminate-if-end-of-input-is-reached-CVE-20.patch b/src/patches/glibc-2.38/0027-tunables-Terminate-if-end-of-input-is-reached-CVE-20.patch
new file mode 100644 (file)
index 0000000..735153a
--- /dev/null
@@ -0,0 +1,173 @@
+From 750a45a783906a19591fb8ff6b7841470f1f5701 Mon Sep 17 00:00:00 2001
+From: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date: Tue, 19 Sep 2023 18:39:32 -0400
+Subject: [PATCH 27/27] tunables: Terminate if end of input is reached
+ (CVE-2023-4911)
+
+The string parsing routine may end up writing beyond bounds of tunestr
+if the input tunable string is malformed, of the form name=name=val.
+This gets processed twice, first as name=name=val and next as name=val,
+resulting in tunestr being name=name=val:name=val, thus overflowing
+tunestr.
+
+Terminate the parsing loop at the first instance itself so that tunestr
+does not overflow.
+
+This also fixes up tst-env-setuid-tunables to actually handle failures
+correct and add new tests to validate the fix for this CVE.
+
+Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+(cherry picked from commit 1056e5b4c3f2d90ed2b4a55f96add28da2f4c8fa)
+---
+ NEWS                          |  5 +++++
+ elf/dl-tunables.c             | 17 +++++++++-------
+ elf/tst-env-setuid-tunables.c | 37 +++++++++++++++++++++++++++--------
+ 3 files changed, 44 insertions(+), 15 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index f1b1b0a3b4..bfcd46efa9 100644
+--- a/NEWS
++++ b/NEWS
+@@ -24,6 +24,11 @@ Security related changes:
+   an application calls getaddrinfo for AF_INET6 with AI_CANONNAME,
+   AI_ALL and AI_V4MAPPED flags set.
++  CVE-2023-4911: If a tunable of the form NAME=NAME=VAL is passed in the
++  environment of a setuid program and NAME is valid, it may result in a
++  buffer overflow, which could be exploited to achieve escalated
++  privileges.  This flaw was introduced in glibc 2.34.
++
+ The following bugs are resolved with this release:
+   [30723] posix_memalign repeatedly scans long bin lists
+diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
+index 62b7332d95..cae67efa0a 100644
+--- a/elf/dl-tunables.c
++++ b/elf/dl-tunables.c
+@@ -180,11 +180,7 @@ parse_tunables (char *tunestr, char *valstring)
+       /* If we reach the end of the string before getting a valid name-value
+        pair, bail out.  */
+       if (p[len] == '\0')
+-      {
+-        if (__libc_enable_secure)
+-          tunestr[off] = '\0';
+-        return;
+-      }
++      break;
+       /* We did not find a valid name-value pair before encountering the
+        colon.  */
+@@ -244,9 +240,16 @@ parse_tunables (char *tunestr, char *valstring)
+           }
+       }
+-      if (p[len] != '\0')
+-      p += len + 1;
++      /* We reached the end while processing the tunable string.  */
++      if (p[len] == '\0')
++      break;
++
++      p += len + 1;
+     }
++
++  /* Terminate tunestr before we leave.  */
++  if (__libc_enable_secure)
++    tunestr[off] = '\0';
+ }
+ /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
+diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
+index 7dfb0e073a..f0b92c97e7 100644
+--- a/elf/tst-env-setuid-tunables.c
++++ b/elf/tst-env-setuid-tunables.c
+@@ -50,6 +50,8 @@ const char *teststrings[] =
+   "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+   "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
+   "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
++  "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
++  "glibc.malloc.check=2",
+   "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
+   "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
+   ":glibc.malloc.garbage=2:glibc.malloc.check=1",
+@@ -68,6 +70,8 @@ const char *resultstrings[] =
+   "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
+   "glibc.malloc.mmap_threshold=4096",
+   "glibc.malloc.mmap_threshold=4096",
++  "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
++  "",
+   "",
+   "",
+   "",
+@@ -81,11 +85,18 @@ test_child (int off)
+ {
+   const char *val = getenv ("GLIBC_TUNABLES");
++  printf ("    [%d] GLIBC_TUNABLES is %s\n", off, val);
++  fflush (stdout);
+   if (val != NULL && strcmp (val, resultstrings[off]) == 0)
+     return 0;
+   if (val != NULL)
+-    printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
++    printf ("    [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n",
++          off, val, resultstrings[off]);
++  else
++    printf ("    [%d] GLIBC_TUNABLES environment variable absent\n", off);
++
++  fflush (stdout);
+   return 1;
+ }
+@@ -106,21 +117,26 @@ do_test (int argc, char **argv)
+       if (ret != 0)
+       exit (1);
+-      exit (EXIT_SUCCESS);
++      /* Special return code to make sure that the child executed all the way
++       through.  */
++      exit (42);
+     }
+   else
+     {
+-      int ret = 0;
+-
+       /* Spawn tests.  */
+       for (int i = 0; i < array_length (teststrings); i++)
+       {
+         char buf[INT_BUFSIZE_BOUND (int)];
+-        printf ("Spawned test for %s (%d)\n", teststrings[i], i);
++        printf ("[%d] Spawned test for %s\n", i, teststrings[i]);
+         snprintf (buf, sizeof (buf), "%d\n", i);
++        fflush (stdout);
+         if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0)
+-          exit (1);
++          {
++            printf ("    [%d] Failed to set GLIBC_TUNABLES: %m", i);
++            support_record_failure ();
++            continue;
++          }
+         int status = support_capture_subprogram_self_sgid (buf);
+@@ -128,9 +144,14 @@ do_test (int argc, char **argv)
+         if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
+           return EXIT_UNSUPPORTED;
+-        ret |= status;
++        if (WEXITSTATUS (status) != 42)
++          {
++            printf ("    [%d] child failed with status %d\n", i,
++                    WEXITSTATUS (status));
++            support_record_failure ();
++          }
+       }
+-      return ret;
++      return 0;
+     }
+ }
+-- 
+2.39.2
+