diff --git a/io/Makefile b/io/Makefile
index 427a14bec7..f80e8b8358 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -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 \
diff --git a/io/fts-common.c b/io/fts-common.c
index d3219071d9..b3a75d2b90 100644
--- a/io/fts-common.c
+++ b/io/fts-common.c
@@ -2019,8 +2019,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)
diff --git a/io/tst-fts-bz34782-root.c b/io/tst-fts-bz34782-root.c
new file mode 100644
index 0000000000..5df4b09bb5
--- /dev/null
+++ b/io/tst-fts-bz34782-root.c
@@ -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
+ . */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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