mirror of
https://sourceware.org/git/glibc.git
synced 2026-10-28 10:25:13 +08:00
io: Reject fts_open root arguments longer than USHRT_MAX (BZ 34782)
FTSENT::fts_namelen and fts_pathlen are unsigned short in glibc, but fts_alloc stores the length of a root argument without checking it. With an argument longer than USHRT_MAX both lengths wrap around, and the fts_path of the root entry is truncated and may not be NUL-terminated. glibc 2.43 failed with ENAMETOOLONG instead. Make fts_alloc fail with ENAMETOOLONG if the name does not fit, so fts_open fails as before. Checked on x86_64-linux-gnu and i686-linux-gnu. Reviewed-by: Collin Funk <collin.funk1@gmail.com>
This commit is contained in:
1 parent
a8c5a1366f
commit
6b0ceace67
3 files changed
+88
-1
No files matched your search
@@ -199,6 +199,7 @@ tests := \
|
||||
tst-fts \
|
||||
tst-fts-bz22944 \
|
||||
tst-fts-bz34782 \
|
||||
tst-fts-bz34782-root \
|
||||
tst-fts-cwdfd-follow \
|
||||
tst-fts-follow \
|
||||
tst-fts-lfs \
|
||||
|
||||
+7
-1
@@ -2024,8 +2024,14 @@ fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)
|
||||
#else
|
||||
/*
|
||||
* For glibc, we use a wrapper struct to provide the extra required
|
||||
* fields without changing the FSENT layout.
|
||||
* fields without changing the FSENT layout. That layout also keeps
|
||||
* fts_namelen and fts_pathlen as unsigned short, so reject a name
|
||||
* that does not fit (only fts_open root arguments can be that long).
|
||||
*/
|
||||
if (namelen > USHRT_MAX) {
|
||||
__set_errno (ENAMETOOLONG);
|
||||
return (NULL);
|
||||
}
|
||||
len += sizeof (struct FTSENT_wrapper);
|
||||
struct FTSENT_wrapper *wrapper = malloc(len);
|
||||
if (wrapper == NULL)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Check fts_open with root arguments around USHRT_MAX bytes (BZ 34782).
|
||||
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 <errno.h>
|
||||
#include <fts.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* FTSENT::fts_namelen and FTSENT::fts_pathlen are unsigned short, so
|
||||
fts_open must reject a root argument longer than USHRT_MAX instead of
|
||||
truncating its length. */
|
||||
static void
|
||||
check_root (size_t len)
|
||||
{
|
||||
char *root = xmalloc (len + 1);
|
||||
memset (root, 'a', len);
|
||||
root[len] = '\0';
|
||||
char *paths[] = { root, NULL };
|
||||
|
||||
errno = 0;
|
||||
FTS *ftsp = fts_open (paths, FTS_PHYSICAL, NULL);
|
||||
if (len > USHRT_MAX)
|
||||
{
|
||||
if (ftsp != NULL)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("error: fts_open accepted a %zu byte root\n", len);
|
||||
fts_close (ftsp);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (errno, ENAMETOOLONG);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ftsp != NULL);
|
||||
FTSENT *ent = fts_read (ftsp);
|
||||
TEST_VERIFY_EXIT (ent != NULL);
|
||||
/* The name does not exist (and is longer than PATH_MAX on Linux),
|
||||
but the entry must still describe it correctly. */
|
||||
TEST_COMPARE (ent->fts_info, FTS_NS);
|
||||
TEST_COMPARE (ent->fts_namelen, len);
|
||||
TEST_COMPARE (ent->fts_pathlen, len);
|
||||
TEST_COMPARE (strnlen (ent->fts_path, len + 1), len);
|
||||
TEST_VERIFY (memcmp (ent->fts_path, root, len) == 0);
|
||||
TEST_COMPARE (fts_close (ftsp), 0);
|
||||
}
|
||||
|
||||
free (root);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
check_root (USHRT_MAX);
|
||||
check_root (USHRT_MAX + 1);
|
||||
check_root (USHRT_MAX + 2);
|
||||
check_root (2 * (USHRT_MAX + 1) + 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
Reference in new issue
Block a user