diff options
| author | Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> | 2026-05-07 15:44:32 +0000 |
|---|---|---|
| committer | Wei Liu <wei.liu@kernel.org> | 2026-07-23 06:27:20 +0000 |
| commit | b7e92e14744131ca00720837dcdff944fa54fa10 (patch) | |
| tree | f3ec0f5fa80589f92223582f07a8858a91912e14 | |
| parent | 2225dc7722999c4746dddc1efd7ac0282975fd90 (diff) | |
| download | linux-next-b7e92e14744131ca00720837dcdff944fa54fa10.tar.gz linux-next-b7e92e14744131ca00720837dcdff944fa54fa10.zip | |
mshv: Publish VP to pt_vp_array before installing the file descriptor
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 <skinsburskii@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
| -rw-r--r-- | drivers/hv/mshv_root_main.c | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 8a15448e2ace..cc2cfce2aefd 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1072,6 +1072,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))) @@ -1146,14 +1148,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++; @@ -1165,8 +1171,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: |
