From: Michael Tremer Date: Tue, 20 Jan 2026 16:13:52 +0000 (+0000) Subject: glibc: Import fix for CVE-2025-15281 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=795b3317746dbc1941436f136c8c7ba136498fe9;p=ipfire-2.x.git glibc: Import fix for CVE-2025-15281 GLIBC-SA-2026-0003: =================== wordexp with WRDE_REUSE and WRDE_APPEND may return uninitialized memory Calling wordexp with WRDE_REUSE in conjunction with WRDE_APPEND in the GNU C Library version 2.0 to version 2.42 may cause the interface to return uninitialized memory in the we_wordv member, which on subsequent calls to wordfree may abort the process. The implementation of WRDE_REUSE in conjunction with WRDE_APPEND fails to clear the we_wordc member of the structure, and as such, when new words are added internally, a leading we_wordc count number of entries are skipped since they are assumed initialized. These skipped entries are not initialized, but are the contents of a realloc-expanded array of pointers. If the caller inspects the we_wordv array, it will dereference invalid pointers and crash. If the caller calls wordfree, the malloc implementation may detect the invalid pointers and abort the process. Calls to wordexp using WRDE_REUSE and WRDE_APPEND have never worked correctly and thus the existence of applications that make use of this feature is unlikely. CVE-Id: CVE-2025-15281 Public-Date: 2026-01-20 Vulnerable-Commit: 8f2ece695d8822e9ecc63ecd157e90bf17a6fe65 (1.93-260) Fix-Commit: 80cc58ea2de214f85b0a1d902a3b668ad2ecb302 (2.43) Reported-by: Vitaly Simonovich Signed-off-by: Michael Tremer --- diff --git a/lfs/glibc b/lfs/glibc index 32dcdb386..49cce7282 100644 --- a/lfs/glibc +++ b/lfs/glibc @@ -122,6 +122,7 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-localedef-no-archive.patch cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.42-CVE-2026-0861.patch cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.42-CVE-2026-0915.patch + cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-2.42-CVE-2025-15281.patch ifneq "$(TOOLCHAIN)" "1" ifeq "$(BUILD_ARCH)" "x86_64" diff --git a/src/patches/glibc-2.42-CVE-2025-15281.patch b/src/patches/glibc-2.42-CVE-2025-15281.patch new file mode 100644 index 000000000..89322f80d --- /dev/null +++ b/src/patches/glibc-2.42-CVE-2025-15281.patch @@ -0,0 +1,175 @@ +From cbf39c26b25801e9bc88499b4fd361ac172d4125 Mon Sep 17 00:00:00 2001 +From: Adhemerval Zanella +Date: Thu, 15 Jan 2026 10:32:19 -0300 +Subject: [PATCH] posix: Reset wordexp_t fields with WRDE_REUSE (CVE-2025-15281 + / BZ 33814) + +The wordexp fails to properly initialize the input wordexp_t when +WRDE_REUSE is used. The wordexp_t struct is properly freed, but +reuses the old wc_wordc value and updates the we_wordv in the +wrong position. A later wordfree will then call free with an +invalid pointer. + +Checked on x86_64-linux-gnu and i686-linux-gnu. + +Reviewed-by: Carlos O'Donell +(cherry picked from commit 80cc58ea2de214f85b0a1d902a3b668ad2ecb302) +--- + posix/Makefile | 11 +++++ + posix/tst-wordexp-reuse.c | 89 +++++++++++++++++++++++++++++++++++++++ + posix/wordexp.c | 2 + + 3 files changed, 102 insertions(+) + create mode 100644 posix/tst-wordexp-reuse.c + +diff --git a/posix/Makefile b/posix/Makefile +index a36e5decd3..1ea86efcc1 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -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: $(..)scripts/gen-posix-conf-vars.awk \ + $(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 --git a/posix/tst-wordexp-reuse.c b/posix/tst-wordexp-reuse.c +new file mode 100644 +index 0000000000..3926b9f557 +--- /dev/null ++++ b/posix/tst-wordexp-reuse.c +@@ -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 --git a/posix/wordexp.c b/posix/wordexp.c +index a69b732801..9df4bb7424 100644 +--- a/posix/wordexp.c ++++ b/posix/wordexp.c +@@ -2216,7 +2216,9 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags) + { + /* 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) +-- +2.43.7 +