From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794590758; b=p/W911SEVR3nHCrcTfJ8PHYkCegVp/2V0PkMcwUyo9j9vWgWBQyR4eu4LzSA+MCT3Ooy 9/SWfw5pvJR7WB7/rWQjy8xAMjieqC6o+MGpCWy/X5KQuOQhEmNb3UIA+4FZb9EAbLayk XYMrpaWGU0mmvRanPTLjojQBLBFFthigXbWbcli3u/BOfEJ7KJz4H94GKUpwdtp3l1PTv 62Qgd3VNXXVO0I7aj8wUgBrkISaojopua/5HyfjJ5mGxCEhBxRAWk+VWCUBEY/7593uqG OenJwkIxJebpY68ZYbuVTqt0gV3ueSnO3tUvwA/mx1WUHQTjGjRrxKfmk64U9D+moGQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794590758; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Transfer-Encoding; bh=BfLslj77rfUHjQO+/TJ245SN9IJNrJC2LADcXKMiYP4=; b=47CJKyqPsrndge8tJwkL/ohsUwB/0DE+w0D1mFOpHw57VWTo21B2kqK4ely20zmDsHzP xJya7t63qgT6l+8ocziLMnx/1fg31wWqX4nUY7pTV7bPUwb0COWJSDdrnT563cwvjfWRN 9HmgOO2G665u5NNIomf4PLXiEEE/OEhXEBuQq0OipjVW//P11ttsktqFBH5BaEz9gr/J0 Z7p8zdqltIECWBUJSfw6dSKfuDbbfl8GMj7GTgQ9J34FPVMCZXgtgoOf0feUEXfJopGMK cd8YT+7hVO5J5+I4SiweYVCv779RNyKanjWAvc9J+S8nl+UODRxOG2Ra/TDyp0SyPUw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=P9eN7ILR; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3E47607C34C for ; Fri, 13 Nov 2026 17:25:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=P9eN7ILR Received: by smtp.kernel.org (Postfix) with ESMTPSA id E9DBF1F000C9; Fri, 13 Nov 2026 17:25:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794590758; bh=BfLslj77rfUHjQO+/TJ245SN9IJNrJC2LADcXKMiYP4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=P9eN7ILRfDThgzZ/jvE3F9OppRtzmE2j53/fEOE0BMXtlmXpOnBoREQa9fy6maLwO yZNSwNPKXjgHuOMZLCw6ctQEZK4xDilKX79dIulsAPmbQWAm/Fahslyz2FeqsTME3y N1zuGY2rQ9c7GiY7WHEwJxC8Ht2YL6kyqikkz8riE5YflltHI4cdBHoDZ3wnhSvTU1 u1thjRawp5o5slRDFka+Wrn6zoa63igQiRPs6y15c64QKUI7m4j5R9rCW9SXcImhF5 wOeyWc5r1bg8KcPFfd6ZbR5Y5Svl9i+1lZxEFya9lnY6aWGB45GUBEx6DFEArH4Fpn WxbJ3UCysG4Dw== From: Sasha Levin To: stable@vger.kernel.org Cc: Fuad Tabba , Marc Zyngier , Oliver Upton , Sasha Levin Subject: [PATCH 6.12.y] KVM: arm64: vgic-its: Don't map an LPI from more than one event Date: Fri, 13 Nov 2026 12:25:56 -0500 Message-ID: <20261113172532.2172602-1-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261107052341-failed-6.12-de81b28f0b83@kernel.org> References: <20261107052341-failed-6.12-de81b28f0b83@kernel.org> X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Fuad Tabba [ Upstream commit de81b28f0b83b22163d7d9a9b708d7aa912e0ef7 ] KVM keeps one vgic_irq per LPI. A MAPTI of an LPI whose vgic_irq already exists reuses it for the new ITE and leaves its target vCPU as it was. Mapping one LPI from two events is UNPREDICTABLE (IHI 0069H.b, 5.2.10), but nothing stops a guest from doing it. If the first MAPTI names a collection the guest never maps and the second a mapped one, the LPI translates through the second event with a NULL target vCPU. When the VMM routes an assigned device's MSI to that event on a host with GICv4 enabled, kvm_vgic_v4_set_forwarding() takes the vPE from the NULL target vCPU and the host oopses: Unable to handle kernel paging request at virtual address 0000000000001980 [...] Call trace: its_build_vmapti_cmd (drivers/irqchip/irq-gic-v3-its.c:943) (P) its_irq_set_vcpu_affinity (drivers/irqchip/irq-gic-v3-its.c:1380) irq_set_vcpu_affinity (kernel/irq/manage.c:670) its_map_vlpi (drivers/irqchip/irq-gic-v4.c:326) kvm_vgic_v4_set_forwarding (arch/arm64/kvm/vgic/vgic-v4.c:485) kvm_arch_irq_bypass_add_producer (arch/arm64/kvm/arm.c:2977) irq_bypass_register_consumer (virt/lib/irqbypass.c:44) kvm_irqfd (arch/arm64/kvm/../../../virt/kvm/eventfd.c:496) kvm_vm_ioctl (arch/arm64/kvm/../../../virt/kvm/kvm_main.c:5270) Track in each vgic_irq whether an ITE maps it. Drop a MAPTI of an LPI that is already mapped, as a failed MAPTI already is. Restoring the ITS tables keeps the first ITE it reads for each LPI and drops any later one, so a guest saved with a double mapping still restores. Key the check on the mapping, not on the vgic_irq. A vgic_irq outlives its ITE while anything holds a reference to it, such as the vCPU it was last queued on. A later MAPTI of such an LPI succeeds as before, and now retargets it to its collection. Fixes: 196b136498b35 ("KVM: arm/arm64: GICv4: Wire mapping/unmapping of VLPIs in VFIO irq bypass") Suggested-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20261013081646.933312-2-fuad.tabba@linux.dev Signed-off-by: Marc Zyngier [ sashal: added the missing irq_lock cleanup while retaining the unmap WARN_ON() and defining mapped as a plain bool. ] Signed-off-by: Sasha Levin --- arch/arm64/kvm/vgic/vgic-its.c | 37 +++++++++++++++++++++++++++++----- include/kvm/arm_vgic.h | 1 + 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index e13e25dddd528..cff18e79a3ac5 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -786,12 +786,18 @@ int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi) /* Requires the its_lock to be held. */ static void its_free_ite(struct kvm *kvm, struct its_ite *ite) { + struct vgic_irq *irq = ite->irq; list_del(&ite->ite_list); /* This put matches the get in vgic_add_lpi. */ - if (ite->irq) { - if (ite->irq->hw) - WARN_ON(its_unmap_vlpi(ite->irq->host_irq)); + if (irq) { + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + if (irq->hw) + WARN_ON(its_unmap_vlpi(ite->irq->host_irq)); + + irq->hw = false; + irq->mapped = false; + } vgic_put_irq(kvm, ite->irq); } @@ -799,6 +805,23 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite) kfree(ite); } +/* Mapping one LPI from two events is UNPREDICTABLE: the first ITE keeps it. */ +static bool vgic_its_map_lpi(struct kvm *kvm, struct its_ite *ite, + struct vgic_irq *irq, struct kvm_vcpu *vcpu) +{ + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + if (!irq->mapped) { + irq->mapped = true; + irq->target_vcpu = vcpu; + ite->irq = irq; + return true; + } + } + + vgic_put_irq(kvm, irq); + return false; +} + static u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size) { return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1); @@ -1108,13 +1131,14 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its, vcpu = collection_to_vcpu(kvm, collection); irq = vgic_add_lpi(kvm, lpi_nr, vcpu); + if (!IS_ERR(irq) && !vgic_its_map_lpi(kvm, ite, irq, vcpu)) + irq = ERR_PTR(-EEXIST); if (IS_ERR(irq)) { if (new_coll) vgic_its_free_collection(its, coll_id); its_free_ite(kvm, ite); return PTR_ERR(irq); } - ite->irq = irq; return 0; } @@ -2223,7 +2247,10 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id, its_free_ite(kvm, ite); return PTR_ERR(irq); } - ite->irq = irq; + + /* Drop an ITE whose LPI another ITE maps, as MAPTI does. */ + if (!vgic_its_map_lpi(kvm, ite, irq, vcpu)) + its_free_ite(kvm, ite); return offset; } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f5172549f9ba0..6c2b1ac1e9671 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -139,6 +139,7 @@ struct vgic_irq { bool active; /* not used for LPIs */ bool enabled; bool hw; /* Tied to HW IRQ */ + bool mapped; /* LPIs: mapped by an ITE */ struct kref refcount; /* Used for LPIs */ u32 hwintid; /* HW INTID number */ unsigned int host_irq; /* linux irq corresponding to hwintid */ -- 2.53.0