Legacy PCI INTx interrupts are level triggered and can, in theory, be shared.
Upon receiving such an interrupt, VFIO kernel driver masks it in a generic way, using INTX_DISABLE bit in the PCI config space. It then waits for the guest to signal completion (EOI) of the interrupt, upon which VFIO re-enables the interrupt. The completion is signaled with another eventfd. For split irqchip mode, KVM doesn't have a notion of EOI on IOAPIC level. Instead it signals EOI with a userspace VM exit, KVM_EXIT_IOAPIC_EOI. Upon receiving this VM exit, Qemu manually signals the above-mentioned eventfd. The problem that this patch resolves is that QEMU keeps these eventfds in a linked list without any locking to prevent the list from being corrupted by concurrent access. Such use is possible and guest triggerable if the guest rapidly toggles INTX mode of a VFIO device and at the same time signals EOI with an arbitrary vector. Fix this by protecting this list (kvm_resample_fd_list) with a mutex. This patch was only compile tested. Reported-by: "Vulnerability Report" <[email protected]> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3942 Signed-off-by: Maxim Levitsky <[email protected]> --- accel/kvm/kvm-all.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 83cbd120a847..da3afdc68d8d 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -146,6 +146,7 @@ typedef struct KVMResampleFd KVMResampleFd; static QLIST_HEAD(, KVMResampleFd) kvm_resample_fd_list = QLIST_HEAD_INITIALIZER(kvm_resample_fd_list); +static QemuMutex kvm_resample_fd_list_lock; static QemuMutex kml_slots_lock; #define kvm_slots_lock() qemu_mutex_lock(&kml_slots_lock) @@ -157,6 +158,8 @@ static inline void kvm_resample_fd_remove(int gsi) { KVMResampleFd *rfd; + qemu_mutex_lock(&kvm_resample_fd_list_lock); + QLIST_FOREACH(rfd, &kvm_resample_fd_list, node) { if (rfd->gsi == gsi) { QLIST_REMOVE(rfd, node); @@ -164,6 +167,8 @@ static inline void kvm_resample_fd_remove(int gsi) break; } } + + qemu_mutex_unlock(&kvm_resample_fd_list_lock); } static inline void kvm_resample_fd_insert(int gsi, EventNotifier *event) @@ -173,20 +178,26 @@ static inline void kvm_resample_fd_insert(int gsi, EventNotifier *event) rfd->gsi = gsi; rfd->resample_event = event; + qemu_mutex_lock(&kvm_resample_fd_list_lock); QLIST_INSERT_HEAD(&kvm_resample_fd_list, rfd, node); + qemu_mutex_unlock(&kvm_resample_fd_list_lock); } void kvm_resample_fd_notify(int gsi) { KVMResampleFd *rfd; + qemu_mutex_lock(&kvm_resample_fd_list_lock); + QLIST_FOREACH(rfd, &kvm_resample_fd_list, node) { if (rfd->gsi == gsi) { event_notifier_set(rfd->resample_event); trace_kvm_resample_fd_notify(gsi); - return; + break; } } + + qemu_mutex_unlock(&kvm_resample_fd_list_lock); } /** @@ -2910,6 +2921,7 @@ static int kvm_init(AccelState *as, MachineState *ms) int type; qemu_mutex_init(&kml_slots_lock); + qemu_mutex_init(&kvm_resample_fd_list_lock); /* * On systems where the kernel can support different base page -- 2.54.0
