Fix a heap-use-after-free in the virtio-rng frontend when a delayed
rng-random backend completion arrives after the virtio-rng device has been
hot-unplugged.
Fixes: CVE-2026-50624
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3917
Reported-by: Jia Jia <physicalmtea@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---
Notes:
v2:
- rename rng_backend_cancel_request_entropy() to
rng_backend_cancel_requests()
- added const to void *opaque
backends/rng.c | 16 ++++++++++++++++
hw/virtio/virtio-rng.c | 2 ++
include/system/rng.h | 14 ++++++++++++++
3 files changed, 32 insertions(+)
diff --git a/backends/rng.c b/backends/rng.c
index ab94dfea850a..7bed62616a52 100644
--- a/backends/rng.c
+++ b/backends/rng.c
@@ -68,6 +68,22 @@ static void rng_backend_free_request(RngRequest *req)
g_free(req);
}
+void rng_backend_cancel_requests(RngBackend *s,
+ EntropyReceiveFunc *receive_entropy,
+ const void *opaque)
+{
+ RngRequest *req, *next;
+
+ QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) {
+ if (req->receive_entropy != receive_entropy ||
+ req->opaque != opaque) {
+ continue;
+ }
+ QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next);
+ rng_backend_free_request(req);
+ }
+}
+
static void rng_backend_free_requests(RngBackend *s)
{
RngRequest *req, *next;
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index 66690a34dc97..d68d9011953b 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -234,6 +234,8 @@ static void virtio_rng_device_unrealize(DeviceState *dev)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIORNG *vrng = VIRTIO_RNG(dev);
+ rng_backend_cancel_requests(vrng->rng, chr_read, vrng);
+
qemu_del_vm_change_state_handler(vrng->vmstate);
timer_free(vrng->rate_limit_timer);
virtio_del_queue(vdev, 0);
diff --git a/include/system/rng.h b/include/system/rng.h
index e383f87d20b2..a2667d75efd0 100644
--- a/include/system/rng.h
+++ b/include/system/rng.h
@@ -86,4 +86,18 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
* deleted.
*/
void rng_backend_finalize_request(RngBackend *s, RngRequest *req);
+
+/**
+ * rng_backend_cancel_requests:
+ * @s: the backend that created the request
+ * @receive_entropy: the function invoked when entropy is available
+ * @opaque: data passed to @receive_entropy
+ *
+ * This function is used by the front-end to cancel all requests to a
+ * given backend. Requests to cancel are identified by the receive_entropy
+ * function and the data passed to the function.
+ */
+void rng_backend_cancel_requests(RngBackend *s,
+ EntropyReceiveFunc *receive_entropy,
+ const void *opaque);
#endif
--
2.54.0