mshv_partition_ioctl_create_vp() called anon_inode_getfd() before
publishing the new VP into partition->pt_vp_array.  anon_inode_getfd()
includes fd_install(), so the fd was live in current->files before the
publish ran.

A concurrent MSHV_RUN_VP ioctl on that fd does not serialise against the
in-progress MSHV_CREATE_VP — it takes vp->vp_mutex, not the partition
mutex.  Once the VP starts running and traps, mshv_intercept_isr() can look
up partition->pt_vp_array[vp_index] and observe NULL, silently dropping the
intercept message.

Split the fd creation: reserve an fd with get_unused_fd_flags(), create the
file with anon_inode_getfile(), publish the VP via smp_store_release(), and
finally call fd_install() as the userspace-visibility commit point.

Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose 
/dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <[email protected]>
---
 drivers/hv/mshv_root_main.c |   29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index e32f6e0f9f637..1c18d1c1f7947 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1142,6 +1142,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition 
*partition,
        struct mshv_vp *vp;
        struct page *intercept_msg_page, *register_page, *ghcb_page;
        struct hv_stats_page *stats_pages[2];
+       struct file *file;
+       int fd;
        long ret;
 
        if (copy_from_user(&args, arg, sizeof(args)))
@@ -1214,14 +1216,18 @@ mshv_partition_ioctl_create_vp(struct mshv_partition 
*partition,
        if (ret)
                goto put_partition;
 
-       /*
-        * Keep anon_inode_getfd last: it installs fd in the file struct and
-        * thus makes the state accessible in user space.
-        */
-       ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
-                              O_RDWR | O_CLOEXEC);
-       if (ret < 0)
+       fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               ret = fd;
                goto remove_debugfs_vp;
+       }
+
+       file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp,
+                                 O_RDWR | O_CLOEXEC);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto put_unused_vp_fd;
+       }
 
        /* already exclusive with the partition mutex for all ioctls */
        partition->pt_vp_count++;
@@ -1233,8 +1239,17 @@ mshv_partition_ioctl_create_vp(struct mshv_partition 
*partition,
         */
        smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
 
+       /*
+        * fd_install() is the userspace-visibility commit point.  Must be the
+        * last operation that can fail or be observed.
+        */
+       fd_install(fd, file);
+       ret = fd;
+
        goto out;
 
+put_unused_vp_fd:
+       put_unused_fd(fd);
 remove_debugfs_vp:
        mshv_debugfs_vp_remove(vp);
 put_partition:



Reply via email to