Commit fe9e30899f for qemu.org

commit fe9e30899ffceef25cf345ec83f76546afca19dd
Author: Christian Schoenebeck <qemu_oss@crudebyte.com>
Date:   Mon Sep 21 14:18:58 2026 +0200

    hw/9pfs: mutate FID path from main thread only (CVE-2026-93834)

    v9fs_co_open2() is the only code that mutates a FID path from a worker
    thread: inside its v9fs_co_run_in_worker() block it frees fidp->path
    and copies in the new path under the held FID path write lock.

    Every other FID path mutation in the 9p codebase happens on the main
    thread, so main thread readers (v9fs_walk(), v9fs_xattrwalk(),
    v9fs_stat(), v9fs_co_name_to_path()) rely on the same (main) thread
    atomicity and don't take the path read lock themselves on main thread.

    Fix this by making the worker thread block in v9fs_co_open2() read-only
    with respect to the FID: render the new path into the local 'path'
    variable only and copy it to fidp->path after the worker block returned
    back to the main thread and still under the held write lock of the FID,
    like every other FID path mutation does.

    Fixes: 02cb7f3a25 ("hw/9pfs: Use read-write lock for protecting fid path.")
    Fixes: CVE-2026-93834
    Reported-by: Milad Nasr <https://gitlab.com/sirkhezr>
    Suggested-by: Milad Nasr <https://gitlab.com/sirkhezr>
    Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4491
    Reviewed-by: Greg Kurz <groug@kaod.org>
    Link: https://lore.kernel.org/qemu-devel/E1x8d3N-003M3F-Pz@kylie.crudebyte.com
    Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>

diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 6e775c8e41..27fe5bfb20 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -144,10 +144,11 @@ int coroutine_fn v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp,
     cred.fc_mode = mode & 07777;
     cred.fc_uid = fidp->uid;
     cred.fc_gid = gid;
+    v9fs_path_init(&path);
     /*
      * Hold the directory fid lock so that directory path name
-     * don't change. Take the write lock to be sure this fid
-     * cannot be used by another operation.
+     * don't change. Take the write lock since the fid path is
+     * mutated below on success.
      */
     v9fs_path_write_lock(s);
     v9fs_co_run_in_worker(
@@ -157,23 +158,30 @@ int coroutine_fn v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp,
             if (err < 0) {
                 err = -errno;
             } else {
-                v9fs_path_init(&path);
                 err = v9fs_name_to_path(s, &fidp->path, name->data, &path);
                 if (!err) {
                     err = s->ops->lstat(&s->ctx, &path, stbuf);
                     if (err < 0) {
                         err = -errno;
                         s->ops->close(&s->ctx, &fidp->fs);
-                    } else {
-                        v9fs_path_copy(&fidp->path, &path);
                     }
                 } else {
                     s->ops->close(&s->ctx, &fidp->fs);
                 }
-                v9fs_path_free(&path);
             }
         });
+    /*
+     * The fid path must not be mutated from the worker thread: other
+     * requests may access the same fid on the main thread, and the main
+     * thread never takes the path lock for reads. Mutate the new path
+     * here, on the main thread and still under the held write lock, like
+     * every other mutation of a fid path.
+     */
+    if (!err) {
+        v9fs_path_copy(&fidp->path, &path);
+    }
     v9fs_path_unlock(s);
+    v9fs_path_free(&path);
     if (!err) {
         total_open_fd++;
         if (total_open_fd > open_fd_hw) {