mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
9p: skip intermediate directory entry name copy in p9dirent_read()
v9fs_dir_readdir_dotl() decodes each entry of a Rreaddir reply with
p9dirent_read(), which parses it through p9pdu_readf("Qqbs"). The 's'
conversion in p9pdu_vreadf() already allocates a NUL-terminated copy
of the name from the wire buffer; p9dirent_read() then strscpy()s that
copy into the fixed 256-byte p9_dirent::d_name and frees the original.
The wire format carries the name length in 16 bits, so a name longer
than 255 bytes is valid on the wire and may well be valid on the
server's filesystem, but it makes strscpy() return -E2BIG.
v9fs_dir_readdir_dotl() turns that into -EIO and aborts getdents64(),
so every entry after the long one disappears from the listing.
Drop the second copy: keep the string allocated by p9pdu_vreadf() in
p9_dirent and let v9fs_dir_readdir_dotl(), its only user, free it once
dir_emit() has consumed it. p9_dirent is a short-lived stack object, so
the string's lifetime does not change.
Note that the VFS only rejects names of PATH_MAX bytes or more in
verify_dirent_name(), so after this change a name between NAME_MAX and
PATH_MAX is returned by getdents64() even though any later lookup on it
fails with -ENAMETOOLONG. The next patch skips such entries.
Fixes: 7751bdb3a0 ("9p: readdir implementation for 9p2000.L")
Closes: https://github.com/microsoft/WSL/issues/41192
Signed-off-by: Haobin Wu <853555@gmail.com>
Reviewed-by: Christian Schoenebeck <linux_oss@crudebyte.com>
Message-ID: <20260918024851.51229-2-853555@gmail.com>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
This commit is contained in:
parent
a869fe526c
commit
8bb0279930
|
|
@ -196,8 +196,12 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
|
|||
if (!dir_emit(ctx, curdirent.d_name,
|
||||
strlen(curdirent.d_name),
|
||||
QID2INO(&curdirent.qid),
|
||||
curdirent.d_type))
|
||||
curdirent.d_type)) {
|
||||
kfree(curdirent.d_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kfree(curdirent.d_name);
|
||||
|
||||
ctx->pos = curdirent.d_off;
|
||||
rdir->head += err;
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ struct p9_dirent {
|
|||
struct p9_qid qid;
|
||||
u64 d_off;
|
||||
unsigned char d_type;
|
||||
char d_name[256];
|
||||
char *d_name;
|
||||
};
|
||||
|
||||
struct iov_iter;
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
|||
{
|
||||
struct p9_fcall fake_pdu;
|
||||
int ret;
|
||||
char *nameptr;
|
||||
char *nameptr = NULL;
|
||||
|
||||
fake_pdu.size = len;
|
||||
fake_pdu.capacity = len;
|
||||
|
|
@ -785,15 +785,7 @@ int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = strscpy(dirent->d_name, nameptr, sizeof(dirent->d_name));
|
||||
if (ret < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"On the wire dirent name too long: %s\n",
|
||||
nameptr);
|
||||
kfree(nameptr);
|
||||
return ret;
|
||||
}
|
||||
kfree(nameptr);
|
||||
dirent->d_name = nameptr;
|
||||
|
||||
return fake_pdu.offset;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user