[Stable-11.0.4 055/120] virtio-pmem: wait for flush requests on unrealize
Michael Tokarev <[email protected]>
| Newsgroups | gmane.comp.emulators.qemu.stable,gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
From: "Michael S. Tsirkin" <[email protected]> virtio_pmem_flush submits fsync requests to the thread pool and stores a VirtIOPMEM pointer in each request. If device is deleted e.g. by hot-unplug, once these complete, done_cb can run after virtio_pmem_unrealize frees the device, causing a use-after-free. Track in-flight requests and wait in virtio_pmem_unrealize until their completions finish before tearing the device down. Fixes: CVE-2026-63323 Fixes: 5f503cd9f3 ("virtio-pmem: add virtio device") Cc: David Hildenbrand <[email protected]> Cc: Pankaj Gupta <[email protected]> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3938 Reported-by: Jia Jia <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Message-ID: <417b6685f37ce818c660ca3c84945992f5c30dcf.1784894206.git.mst@redhat.com> (cherry picked from commit 5cc182ba39a3ca8ec9ba0576de9696be76dc087d) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c3b3299c9cd..6f7271c140a 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -23,6 +23,7 @@ #include "standard-headers/linux/virtio_pmem.h" #include "system/hostmem.h" #include "block/thread-pool.h" +#include "qemu/aio-wait.h" #include "trace.h" typedef struct VirtIODeviceRequest { @@ -54,14 +55,20 @@ static int worker_cb(void *opaque) static void done_cb(void *opaque, int ret) { VirtIODeviceRequest *req_data = opaque; + VirtIOPMEM *pmem = req_data->pmem; int len = iov_from_buf(req_data->elem.in_sg, req_data->elem.in_num, 0, &req_data->resp, sizeof(struct virtio_pmem_resp)); /* Callbacks are serialized, so no need to use atomic ops. */ - virtqueue_push(req_data->pmem->rq_vq, &req_data->elem, len); - virtio_notify((VirtIODevice *)req_data->pmem, req_data->pmem->rq_vq); + virtqueue_push(pmem->rq_vq, &req_data->elem, len); + virtio_notify((VirtIODevice *)pmem, pmem->rq_vq); trace_virtio_pmem_response(); g_free(req_data); + + pmem->inflight--; + if (!pmem->inflight) { + aio_wait_kick(); + } } static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) @@ -85,6 +92,7 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) req_data->fd = memory_region_get_fd(&backend->mr); req_data->pmem = pmem; req_data->vdev = vdev; + pmem->inflight++; thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data); } @@ -122,6 +130,7 @@ static void virtio_pmem_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(pmem->memdev, true); virtio_init(vdev, VIRTIO_ID_PMEM, sizeof(struct virtio_pmem_config)); pmem->rq_vq = virtio_add_queue(vdev, 128, virtio_pmem_flush); + pmem->inflight = 1; } static void virtio_pmem_unrealize(DeviceState *dev) @@ -129,6 +138,10 @@ static void virtio_pmem_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOPMEM *pmem = VIRTIO_PMEM(dev); + /* Release the device's own reference and wait for in-flight flushes */ + pmem->inflight--; + AIO_WAIT_WHILE(NULL, pmem->inflight > 0); + host_memory_backend_set_mapped(pmem->memdev, false); virtio_delete_queue(pmem->rq_vq); virtio_cleanup(vdev); diff --git a/include/hw/virtio/virtio-pmem.h b/include/hw/virtio/virtio-pmem.h index 9cce600d0b2..bb959d10cf6 100644 --- a/include/hw/virtio/virtio-pmem.h +++ b/include/hw/virtio/virtio-pmem.h @@ -32,6 +32,7 @@ struct VirtIOPMEM { VirtQueue *rq_vq; uint64_t start; HostMemoryBackend *memdev; + unsigned int inflight; }; struct VirtIOPMEMClass { -- 2.47.3