[PATCH] KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM

From: Fuad Tabba

Date: Mon Sep 14 2026 - 02:54:40 EST


Under pKVM, a page the host shares with EL2 is mapped at EL2 only while
it's pinned, and the host vCPU and its VM are pinned at the vCPU's
first KVM_RUN. KVM_SET_VCPU_EVENTS with ext_dabt_pending reaches
__kvm_adjust_pc() at EL2 before that, and dereferencing the unmapped
host vCPU panics the hypervisor. Any process holding /dev/kvm on a
pKVM host can trigger it.

Pin the host vCPU around the adjustment when no hyp vCPU is loaded for
it, and its VM too, for the MTE flag the exception entry reads. A
loaded hyp vCPU implies both are pinned. A pin fails only for memory
the host never shared, and the request is then dropped like any other
bad host pointer.

Fixes: efa1368ba9f4b ("KVM: arm64: Commit exceptions from KVM_SET_VCPU_EVENTS immediately")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Fuad Tabba <fuad.tabba@xxxxxxxxx>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 9a3b92e626adb..e41b4c181eb62 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -539,11 +539,39 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt)
cpu_reg(host_ctxt, 1) = ret;
}

+/*
+ * With no hyp vCPU loaded for it, the host vCPU and its VM may be unpinned,
+ * and so unmapped at EL2: both are pinned from the vCPU's first run, and
+ * KVM_SET_VCPU_EVENTS can reach here before that.
+ */
+static void adjust_pc_unloaded(struct kvm_vcpu *host_vcpu)
+{
+ struct kvm *host_kvm;
+
+ if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1))
+ return;
+
+ /* The exception entry reads the VM's MTE flag. */
+ host_kvm = kern_hyp_va(host_vcpu->kvm);
+ if (!hyp_pin_shared_mem(host_kvm, host_kvm + 1)) {
+ __kvm_adjust_pc(host_vcpu);
+ hyp_unpin_shared_mem(host_kvm, host_kvm + 1);
+ }
+
+ hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1);
+}
+
static void handle___kvm_adjust_pc(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+ struct kvm_vcpu *host_vcpu;

- __kvm_adjust_pc(kern_hyp_va(vcpu));
+ host_vcpu = __get_host_hyp_vcpus(vcpu, &hyp_vcpu);
+ if (host_vcpu)
+ __kvm_adjust_pc(host_vcpu);
+ else
+ adjust_pc_unloaded(kern_hyp_va(vcpu));
}

static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt)

base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
--
2.39.5