* [PULL 0/3] 9p queue 2026-06-17
@ 2026-06-17 15:39 Christian Schoenebeck
2026-06-17 15:39 ` [PULL 1/3] hw/9pfs: fix abort due to illegal name with Twstat rename Christian Schoenebeck
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Christian Schoenebeck @ 2026-06-17 15:39 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
The following changes since commit b0df6e2f2c6c45df8d4d286933799c623e124d98:
Merge tag 'pull-riscv-to-apply-20260616' of https://github.com/alistair23/qemu into staging (2026-06-16 10:41:47 -0400)
are available in the Git repository at:
https://github.com/cschoenebeck/qemu.git tags/pull-9p-20260617
for you to fetch changes up to 116db2986b11c914217bbd1547815b6c7efb944a:
hw/9pfs: consolidate name validation with check_name() (2026-06-17 17:21:55 +0200)
----------------------------------------------------------------
9pfs changes:
- Fix guest-triggerable assertion fault (DoS) with the legacy Twstat
rename handler.
- Code deduplication.
----------------------------------------------------------------
Christian Schoenebeck (3):
hw/9pfs: fix abort due to illegal name with Twstat rename
hw/9pfs: reject . and .. in Twstat rename
hw/9pfs: consolidate name validation with check_name()
hw/9pfs/9p.c | 97 ++++++++++++++++++++++++++----------------------------------
1 file changed, 42 insertions(+), 55 deletions(-)
^ permalink raw reply [flat|nested] 4+ messages in thread* [PULL 1/3] hw/9pfs: fix abort due to illegal name with Twstat rename 2026-06-17 15:39 [PULL 0/3] 9p queue 2026-06-17 Christian Schoenebeck @ 2026-06-17 15:39 ` Christian Schoenebeck 2026-06-17 15:39 ` [PULL 3/3] hw/9pfs: consolidate name validation with check_name() Christian Schoenebeck 2026-06-17 15:39 ` [PULL 2/3] hw/9pfs: reject . and .. in Twstat rename Christian Schoenebeck 2 siblings, 0 replies; 4+ messages in thread From: Christian Schoenebeck @ 2026-06-17 15:39 UTC (permalink / raw) To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian The legacy Twstat 9p request can be used to rename files and directories. Unlike the other, more recent rename requests like Trename and Trenameat, Twstat does not validate the submitted new name before passing it to v9fs_complete_rename(). A priviliged guest user with direct communication access to 9p server could pass a string containing '/' as new name, which causes an assertion fault (DoS) in local_name_to_path(). Fix this by rejecting such strings by checking the client supplied new name with name_is_illegal(), similar to how Trename and Trenameat handlers do already. Reported-by: Feifan Qian <bea1e@proton.me> Fixes: 8cf89e007a ("virtio-9p: Add P9_TWSTAT support") Link: https://lore.kernel.org/qemu-devel/ba09716828e82992f9d8cac7f00eee0bc1c43c61.1780072238.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index b4314d2549..f84698bfcc 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3638,6 +3638,11 @@ static void coroutine_fn v9fs_wstat(void *opaque) err = -EOPNOTSUPP; goto out; } + if (name_is_illegal(v9stat.name.data)) { + err = -ENOENT; + goto out; + } + v9fs_path_write_lock(s); err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); v9fs_path_unlock(s); -- 2.47.3 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PULL 3/3] hw/9pfs: consolidate name validation with check_name() 2026-06-17 15:39 [PULL 0/3] 9p queue 2026-06-17 Christian Schoenebeck 2026-06-17 15:39 ` [PULL 1/3] hw/9pfs: fix abort due to illegal name with Twstat rename Christian Schoenebeck @ 2026-06-17 15:39 ` Christian Schoenebeck 2026-06-17 15:39 ` [PULL 2/3] hw/9pfs: reject . and .. in Twstat rename Christian Schoenebeck 2 siblings, 0 replies; 4+ messages in thread From: Christian Schoenebeck @ 2026-06-17 15:39 UTC (permalink / raw) To: qemu-devel; +Cc: Greg Kurz, Peter Maydell Add a new, shared helper function check_name() that consolidates the name validation logic (illegal name check and "." / ".." rejection) currently spread over multiple 9p handlers, unnecessarily duplicating code. This is pure refactoring with no behavior change. The existing error code semantics are preserved: rename operations return -EISDIR, create operations return -EEXIST. Note: These current error codes actually differ from native Linux system calls (e.g. rename() returns -EBUSY, open(O_CREAT) returns -EISDIR). The 9P protocol does not mandate specific error codes for these validation errors. Hence consolidating to a single error code (e.g., -EINVAL) for all cases could be considered in the future for simplicity reason. This change reduces code duplication across 9 functions: - v9fs_lcreate - v9fs_create - v9fs_symlink - v9fs_link - v9fs_rename - v9fs_renameat - v9fs_wstat - v9fs_mknod - v9fs_mkdir Link: https://lore.kernel.org/qemu-devel/0573103880129eb543f07b68c77e86f2f572f6bf.1780072238.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 100 ++++++++++++++++++++------------------------------- 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 936e4e9349..b486cce48a 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1823,6 +1823,25 @@ static bool name_is_illegal(const char *name) return !*name || strchr(name, '/') != NULL; } +static int check_name(const char *name, V9fsPDU *pdu) +{ + int request_type = pdu->id; + + if (name_is_illegal(name)) { + return -ENOENT; + } + if (!strcmp(name, ".") || !strcmp(name, "..")) { + /* + * TODO: The different error codes here are just there to preserve + * pre-existing behaviour of 9p server. In future it might make sense to + * consolidate this and e.g. just return -EINVAL for everyone. + */ + return (request_type == P9_TRENAME || request_type == P9_TRENAMEAT || + request_type == P9_TWSTAT) ? -EISDIR : -EEXIST; + } + return 0; +} + static bool same_stat_id(const struct stat *a, const struct stat *b) { return a->st_dev == b->st_dev && a->st_ino == b->st_ino; @@ -2173,13 +2192,8 @@ static void coroutine_fn v9fs_lcreate(void *opaque) } trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -2861,13 +2875,8 @@ static void coroutine_fn v9fs_create(void *opaque) } trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -3055,13 +3064,8 @@ static void coroutine_fn v9fs_symlink(void *opaque) } trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -3148,13 +3152,8 @@ static void coroutine_fn v9fs_link(void *opaque) } trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -3385,13 +3384,8 @@ static void coroutine_fn v9fs_rename(void *opaque) goto out_nofid; } - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EISDIR; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -3526,14 +3520,12 @@ static void coroutine_fn v9fs_renameat(void *opaque) goto out_err; } - if (name_is_illegal(old_name.data) || name_is_illegal(new_name.data)) { - err = -ENOENT; + err = check_name(old_name.data, pdu); + if (err < 0) { goto out_err; } - - if (!strcmp(".", old_name.data) || !strcmp("..", old_name.data) || - !strcmp(".", new_name.data) || !strcmp("..", new_name.data)) { - err = -EISDIR; + err = check_name(new_name.data, pdu); + if (err < 0) { goto out_err; } @@ -3638,12 +3630,8 @@ static void coroutine_fn v9fs_wstat(void *opaque) err = -EOPNOTSUPP; goto out; } - if (name_is_illegal(v9stat.name.data)) { - err = -ENOENT; - goto out; - } - if (!strcmp(".", v9stat.name.data) || !strcmp("..", v9stat.name.data)) { - err = -EISDIR; + err = check_name(v9stat.name.data, pdu); + if (err < 0) { goto out; } @@ -3776,13 +3764,8 @@ static void coroutine_fn v9fs_mknod(void *opaque) } trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } @@ -3938,13 +3921,8 @@ static void coroutine_fn v9fs_mkdir(void *opaque) } trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); - if (name_is_illegal(name.data)) { - err = -ENOENT; - goto out_nofid; - } - - if (!strcmp(".", name.data) || !strcmp("..", name.data)) { - err = -EEXIST; + err = check_name(name.data, pdu); + if (err < 0) { goto out_nofid; } -- 2.47.3 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PULL 2/3] hw/9pfs: reject . and .. in Twstat rename 2026-06-17 15:39 [PULL 0/3] 9p queue 2026-06-17 Christian Schoenebeck 2026-06-17 15:39 ` [PULL 1/3] hw/9pfs: fix abort due to illegal name with Twstat rename Christian Schoenebeck 2026-06-17 15:39 ` [PULL 3/3] hw/9pfs: consolidate name validation with check_name() Christian Schoenebeck @ 2026-06-17 15:39 ` Christian Schoenebeck 2 siblings, 0 replies; 4+ messages in thread From: Christian Schoenebeck @ 2026-06-17 15:39 UTC (permalink / raw) To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell The other Trename and Trenameat handlers already reject "." and ".." as new name on rename requests by returning -EISDIR in this case. The legacy Twstat rename handler is missing this validation. While passing "." or ".." does not trigger a crash as fixed by the previous patch (since the fs backend driver's system calls handle these gracefully), it creates a behavioral inconsistency, as it is semantically meaningless to rename a file to a directory reference in the first place. Fix this by rejecting "." and ".." in Twstat rename handler with -EISDIR to match behavior of Trename and Trenameat handlers. Fixes: 8cf89e007a ("virtio-9p: Add P9_TWSTAT support") Link: https://lore.kernel.org/qemu-devel/662333331d371c6c343c8091161de8eaa121880e.1780072238.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index f84698bfcc..936e4e9349 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3642,6 +3642,10 @@ static void coroutine_fn v9fs_wstat(void *opaque) err = -ENOENT; goto out; } + if (!strcmp(".", v9stat.name.data) || !strcmp("..", v9stat.name.data)) { + err = -EISDIR; + goto out; + } v9fs_path_write_lock(s); err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); -- 2.47.3 ^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-17 15:44 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-17 15:39 [PULL 0/3] 9p queue 2026-06-17 Christian Schoenebeck 2026-06-17 15:39 ` [PULL 1/3] hw/9pfs: fix abort due to illegal name with Twstat rename Christian Schoenebeck 2026-06-17 15:39 ` [PULL 3/3] hw/9pfs: consolidate name validation with check_name() Christian Schoenebeck 2026-06-17 15:39 ` [PULL 2/3] hw/9pfs: reject . and .. in Twstat rename Christian Schoenebeck
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.