]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
glibc: Import fix for CVE-2025-15281
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 20 Jan 2026 16:13:52 +0000 (16:13 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 20 Jan 2026 16:13:52 +0000 (16:13 +0000)
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 <michael.tremer@ipfire.org>
lfs/glibc
src/patches/glibc-2.42-CVE-2025-15281.patch [new file with mode: 0644]

index 32dcdb3860ec2a509abad26cf8449ec5da951cd5..49cce7282ed0e455dee306acc63cbd3fd27be020 100644 (file)
--- 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 (file)
index 0000000..89322f8
--- /dev/null
@@ -0,0 +1,175 @@
+From cbf39c26b25801e9bc88499b4fd361ac172d4125 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+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 <carlos@redhat.com>
+(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
++   <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 --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
+