Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

vhost: Fix crash during early vhost_transport_send_pkt calls

If userspace does VHOST_VSOCK_SET_GUEST_CID before VHOST_SET_OWNER we
can race where:
1. thread0 calls vhost_transport_send_pkt -> vhost_work_queue
2. thread1 does VHOST_SET_OWNER which calls vhost_worker_create.
3. vhost_worker_create will set the dev->worker pointer before setting
the worker->vtsk pointer.
4. thread0's vhost_work_queue will see the dev->worker pointer is
set and try to call vhost_task_wake using not yet set worker->vtsk
pointer.
5. We then crash since vtsk is NULL.

Before commit 6e890c5d5021 ("vhost: use vhost_tasks for worker
threads"), we only had the worker pointer so we could just check it to
see if VHOST_SET_OWNER has been done. After that commit we have the
vhost_worker and vhost_task pointer, so we can now hit the bug above.

This patch embeds the vhost_worker in the vhost_dev and moves the work
list initialization back to vhost_dev_init, so we can just check the
worker.vtsk pointer to check if VHOST_SET_OWNER has been done like
before.

Fixes: 6e890c5d5021 ("vhost: use vhost_tasks for worker threads")
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Message-Id: <20230607192338.6041-2-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reported-by: syzbot+d0d442c22fa8db45ff0e@syzkaller.appspotmail.com
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>

authored by

Mike Christie and committed by
Michael S. Tsirkin
a284f09e 1f5d2e3b

+18 -34
+17 -33
drivers/vhost/vhost.c
··· 235 235 { 236 236 struct vhost_flush_struct flush; 237 237 238 - if (dev->worker) { 238 + if (dev->worker.vtsk) { 239 239 init_completion(&flush.wait_event); 240 240 vhost_work_init(&flush.work, vhost_flush_work); 241 241 ··· 247 247 248 248 void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work) 249 249 { 250 - if (!dev->worker) 250 + if (!dev->worker.vtsk) 251 251 return; 252 252 253 253 if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) { ··· 255 255 * sure it was not in the list. 256 256 * test_and_set_bit() implies a memory barrier. 257 257 */ 258 - llist_add(&work->node, &dev->worker->work_list); 259 - vhost_task_wake(dev->worker->vtsk); 258 + llist_add(&work->node, &dev->worker.work_list); 259 + vhost_task_wake(dev->worker.vtsk); 260 260 } 261 261 } 262 262 EXPORT_SYMBOL_GPL(vhost_work_queue); ··· 264 264 /* A lockless hint for busy polling code to exit the loop */ 265 265 bool vhost_has_work(struct vhost_dev *dev) 266 266 { 267 - return dev->worker && !llist_empty(&dev->worker->work_list); 267 + return !llist_empty(&dev->worker.work_list); 268 268 } 269 269 EXPORT_SYMBOL_GPL(vhost_has_work); 270 270 ··· 456 456 dev->umem = NULL; 457 457 dev->iotlb = NULL; 458 458 dev->mm = NULL; 459 - dev->worker = NULL; 459 + memset(&dev->worker, 0, sizeof(dev->worker)); 460 + init_llist_head(&dev->worker.work_list); 460 461 dev->iov_limit = iov_limit; 461 462 dev->weight = weight; 462 463 dev->byte_weight = byte_weight; ··· 531 530 532 531 static void vhost_worker_free(struct vhost_dev *dev) 533 532 { 534 - struct vhost_worker *worker = dev->worker; 535 - 536 - if (!worker) 533 + if (!dev->worker.vtsk) 537 534 return; 538 535 539 - dev->worker = NULL; 540 - WARN_ON(!llist_empty(&worker->work_list)); 541 - vhost_task_stop(worker->vtsk); 542 - kfree(worker); 536 + WARN_ON(!llist_empty(&dev->worker.work_list)); 537 + vhost_task_stop(dev->worker.vtsk); 538 + dev->worker.kcov_handle = 0; 539 + dev->worker.vtsk = NULL; 543 540 } 544 541 545 542 static int vhost_worker_create(struct vhost_dev *dev) 546 543 { 547 - struct vhost_worker *worker; 548 544 struct vhost_task *vtsk; 549 545 char name[TASK_COMM_LEN]; 550 - int ret; 551 546 552 - worker = kzalloc(sizeof(*worker), GFP_KERNEL_ACCOUNT); 553 - if (!worker) 554 - return -ENOMEM; 555 - 556 - dev->worker = worker; 557 - worker->kcov_handle = kcov_common_handle(); 558 - init_llist_head(&worker->work_list); 559 547 snprintf(name, sizeof(name), "vhost-%d", current->pid); 560 548 561 - vtsk = vhost_task_create(vhost_worker, worker, name); 562 - if (!vtsk) { 563 - ret = -ENOMEM; 564 - goto free_worker; 565 - } 549 + vtsk = vhost_task_create(vhost_worker, &dev->worker, name); 550 + if (!vtsk) 551 + return -ENOMEM; 566 552 567 - worker->vtsk = vtsk; 553 + dev->worker.kcov_handle = kcov_common_handle(); 554 + dev->worker.vtsk = vtsk; 568 555 vhost_task_start(vtsk); 569 556 return 0; 570 - 571 - free_worker: 572 - kfree(worker); 573 - dev->worker = NULL; 574 - return ret; 575 557 } 576 558 577 559 /* Caller should have device mutex */
+1 -1
drivers/vhost/vhost.h
··· 154 154 struct vhost_virtqueue **vqs; 155 155 int nvqs; 156 156 struct eventfd_ctx *log_ctx; 157 - struct vhost_worker *worker; 157 + struct vhost_worker worker; 158 158 struct vhost_iotlb *umem; 159 159 struct vhost_iotlb *iotlb; 160 160 spinlock_t iotlb_lock;