virtio_fs: read the vring size under virtio_fs_mutex in ->get_tree

The vring size was read after virtio_fs_find_instance() had dropped
virtio_fs_mutex, so a concurrent virtio_fs_remove() could free the
virtqueue first. The reference from the lookup keeps the virtio_fs
alive, but not its virtqueues.

Hold the mutex across the lookup and the read.

This issue was found by an in-house static analysis tool.

Fixes: a7f0d7aab0 ("virtiofs: split requests that exceed virtqueue size")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6
Co-developed-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Fan Wu 2026-09-22 08:13:18 +00:00 committed by Miklos Szeredi
parent 15c20483d9
commit e7ccc777a0

View File

@ -455,26 +455,23 @@ static int virtio_fs_add_instance(struct virtio_device *vdev,
return ret;
}
/* Return the virtio_fs with a given tag, or NULL */
/* Return the virtio_fs with a given tag, or NULL.
* Callers hold virtio_fs_mutex, which also keeps the virtqueues alive.
*/
static struct virtio_fs *virtio_fs_find_instance(const char *tag)
{
struct virtio_fs *fs;
mutex_lock(&virtio_fs_mutex);
lockdep_assert_held(&virtio_fs_mutex);
list_for_each_entry(fs, &virtio_fs_instances, list) {
if (strcmp(fs->tag, tag) == 0) {
kobject_get(&fs->kobj);
goto found;
return fs;
}
}
fs = NULL; /* not found */
found:
mutex_unlock(&virtio_fs_mutex);
return fs;
return NULL; /* not found */
}
static void virtio_fs_free_devs(struct virtio_fs *fs)
@ -1700,13 +1697,17 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
* in chan->iq->priv. Once fuse_conn is going away, it calls ->put()
* to drop the reference to this object.
*/
mutex_lock(&virtio_fs_mutex);
fs = virtio_fs_find_instance(fsc->source);
if (fs)
virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
mutex_unlock(&virtio_fs_mutex);
if (!fs) {
pr_info("virtio-fs: tag <%s> not found\n", fsc->source);
return -EINVAL;
}
virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
if (WARN_ON(virtqueue_size <= FUSE_HEADER_OVERHEAD))
goto out_err;