mirror of git://sourceware.org/git/glibc.git
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 80cc58ea2d)
This commit is contained in:
parent
49125ffc8e
commit
d5409a1be0
6
NEWS
6
NEWS
|
|
@ -46,6 +46,10 @@ Security related changes:
|
||||||
information, which may lead to a buffer overflow if the message string
|
information, which may lead to a buffer overflow if the message string
|
||||||
size aligns to page size.
|
size aligns to page size.
|
||||||
|
|
||||||
|
GLIBC-SA-2026-0003
|
||||||
|
wordexp with WRDE_REUSE and WRDE_APPEND may return uninitialized
|
||||||
|
memory (CVE-2025-15281)
|
||||||
|
|
||||||
The following bugs are resolved with this release:
|
The following bugs are resolved with this release:
|
||||||
|
|
||||||
[27821] ungetc: Fix backup buffer leak on program exit
|
[27821] ungetc: Fix backup buffer leak on program exit
|
||||||
|
|
@ -77,6 +81,8 @@ The following bugs are resolved with this release:
|
||||||
[32582] Fix underallocation of abort_msg_s struct (CVE-2025-0395)
|
[32582] Fix underallocation of abort_msg_s struct (CVE-2025-0395)
|
||||||
[32987] elf: Fix subprocess status handling for tst-dlopen-sgid
|
[32987] elf: Fix subprocess status handling for tst-dlopen-sgid
|
||||||
[33185] Fix double-free after allocation failure in regcomp
|
[33185] Fix double-free after allocation failure in regcomp
|
||||||
|
[33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return
|
||||||
|
uninitialized memory
|
||||||
|
|
||||||
Version 2.38
|
Version 2.38
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,7 @@ tests := \
|
||||||
tst-wait4 \
|
tst-wait4 \
|
||||||
tst-waitid \
|
tst-waitid \
|
||||||
tst-wordexp-nocmd \
|
tst-wordexp-nocmd \
|
||||||
|
tst-wordexp-reuse \
|
||||||
tstgetopt \
|
tstgetopt \
|
||||||
# tests
|
# tests
|
||||||
|
|
||||||
|
|
@ -453,6 +454,8 @@ generated += \
|
||||||
tst-rxspencer-no-utf8.mtrace \
|
tst-rxspencer-no-utf8.mtrace \
|
||||||
tst-vfork3-mem.out \
|
tst-vfork3-mem.out \
|
||||||
tst-vfork3.mtrace \
|
tst-vfork3.mtrace \
|
||||||
|
tst-wordexp-reuse-mem.out \
|
||||||
|
tst-wordexp-reuse.mtrace \
|
||||||
wordexp-tst.out \
|
wordexp-tst.out \
|
||||||
# generated
|
# generated
|
||||||
|
|
||||||
|
|
@ -484,6 +487,7 @@ tests-special += \
|
||||||
$(objpfx)tst-pcre-mem.out \
|
$(objpfx)tst-pcre-mem.out \
|
||||||
$(objpfx)tst-rxspencer-no-utf8-mem.out \
|
$(objpfx)tst-rxspencer-no-utf8-mem.out \
|
||||||
$(objpfx)tst-vfork3-mem.out \
|
$(objpfx)tst-vfork3-mem.out \
|
||||||
|
$(objpfx)tst-wordexp-reuse.out \
|
||||||
# tests-special
|
# tests-special
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -765,3 +769,10 @@ $(objpfx)posix-conf-vars-def.h: $(..)scripts/gen-posix-conf-vars.awk \
|
||||||
$(make-target-directory)
|
$(make-target-directory)
|
||||||
$(AWK) -f $(filter-out Makefile, $^) > $@.tmp
|
$(AWK) -f $(filter-out Makefile, $^) > $@.tmp
|
||||||
mv -f $@.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)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -2216,7 +2216,9 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||||
{
|
{
|
||||||
/* Minimal implementation of WRDE_REUSE for now */
|
/* Minimal implementation of WRDE_REUSE for now */
|
||||||
wordfree (pwordexp);
|
wordfree (pwordexp);
|
||||||
|
old_word.we_wordc = 0;
|
||||||
old_word.we_wordv = NULL;
|
old_word.we_wordv = NULL;
|
||||||
|
pwordexp->we_wordc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & WRDE_APPEND) == 0)
|
if ((flags & WRDE_APPEND) == 0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue