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....@redhat.com>
(cherry picked from commit 5cc182ba39a3ca8ec9ba0576de9696be76dc087d)
(Mjt: adjust aio-wait.h include across v10.2.0-154-g12e50722e444
"block: rename block/aio-wait.h to qemu/aio-wait.h")
Signed-off-by: Michael Tokarev <[email protected]>
diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c
index 16c7367ab05..392edd093cf 100644
--- a/hw/virtio/virtio-pmem.c
+++ b/hw/virtio/virtio-pmem.c
@@ -24,6 +24,7 @@
#include "system/hostmem.h"
#include "block/aio.h"
#include "block/thread-pool.h"
+#include "block/aio-wait.h"
#include "trace.h"
typedef struct VirtIODeviceRequest {
@@ -55,14 +56,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)
@@ -86,6 +93,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);
}
@@ -123,6 +131,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)
@@ -130,6 +139,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 fc4fd1f7fe8..fc23f3d95d6 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