* [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses
@ 2026-08-06 10:02 Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 1/4] KVM: arm64: Validate the host-provided vgic model in pKVM Fuad Tabba
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Fuad Tabba @ 2026-08-06 10:02 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
linux-kernel
Cc: Will Deacon, Catalin Marinas, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Sascha Bischoff
Hi folks,
This series stops KVM reaching GICv5 CPU interface registers on hardware
that does not implement them, in three places with no guard.
Under pKVM the first two are reachable from an untrusted host. EL2
copies vgic_model out of the host's struct kvm without validating it,
and the nVHE world switch dispatches on that field with no cpucap
guard, so a host writing KVM_DEV_TYPE_ARM_VGIC_V5 steers EL2 into
ICC_ICSR_EL1 and the ICH_PPI_* registers. Separately, __vgic_v5_save_apr
and __vgic_v5_restore_vmcr_apr sit in the hypercall band the
de-privileged host may still call, and pKVM never registers a GICv5
vgic, so neither has a valid caller in protected mode. Without
FEAT_GCIE those registers are UNDEFINED at EL2, so either path panics
the hypervisor. Both need a compromised host kernel rather than host
userspace, so this is hardening and not a guest-reachable hole.
I had said these paths were unreachable under pKVM because
vgic_v5_probe() skips GICv5 registration in protected mode [1]. That was
wrong. The skip is host-side only, and does not constrain what a
malicious host can call.
The third one is not pKVM. can_access_vgic_from_kernel() excludes only
the GICv3 system register interface, so on a native GICv5 system
without FEAT_GCIE_LEGACY the kernel reaches EL2-only registers from EL1
under nVHE, and the world switch does the same work at EL2 anyway.
The last patch drops the VGICv3 reference from two nVHE world switch
comments that cover GICv5 too. No functional change.
Tested on QEMU. I also checked the first one with a local host patch
that hands EL2 a GICv5 model: it panics at __vgic_v5_restore_state
before the series and boots cleanly after.
Based on Linux 7.2-rc6 (075b74841bd00). It also applies cleanly to
kvmarm/next and kvmarm/fixes.
I really should stop looking at the GIC, but I won't be able to anytime
soon I'm afraid...
Cheers,
/fuad
[1] https://lore.kernel.org/all/CA%2BEHjTyGULmVCgyoya3bXG4gRj0OYFE1gnJLhNE6kvCrZFtXyQ@mail.gmail.com/
Fuad Tabba (4):
KVM: arm64: Validate the host-provided vgic model in pKVM
KVM: arm64: Reject the GICv5 CPU interface hypercalls under pKVM
KVM: arm64: vgic: Do not access the GICv5 CPU interface from EL1
KVM: arm64: Fix stale VGICv3 comments in the nVHE world switch
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 6 ++++++
arch/arm64/kvm/hyp/nvhe/pkvm.c | 16 ++++++++++++++--
arch/arm64/kvm/hyp/nvhe/switch.c | 4 ++--
arch/arm64/kvm/vgic/vgic.c | 12 ++++++++----
4 files changed, 30 insertions(+), 8 deletions(-)
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
2.39.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v1 1/4] KVM: arm64: Validate the host-provided vgic model in pKVM
2026-08-06 10:02 [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses Fuad Tabba
@ 2026-08-06 10:02 ` Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 2/4] KVM: arm64: Reject the GICv5 CPU interface hypercalls under pKVM Fuad Tabba
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Fuad Tabba @ 2026-08-06 10:02 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
linux-kernel
Cc: Will Deacon, Catalin Marinas, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Sascha Bischoff
EL2 copies vgic_model from the host's struct kvm unchecked, and the nVHE
world switch dispatches on it with no cpucap guard. A host writing
KVM_DEV_TYPE_ARM_VGIC_V5 makes EL2 access GICv5 CPU interface registers,
which are UNDEFINED without FEAT_GCIE and panic the hypervisor on any
GICv3 machine.
Accept only the models pKVM can run, forcing anything else to 0.
Fixes: 9b8e3d4ca0e73 ("KVM: arm64: gic-v5: Implement GICv5 load/put and save/restore")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/pkvm.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 24d6f164129ac..59bb15efdca42 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -340,13 +340,25 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
{
struct kvm *kvm = &hyp_vm->kvm;
unsigned long host_arch_flags = READ_ONCE(host_kvm->arch.flags);
+ u32 vgic_model = READ_ONCE(host_kvm->arch.vgic.vgic_model);
DECLARE_BITMAP(allowed_features, KVM_VCPU_MAX_FEATURES);
/* CTR_EL0 is always under host control, even for protected VMs. */
hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
- /* Preserve the vgic model so that GICv3 emulation works */
- hyp_vm->kvm.arch.vgic.vgic_model = host_kvm->arch.vgic.vgic_model;
+ /*
+ * Preserve the vgic model for GICv3 emulation, but only what pKVM can
+ * run: the GICv5 world switch touches registers UNDEFINED at EL2
+ * without FEAT_GCIE. 0 is not a valid kvm_device_type: "no vgic".
+ */
+ switch (vgic_model) {
+ case KVM_DEV_TYPE_ARM_VGIC_V2:
+ case KVM_DEV_TYPE_ARM_VGIC_V3:
+ break;
+ default:
+ vgic_model = 0;
+ }
+ hyp_vm->kvm.arch.vgic.vgic_model = vgic_model;
/* No restrictions for non-protected VMs. */
if (!kvm_vm_is_protected(kvm)) {
--
2.39.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v1 2/4] KVM: arm64: Reject the GICv5 CPU interface hypercalls under pKVM
2026-08-06 10:02 [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 1/4] KVM: arm64: Validate the host-provided vgic model in pKVM Fuad Tabba
@ 2026-08-06 10:02 ` Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 3/4] KVM: arm64: vgic: Do not access the GICv5 CPU interface from EL1 Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 4/4] KVM: arm64: Fix stale VGICv3 comments in the nVHE world switch Fuad Tabba
3 siblings, 0 replies; 5+ messages in thread
From: Fuad Tabba @ 2026-08-06 10:02 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
linux-kernel
Cc: Will Deacon, Catalin Marinas, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Sascha Bischoff
__vgic_v5_save_apr() and __vgic_v5_restore_vmcr_apr() remain callable
after pKVM finalises. pKVM never registers a GICv5 vgic, so neither has
a valid caller in protected mode, and on a GICv3 machine the registers
they access are UNDEFINED at EL2 and panic the hypervisor.
Reject both when protected mode is enabled.
Fixes: af325e87af5da ("KVM: arm64: gic-v5: Add vgic-v5 save/restore hyp interface")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index d3df96ed8ba42..a9afd350b1fb3 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -695,6 +695,9 @@ static void handle___vgic_v5_save_apr(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
+ if (unlikely(is_protected_kvm_enabled()))
+ return;
+
__vgic_v5_save_apr(kern_hyp_va(cpu_if));
}
@@ -702,6 +705,9 @@ static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
+ if (unlikely(is_protected_kvm_enabled()))
+ return;
+
__vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
}
--
2.39.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v1 3/4] KVM: arm64: vgic: Do not access the GICv5 CPU interface from EL1
2026-08-06 10:02 [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 1/4] KVM: arm64: Validate the host-provided vgic model in pKVM Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 2/4] KVM: arm64: Reject the GICv5 CPU interface hypercalls under pKVM Fuad Tabba
@ 2026-08-06 10:02 ` Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 4/4] KVM: arm64: Fix stale VGICv3 comments in the nVHE world switch Fuad Tabba
3 siblings, 0 replies; 5+ messages in thread
From: Fuad Tabba @ 2026-08-06 10:02 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
linux-kernel
Cc: Will Deacon, Catalin Marinas, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Sascha Bischoff
can_access_vgic_from_kernel() excludes only the GICv3 system register
interface, so on a native GICv5 system without FEAT_GCIE_LEGACY it
returns true under nVHE. The kernel then saves and restores the CPU
interface from EL1, where ICH_VMCR_EL2 and the ICH_PPI_* registers are
UNDEFINED, and the nVHE world switch already does that work at EL2.
Require VHE for GICv5 as for GICv3.
Fixes: 9b8e3d4ca0e73 ("KVM: arm64: gic-v5: Implement GICv5 load/put and save/restore")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/vgic/vgic.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 74bace10a22ed..df58c0042ed63 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -1042,11 +1042,15 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
static inline bool can_access_vgic_from_kernel(void)
{
/*
- * GICv2 can always be accessed from the kernel because it is
- * memory-mapped, and VHE systems can access GICv3 EL2 system
- * registers.
+ * GICv3 and GICv5 drive the CPU interface through EL2 system
+ * registers, so only VHE reaches them from the kernel. GICv2 is
+ * memory-mapped and always reachable.
*/
- return !static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif) || has_vhe();
+ if (kvm_vgic_global_state.type == VGIC_V5 ||
+ static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
+ return has_vhe();
+
+ return true;
}
static inline void vgic_save_state(struct kvm_vcpu *vcpu)
--
2.39.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v1 4/4] KVM: arm64: Fix stale VGICv3 comments in the nVHE world switch
2026-08-06 10:02 [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses Fuad Tabba
` (2 preceding siblings ...)
2026-08-06 10:02 ` [PATCH v1 3/4] KVM: arm64: vgic: Do not access the GICv5 CPU interface from EL1 Fuad Tabba
@ 2026-08-06 10:02 ` Fuad Tabba
3 siblings, 0 replies; 5+ messages in thread
From: Fuad Tabba @ 2026-08-06 10:02 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, linux-arm-kernel, kvmarm,
linux-kernel
Cc: Will Deacon, Catalin Marinas, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Sascha Bischoff
__hyp_vgic_save_state() and __hyp_vgic_restore_state() handle GICv5 as
well as GICv3, but their comments name VGICv3 only.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/switch.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index 7318e3e6a5f36..6b9e20f6b31b3 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -110,7 +110,7 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
write_sysreg(__kvm_hyp_host_vector, vbar_el2);
}
-/* Save VGICv3 state on non-VHE systems */
+/* Save vgic state on non-VHE systems */
static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu)
{
if (vgic_is_v5(kern_hyp_va(vcpu->kvm))) {
@@ -125,7 +125,7 @@ static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu)
}
}
-/* Restore VGICv3 state on non-VHE systems */
+/* Restore vgic state on non-VHE systems */
static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu)
{
if (vgic_is_v5(kern_hyp_va(vcpu->kvm))) {
--
2.39.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-06 10:04 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 10:02 [PATCH v1 0/4] KVM: arm64: Fix unguarded GICv5 CPU interface accesses Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 1/4] KVM: arm64: Validate the host-provided vgic model in pKVM Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 2/4] KVM: arm64: Reject the GICv5 CPU interface hypercalls under pKVM Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 3/4] KVM: arm64: vgic: Do not access the GICv5 CPU interface from EL1 Fuad Tabba
2026-08-06 10:02 ` [PATCH v1 4/4] KVM: arm64: Fix stale VGICv3 comments in the nVHE world switch Fuad Tabba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox