Submitted By: Douglas R. Reno 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 + . */ + +#include +#include + +#include + +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 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");