From: "Michael S. Tsirkin" <[email protected]> When a guest triggers PCIe hot-unplug while the free-page-hint BH is running on an IOThread, the BH will keep accessing vq->used_elems array when running it. Note that qemu_bh_delete merely prevents new BHs from running, it does not wait for already running ones to finish.
We need to wait for it to finish - do it like virtio scsi and run a dummy oneshot AIO in the same context, and wait for it. But there's a twist: BH could be blocked in qemu_cond_wait, then AIO won't run. Add a special reporting state FREE_PAGE_HINT_S_UNREALIZE to make BH exit immediately. Cc: David Hildenbrand <[email protected]> Fixes: CVE-2026-66899 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4079 Reported-by: mhun512 <[email protected]> Reviewed-by: David Hildenbrand <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Message-ID: <0967d574d9ffe469edf5fc16da86237e54a69c34.1785327742.git....@redhat.com> (cherry picked from commit fdc3f9a83ddf6f9b32f978ee2fee466816a3b9ce) (Mjt: fix aio-wait.h include path 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-balloon.c b/hw/virtio/virtio-balloon.c index 2eb5a14fa2c..1ee679c33fa 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -34,6 +34,7 @@ #include "system/reset.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" +#include "block/aio-wait.h" #define BALLOON_PAGE_SIZE (1 << VIRTIO_BALLOON_PFN_SHIFT) @@ -518,6 +519,9 @@ static bool get_free_page_hints(VirtIOBalloon *dev) int i; while (dev->block_iothread) { + if (dev->free_page_hint_status == FREE_PAGE_HINT_S_UNREALIZE) { + return false; + } qemu_cond_wait(&dev->free_page_cond, &dev->free_page_lock); } @@ -914,6 +918,11 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) qemu_register_resettable(OBJECT(dev)); } +static void dummy_bh(void *opaque) +{ + /* Do nothing */ +} + static void virtio_balloon_device_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -921,9 +930,17 @@ static void virtio_balloon_device_unrealize(DeviceState *dev) qemu_unregister_resettable(OBJECT(dev)); if (s->free_page_bh) { + AioContext *ctx = iothread_get_aio_context(s->iothread); + qemu_bh_delete(s->free_page_bh); + + qemu_mutex_lock(&s->free_page_lock); + s->free_page_hint_status = FREE_PAGE_HINT_S_UNREALIZE; + qemu_cond_signal(&s->free_page_cond); + qemu_mutex_unlock(&s->free_page_lock); + aio_wait_bh_oneshot(ctx, dummy_bh, NULL); + object_unref(OBJECT(s->iothread)); - virtio_balloon_free_page_stop(s); precopy_remove_notifier(&s->free_page_hint_notify); } balloon_stats_destroy_timer(s); diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 0456c211c6e..dc3de998a25 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -39,6 +39,7 @@ enum virtio_balloon_free_page_hint_status { FREE_PAGE_HINT_S_REQUESTED = 1, FREE_PAGE_HINT_S_START = 2, FREE_PAGE_HINT_S_DONE = 3, + FREE_PAGE_HINT_S_UNREALIZE = 4, }; struct VirtIOBalloon { -- 2.47.3
