171 lines
5.3 KiB
Diff
171 lines
5.3 KiB
Diff
commit ce65d944e38a20cb70af2a48a4b8aa5d8fabe1cc
|
|
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
Date: Thu Jan 15 10:32:19 2026 -0300
|
|
|
|
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)
|
|
|
|
Conflicts:
|
|
posix/Makefile
|
|
(Fixup context)
|
|
|
|
diff --git a/posix/Makefile b/posix/Makefile
|
|
index 59cd1dab72acbd54..6dc240a5bf9f3046 100644
|
|
--- a/posix/Makefile
|
|
+++ b/posix/Makefile
|
|
@@ -327,6 +327,7 @@ tests := \
|
|
tst-wait4 \
|
|
tst-waitid \
|
|
tst-wordexp-nocmd \
|
|
+ tst-wordexp-reuse \
|
|
tstgetopt \
|
|
# tests
|
|
|
|
@@ -455,6 +456,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
|
|
@@ -490,6 +493,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
|
|
@@ -773,3 +777,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 0000000000000000..3926b9f5576750ac
|
|
--- /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 a7362ef31b052800..4cd23645198aed30 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)
|