9p: skip directory entries with names longer than NAME_MAX

The 9p wire format carries directory entry names of up to 65535 bytes
and nothing on the client checks them against NAME_MAX. Since the
previous patch, v9fs_dir_readdir_dotl() passes such names straight to
dir_emit(), and the VFS only rejects names of PATH_MAX bytes or more in
verify_dirent_name(), as -EIO, which again fails the whole getdents64()
call.

A name between NAME_MAX and PATH_MAX is therefore returned to userspace
even though every later operation on it fails with -ENAMETOOLONG, and
POSIX requires readdir() to only return components of at most NAME_MAX
bytes. Nothing can use such an entry, so skip it instead of returning it
or failing the listing, reusing the strlen() result that was already
computed for dir_emit(). Log the skipped entry at P9_DEBUG_ERROR, the
same level as the strscpy() failure message this replaces.

Suggested-by: Dominique Martinet <asmadeus@codewreck.org>
Link: https://lore.kernel.org/all/vz5bum547fqyxf5z4m3x7tuqkuq52jlopm65t7hvynqeulh7i3@2t4wnhfxs7qv/
Signed-off-by: Haobin Wu <853555@gmail.com>
Reviewed-by: Christian Schoenebeck <linux_oss@crudebyte.com>
Message-ID: <20260918024851.51229-3-853555@gmail.com>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
This commit is contained in:
Haobin Wu 2026-09-18 10:48:50 +08:00 committed by Dominique Martinet
parent 8bb0279930
commit edd807dc20

View File

@ -184,6 +184,7 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
}
while (rdir->head < rdir->tail) {
size_t namelen;
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
rdir->tail - rdir->head,
@ -193,10 +194,14 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
return -EIO;
}
if (!dir_emit(ctx, curdirent.d_name,
strlen(curdirent.d_name),
QID2INO(&curdirent.qid),
curdirent.d_type)) {
namelen = strlen(curdirent.d_name);
if (namelen > NAME_MAX) {
p9_debug(P9_DEBUG_ERROR,
"skip dentry: name length %zu > NAME_MAX\n",
namelen);
} else if (!dir_emit(ctx, curdirent.d_name, namelen,
QID2INO(&curdirent.qid),
curdirent.d_type)) {
kfree(curdirent.d_name);
return 0;
}