Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2026-03-06
Initial Package Version: 2.42
Upstream Status:         Applied
Origin:                  Upstream (GLIBC-SA-2026-0001 through 0003)
Description:             Fixes thee security vulnerabilities in glibc for 2.42.
                         This was created by recommendation from Pierre, who
                         brought up to me during package freeze that users were
                         running into build issues when updating 12.4 systems
                         to glibc-2.43 following BLFS SA-12.4-079. Users should
                         be building *LFS 13.0 now, however we did drop the SysV
                         version recently, which means some users are likely to
                         stay on 12.4 until they move to systemd or until Randy's
                         branch is available. To keep them safe, I've developed
                         this patch that backports the fixes for CVE-2026-0861,
                         CVE-2026-0915, and CVE-2025-15281. These issues allow
                         for crashes, stack content leaks, and heap corruption
                         due to an integer overflow in the memalign function.
                         Users who are on 12.4 still should use this patch
                         instead.

diff -Naurp glibc-2.42.orig/malloc/malloc.c glibc-2.42/malloc/malloc.c
--- glibc-2.42.orig/malloc/malloc.c	2026-03-06 10:12:45.186777081 -0600
+++ glibc-2.42/malloc/malloc.c	2026-03-06 10:14:37.826294149 -0600
@@ -5152,7 +5152,7 @@ _int_memalign (mstate av, size_t alignme
   INTERNAL_SIZE_T size;
 
   nb = checked_request2size (bytes);
-  if (nb == 0)
+  if (nb == 0 || alignment > PTRDIFF_MAX)
     {
       __set_errno (ENOMEM);
       return NULL;
@@ -5168,7 +5168,10 @@ _int_memalign (mstate av, size_t alignme
      we don't find anything in those bins, the common malloc code will
      scan starting at 2x.  */
 
-  /* Call malloc with worst case padding to hit alignment. */
+  /* Call malloc with worst case padding to hit alignment.  ALIGNMENT is a
+     power of 2, so it tops out at (PTRDIFF_MAX >> 1) + 1, leaving plenty of
+     space to add MINSIZE and whatever checked_request2size adds to BYTES to
+     get NB.  Consequently, total below also does not overflow.  */
   m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
 
   if (m == NULL)
diff -Naurp glibc-2.42.orig/malloc/tst-malloc-too-large.c glibc-2.42/malloc/tst-malloc-too-large.c
--- glibc-2.42.orig/malloc/tst-malloc-too-large.c	2026-03-06 10:12:45.187777077 -0600
+++ glibc-2.42/malloc/tst-malloc-too-large.c	2026-03-06 10:14:37.826294149 -0600
@@ -152,7 +152,6 @@ test_large_allocations (size_t size)
 }
 
 
-static long pagesize;
 
 /* This function tests the following aligned memory allocation functions
    using several valid alignments and precedes each allocation test with a
@@ -171,8 +170,8 @@ test_large_aligned_allocations (size_t s
 
   /* All aligned memory allocation functions expect an alignment that is a
      power of 2.  Given this, we test each of them with every valid
-     alignment from 1 thru PAGESIZE.  */
-  for (align = 1; align <= pagesize; align *= 2)
+     alignment for the type of ALIGN, i.e. until it wraps to 0.  */
+  for (align = 1; align > 0; align <<= 1)
     {
       test_setup ();
 #if __GNUC_PREREQ (7, 0)
@@ -265,11 +264,6 @@ do_test (void)
   DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
 #endif
 
-  /* Aligned memory allocation functions need to be tested up to alignment
-     size equivalent to page size, which should be a power of 2.  */
-  pagesize = sysconf (_SC_PAGESIZE);
-  TEST_VERIFY_EXIT (powerof2 (pagesize));
-
   /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
      in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
 
diff -Naurp glibc-2.42.orig/posix/Makefile glibc-2.42/posix/Makefile
--- glibc-2.42.orig/posix/Makefile	2026-03-06 10:12:45.323776495 -0600
+++ glibc-2.42/posix/Makefile	2026-03-06 10:15:41.211021868 -0600
@@ -327,6 +327,7 @@ tests := \
   tst-wait4 \
   tst-waitid \
   tst-wordexp-nocmd \
+  tst-wordexp-reuse \
   tstgetopt \
   # tests
 
@@ -457,6 +458,8 @@ generated += \
   tst-rxspencer-no-utf8.mtrace \
   tst-vfork3-mem.out \
   tst-vfork3.mtrace \
+  tst-wordexp-reuse-mem.out \
+  tst-wordexp-reuse.mtrace \
   # generated
 endif
 endif
@@ -492,6 +495,7 @@ tests-special += \
   $(objpfx)tst-pcre-mem.out \
   $(objpfx)tst-rxspencer-no-utf8-mem.out \
   $(objpfx)tst-vfork3-mem.out \
+  $(objpfx)tst-wordexp-reuse.out \
   # tests-special
 endif
 endif
@@ -775,3 +779,10 @@ $(objpfx)posix-conf-vars-def.h: $(..)scr
 	$(make-target-directory)
 	$(AWK) -f $(filter-out Makefile, $^) > $@.tmp
 	mv -f $@.tmp $@
+
+tst-wordexp-reuse-ENV += MALLOC_TRACE=$(objpfx)tst-wordexp-reuse.mtrace \
+			 LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+
+$(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out
+	$(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \
+	$(evaluate-test)
diff -Naurp glibc-2.42.orig/posix/tst-wordexp-reuse.c glibc-2.42/posix/tst-wordexp-reuse.c
--- glibc-2.42.orig/posix/tst-wordexp-reuse.c	1969-12-31 18:00:00.000000000 -0600
+++ glibc-2.42/posix/tst-wordexp-reuse.c	2026-03-06 10:15:41.211021868 -0600
@@ -0,0 +1,89 @@
+/* Test for wordexp with WRDE_REUSE flag.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <wordexp.h>
+#include <mcheck.h>
+
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  mtrace ();
+
+  {
+    wordexp_t p = { 0 };
+    TEST_COMPARE (wordexp ("one", &p, 0), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[0], "one");
+    TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[0], "two");
+    wordfree (&p);
+  }
+
+  {
+    wordexp_t p = { .we_offs = 2 };
+    TEST_COMPARE (wordexp ("one", &p, 0), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[0], "one");
+    TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_DOOFFS), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
+    wordfree (&p);
+  }
+
+  {
+    wordexp_t p = { 0 };
+    TEST_COMPARE (wordexp ("one", &p, 0), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[0], "one");
+    TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_APPEND), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[0], "two");
+    wordfree (&p);
+  }
+
+  {
+    wordexp_t p = { .we_offs = 2 };
+    TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
+    TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
+				      | WRDE_DOOFFS), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
+    wordfree (&p);
+  }
+
+  {
+    wordexp_t p = { .we_offs = 2 };
+    TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
+    TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
+				      | WRDE_DOOFFS | WRDE_APPEND), 0);
+    TEST_COMPARE (p.we_wordc, 1);
+    TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
+    wordfree (&p);
+  }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff -Naurp glibc-2.42.orig/posix/wordexp.c glibc-2.42/posix/wordexp.c
--- glibc-2.42.orig/posix/wordexp.c	2026-03-06 10:12:45.333776452 -0600
+++ glibc-2.42/posix/wordexp.c	2026-03-06 10:15:41.211021868 -0600
@@ -2216,7 +2216,9 @@ wordexp (const char *words, wordexp_t *p
     {
       /* Minimal implementation of WRDE_REUSE for now */
       wordfree (pwordexp);
+      old_word.we_wordc = 0;
       old_word.we_wordv = NULL;
+      pwordexp->we_wordc = 0;
     }
 
   if ((flags & WRDE_APPEND) == 0)
diff -Naurp glibc-2.42.orig/resolv/nss_dns/dns-network.c glibc-2.42/resolv/nss_dns/dns-network.c
--- glibc-2.42.orig/resolv/nss_dns/dns-network.c	2026-03-06 10:12:45.347776392 -0600
+++ glibc-2.42/resolv/nss_dns/dns-network.c	2026-03-06 10:15:07.912164952 -0600
@@ -207,6 +207,10 @@ _nss_dns_getnetbyaddr_r (uint32_t net, i
       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
 	       net_bytes[1], net_bytes[0]);
       break;
+    default:
+      /* Default network (net is originally zero).  */
+      strcpy (qbuf, "0.0.0.0.in-addr.arpa");
+      break;
     }
 
   net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
diff -Naurp glibc-2.42.orig/resolv/tst-resolv-network.c glibc-2.42/resolv/tst-resolv-network.c
--- glibc-2.42.orig/resolv/tst-resolv-network.c	2026-03-06 10:12:45.349776383 -0600
+++ glibc-2.42/resolv/tst-resolv-network.c	2026-03-06 10:15:07.912164952 -0600
@@ -46,6 +46,9 @@ handle_code (const struct resolv_respons
 {
   switch (code)
     {
+    case 0:
+      send_ptr (b, qname, qclass, qtype, "0.in-addr.arpa");
+      break;
     case 1:
       send_ptr (b, qname, qclass, qtype, "1.in-addr.arpa");
       break;
@@ -265,6 +268,9 @@ do_test (void)
                 "error: TRY_AGAIN\n");
 
   /* Lookup by address, success cases.  */
+  check_reverse (0,
+                 "name: 0.in-addr.arpa\n"
+                 "net: 0x00000000\n");
   check_reverse (1,
                  "name: 1.in-addr.arpa\n"
                  "net: 0x00000001\n");
