A resampler-bound irqfd is only meaningful for level-triggered interrupts,
because the resampler signals userspace on guest EOI and edge-triggered
interrupts have no EOI to react to.  mshv_irqfd_assign() already rejects
the combination at registration time, but nothing prevented a subsequent
MSHV_SET_MSI_ROUTING ioctl from flipping the cached
lapic_control.level_triggered bit to edge-triggered while the resampler was
still attached. Once that happened, the WARN_ON() in mshv_assert_irq_slow()
was the only signal of an inconsistency the kernel had no way to recover
from: the resampler stayed wired in but no EOI ever arrived, so userspace
never saw a resample signal and the guest interrupt could become stuck.

Add mshv_irqfd_validate_routing() and call it from
mshv_update_routing_table() before publishing the new table. It walks
pt_irqfds_list looking for resampler-bound irqfds whose new routing entry
would be valid but not level-triggered, and returns -EINVAL to reject the
routing change atomically. The check is x86-only because
lapic_control.level_triggered is only populated on x86; on ARM64
mshv_copy_girq_info() unconditionally sets asserted = 1 and the invariant
does not apply.

The validator briefly takes pt_irqfds_lock for the list walk and drops it
before mshv_irqfd_routing_update() reacquires it.  This is safe because the
partition ioctl dispatcher holds pt_mutex for the duration of every
partition ioctl, so MSHV_IRQFD (which calls mshv_irqfd_assign()) cannot run
concurrently with MSHV_SET_MSI_ROUTING; no new resampler-bound irqfd can be
inserted between validation and refresh.

Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose 
/dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <[email protected]>
---
 drivers/hv/mshv_irq.c |   44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c
index b3142c84dcbc2..59584a132ca9f 100644
--- a/drivers/hv/mshv_irq.c
+++ b/drivers/hv/mshv_irq.c
@@ -14,7 +14,44 @@
 #include "mshv.h"
 #include "mshv_root.h"
 
-/* called from the ioctl code, user wants to update the guest irq table */
+static int mshv_irqfd_validate_routing(struct mshv_partition *pt,
+                                      struct mshv_girq_routing_table *new)
+{
+       int r = 0;
+#if IS_ENABLED(CONFIG_X86)
+       struct mshv_irqfd *irqfd;
+
+       if (!new)
+               return 0;
+
+       spin_lock_irq(&pt->pt_irqfds_lock);
+       hlist_for_each_entry(irqfd, &pt->pt_irqfds_list, irqfd_hnode) {
+               struct mshv_guest_irq_ent ent = {};
+               struct mshv_lapic_irq lirq;
+
+               if (!irqfd->irqfd_resampler)
+                       continue;
+
+               if (irqfd->irqfd_irqnum < new->num_rt_entries)
+                       ent = new->mshv_girq_info_tbl[irqfd->irqfd_irqnum];
+
+               mshv_copy_girq_info(&ent, &lirq);
+
+               if (ent.girq_entry_valid &&
+                   !lirq.lapic_control.level_triggered) {
+                       r = -EINVAL;
+                       break;
+               }
+       }
+       spin_unlock_irq(&pt->pt_irqfds_lock);
+#endif
+       return r;
+}
+
+/*
+ * Called from the ioctl code, user wants to update the guest irq table.
+ * Serialized with mshv_irqfd_assign by partition mutex.
+ */
 int mshv_update_routing_table(struct mshv_partition *partition,
                              const struct mshv_user_irq_entry *ue,
                              unsigned int numents)
@@ -65,6 +102,11 @@ int mshv_update_routing_table(struct mshv_partition 
*partition,
 
 swap_routes:
        mutex_lock(&partition->pt_irq_lock);
+       r = mshv_irqfd_validate_routing(partition, new);
+       if (r) {
+               mutex_unlock(&partition->pt_irq_lock);
+               goto out;
+       }
        old = rcu_dereference_protected(partition->pt_girq_tbl, 1);
        rcu_assign_pointer(partition->pt_girq_tbl, new);
        mshv_irqfd_routing_update(partition);



Reply via email to