* [PATCH v2 0/2] KVM: arm64: Validate host pointers in __kvm_adjust_pc() under pKVM
@ 2026-09-15 7:04 Fuad Tabba
2026-09-15 7:04 ` [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba
2026-09-15 7:04 ` [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba
0 siblings, 2 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-09-15 7:04 UTC (permalink / raw)
To: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel
Cc: catalin.marinas, will, joey.gouly, seiden, suzuki.poulose,
yuzenghui, mark.rutland, steven.price, vdonnefort, qperret,
tabba
Hi folks,
Changes since v1 [1]:
- New patch 1: enter_exception64() reads the VM's MTE flag through a
host-writable vcpu->kvm that nothing validates. It goes first,
since pinning the vCPU alone leaves that read on an unmapped VM.
(sashiko)
- Dropped the VM pin the fix carried in v1, patch 1 validating that
read at its site.
Two host pointers reach EL2 unvalidated on the __kvm_adjust_pc() path
under pKVM, and this series fixes both. The host vCPU isn't mapped at
EL2 until its first KVM_RUN pins it, and KVM_SET_VCPU_EVENTS with
ext_dabt_pending reaches the hypercall before that. The vcpu->kvm the
exception entry reads the MTE flag from is host-writable, and nothing
checks it. Either one panics the hypervisor, and on MTE-capable
hardware the flag read leaks one bit of hyp memory the host chose,
through PSR_TCO.
These are patches 2 and 3 of the pKVM core series [2], which carries
them so that it applies as is. They're respun here so they can be
applied on their own.
Based on v7.3-rc3 (fd73f4a665989).
Cheers,
/fuad
[1] https://lore.kernel.org/all/20260914065136.3418404-1-fuad.tabba@linux.dev/
[2] https://lore.kernel.org/all/20260914113338.159227-1-fuad.tabba@linux.dev/
Fuad Tabba (2):
KVM: arm64: Validate the host vCPU's VM before reading it under pKVM
KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM
arch/arm64/kvm/hyp/exception.c | 5 ++-
arch/arm64/kvm/hyp/include/hyp/adjust_pc.h | 18 ++++++++++
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 19 ++++++++++-
arch/arm64/kvm/hyp/nvhe/pkvm.c | 39 ++++++++++++++++++++++
4 files changed, 79 insertions(+), 2 deletions(-)
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it under pKVM 2026-09-15 7:04 [PATCH v2 0/2] KVM: arm64: Validate host pointers in __kvm_adjust_pc() under pKVM Fuad Tabba @ 2026-09-15 7:04 ` Fuad Tabba 2026-09-15 7:34 ` Vincent Donnefort 2026-09-15 10:17 ` Joey Gouly 2026-09-15 7:04 ` [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba 1 sibling, 2 replies; 10+ messages in thread From: Fuad Tabba @ 2026-09-15 7:04 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel Cc: catalin.marinas, will, joey.gouly, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret, tabba On an MTE-capable host under pKVM, enter_exception64() reads the VM's MTE flag through vcpu->kvm, which for a host vCPU is a host-writable pointer nothing validates. The host can point it at any address in the hyp linear map and read back bit 1 of that word through PSR_TCO in the vCPU's CPSR, or panic the hypervisor with an unmapped one. Get the VM through a get/put pair around the read: a loaded vCPU's is the hyp VM, an unloaded host vCPU's is read once and pinned, and a pointer the host never shared leaves TCO clear. Fixes: ea7fc1bb1cd1b ("KVM: arm64: Introduce MTE VM feature") Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://lore.kernel.org/all/20260914070536.877D91F000FF@smtp.kernel.org/ Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> --- arch/arm64/kvm/hyp/exception.c | 5 ++- arch/arm64/kvm/hyp/include/hyp/adjust_pc.h | 18 ++++++++++ arch/arm64/kvm/hyp/nvhe/pkvm.c | 39 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c index 754e2dc1df54a..6e60d890afa4a 100644 --- a/arch/arm64/kvm/hyp/exception.c +++ b/arch/arm64/kvm/hyp/exception.c @@ -70,6 +70,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, enum exception_type type) { unsigned long sctlr, vbar, old, new, mode; + struct kvm *kvm; u64 exc_offset; mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT); @@ -109,8 +110,10 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, new |= (old & PSR_C_BIT); new |= (old & PSR_V_BIT); - if (kvm_has_mte(kern_hyp_va(vcpu->kvm))) + kvm = vcpu_get_kvm(vcpu); + if (kvm && kvm_has_mte(kvm)) new |= PSR_TCO_BIT; + vcpu_put_kvm(vcpu, kvm); new |= (old & PSR_DIT_BIT); diff --git a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h index 4fdfeabefeb43..a4fb04faa7d09 100644 --- a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h +++ b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h @@ -13,6 +13,24 @@ #include <asm/kvm_emulate.h> #include <asm/kvm_host.h> +/* + * Under pKVM a host vCPU's ->kvm is host-writable: the nVHE pair + * validates it. + */ +#ifdef __KVM_NVHE_HYPERVISOR__ +struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu); +void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm); +#else +static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu) +{ + return vcpu->kvm; +} + +static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm) +{ +} +#endif + static inline void kvm_skip_instr(struct kvm_vcpu *vcpu) { if (vcpu_mode_is_32bit(vcpu)) { diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 459bd9eb7e4bc..9bdc7a9b84b8c 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -11,6 +11,8 @@ #include <asm/kvm_emulate.h> +#include <hyp/adjust_pc.h> + #include <nvhe/mem_protect.h> #include <nvhe/memory.h> #include <nvhe/pkvm.h> @@ -304,6 +306,43 @@ struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void) } +static struct pkvm_hyp_vm *loaded_hyp_vm_of(struct kvm_vcpu *vcpu) +{ + struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); + + if (hyp_vcpu && + (vcpu == &hyp_vcpu->vcpu || vcpu == hyp_vcpu->host_vcpu)) + return pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + + return NULL; +} + +/* An unloaded host vCPU's VM is mapped at EL2 only while pinned. */ +struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu) +{ + struct pkvm_hyp_vm *hyp_vm; + struct kvm *kvm; + + if (!is_protected_kvm_enabled()) + return kern_hyp_va(vcpu->kvm); + + hyp_vm = loaded_hyp_vm_of(vcpu); + if (hyp_vm) + return &hyp_vm->kvm; + + kvm = kern_hyp_va(READ_ONCE(vcpu->kvm)); + if (hyp_pin_shared_mem(kvm, kvm + 1)) + return NULL; + + return kvm; +} + +void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm) +{ + if (kvm && is_protected_kvm_enabled() && !loaded_hyp_vm_of(vcpu)) + hyp_unpin_shared_mem(kvm, kvm + 1); +} + struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle) { struct pkvm_hyp_vm *hyp_vm; -- 2.39.5 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it under pKVM 2026-09-15 7:04 ` [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba @ 2026-09-15 7:34 ` Vincent Donnefort 2026-09-15 10:03 ` Fuad Tabba 2026-09-15 10:17 ` Joey Gouly 1 sibling, 1 reply; 10+ messages in thread From: Vincent Donnefort @ 2026-09-15 7:34 UTC (permalink / raw) To: Fuad Tabba Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, joey.gouly, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, qperret, tabba On Tue, Sep 15, 2026 at 08:04:17AM +0100, Fuad Tabba wrote: > On an MTE-capable host under pKVM, enter_exception64() reads the VM's > MTE flag through vcpu->kvm, which for a host vCPU is a host-writable > pointer nothing validates. The host can point it at any address in the > hyp linear map and read back bit 1 of that word through PSR_TCO in the > vCPU's CPSR, or panic the hypervisor with an unmapped one. > > Get the VM through a get/put pair around the read: a loaded vCPU's is > the hyp VM, an unloaded host vCPU's is read once and pinned, and a > pointer the host never shared leaves TCO clear. > > Fixes: ea7fc1bb1cd1b ("KVM: arm64: Introduce MTE VM feature") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/all/20260914070536.877D91F000FF@smtp.kernel.org/ > Cc: stable@vger.kernel.org > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> > --- > arch/arm64/kvm/hyp/exception.c | 5 ++- > arch/arm64/kvm/hyp/include/hyp/adjust_pc.h | 18 ++++++++++ > arch/arm64/kvm/hyp/nvhe/pkvm.c | 39 ++++++++++++++++++++++ > 3 files changed, 61 insertions(+), 1 deletion(-) > > diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c > index 754e2dc1df54a..6e60d890afa4a 100644 > --- a/arch/arm64/kvm/hyp/exception.c > +++ b/arch/arm64/kvm/hyp/exception.c > @@ -70,6 +70,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, > enum exception_type type) > { > unsigned long sctlr, vbar, old, new, mode; > + struct kvm *kvm; > u64 exc_offset; > > mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT); > @@ -109,8 +110,10 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, > new |= (old & PSR_C_BIT); > new |= (old & PSR_V_BIT); > > - if (kvm_has_mte(kern_hyp_va(vcpu->kvm))) > + kvm = vcpu_get_kvm(vcpu); > + if (kvm && kvm_has_mte(kvm)) > new |= PSR_TCO_BIT; > + vcpu_put_kvm(vcpu, kvm); > > new |= (old & PSR_DIT_BIT); > > diff --git a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h > index 4fdfeabefeb43..a4fb04faa7d09 100644 > --- a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h > +++ b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h > @@ -13,6 +13,24 @@ > #include <asm/kvm_emulate.h> > #include <asm/kvm_host.h> > > +/* > + * Under pKVM a host vCPU's ->kvm is host-writable: the nVHE pair > + * validates it. > + */ > +#ifdef __KVM_NVHE_HYPERVISOR__ > +struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu); > +void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm); > +#else > +static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu) > +{ > + return vcpu->kvm; > +} > + > +static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm) > +{ > +} > +#endif > + > static inline void kvm_skip_instr(struct kvm_vcpu *vcpu) > { > if (vcpu_mode_is_32bit(vcpu)) { > diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c > index 459bd9eb7e4bc..9bdc7a9b84b8c 100644 > --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c > +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c > @@ -11,6 +11,8 @@ > > #include <asm/kvm_emulate.h> > > +#include <hyp/adjust_pc.h> > + > #include <nvhe/mem_protect.h> > #include <nvhe/memory.h> > #include <nvhe/pkvm.h> > @@ -304,6 +306,43 @@ struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void) > > } > > +static struct pkvm_hyp_vm *loaded_hyp_vm_of(struct kvm_vcpu *vcpu) pkvm_get_loaded_hyp_vm()? to align with the other. > +{ > + struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); > + > + if (hyp_vcpu && > + (vcpu == &hyp_vcpu->vcpu || vcpu == hyp_vcpu->host_vcpu)) > + return pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); > + > + return NULL; > +} > + > +/* An unloaded host vCPU's VM is mapped at EL2 only while pinned. */ Could we get the last paragraph of the commit here? As this describes quite well what this is doing. > +struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu) > +{ > + struct pkvm_hyp_vm *hyp_vm; > + struct kvm *kvm; > + > + if (!is_protected_kvm_enabled()) > + return kern_hyp_va(vcpu->kvm); > + > + hyp_vm = loaded_hyp_vm_of(vcpu); > + if (hyp_vm) > + return &hyp_vm->kvm; > + > + kvm = kern_hyp_va(READ_ONCE(vcpu->kvm)); > + if (hyp_pin_shared_mem(kvm, kvm + 1)) > + return NULL; > + > + return kvm; > +} > + > +void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm) > +{ > + if (kvm && is_protected_kvm_enabled() && !loaded_hyp_vm_of(vcpu)) > + hyp_unpin_shared_mem(kvm, kvm + 1); > +} > + It seems strange for pkvm.c to provides function that aren't prefixed with pkvm_ and declared into adjust_pc.h and not pkvm.h. I wonder if adjust_pc.h should't just struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu) { if (is_protected_kvm_enabled()) return pkvm_vcpu_get_kvm() else kern_hyp_va(vcpu->kvm); } > struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle) > { > struct pkvm_hyp_vm *hyp_vm; > -- > 2.39.5 > Beside those nits: Reviewed-by: Vincent Donnefort <vdonnefort@google.com> -- Vincent ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it under pKVM 2026-09-15 7:34 ` Vincent Donnefort @ 2026-09-15 10:03 ` Fuad Tabba 0 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-09-15 10:03 UTC (permalink / raw) To: Vincent Donnefort Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, joey.gouly, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, qperret On Tue, 15 Sept 2026 at 08:34, Vincent Donnefort <vdonnefort@google.com> wrote: [...] > Beside those nits: > > Reviewed-by: Vincent Donnefort <vdonnefort@google.com> Nits addressed, and I'll respin this as part of a consolidated series, as requested by Marc. Thanks for the review! /fuad > -- > Vincent ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it under pKVM 2026-09-15 7:04 ` [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba 2026-09-15 7:34 ` Vincent Donnefort @ 2026-09-15 10:17 ` Joey Gouly 2026-09-15 10:23 ` Fuad Tabba 1 sibling, 1 reply; 10+ messages in thread From: Joey Gouly @ 2026-09-15 10:17 UTC (permalink / raw) To: Fuad Tabba Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret, tabba Hi, On Tue, Sep 15, 2026 at 08:04:17AM +0100, Fuad Tabba wrote: > On an MTE-capable host under pKVM, enter_exception64() reads the VM's > MTE flag through vcpu->kvm, which for a host vCPU is a host-writable > pointer nothing validates. The host can point it at any address in the > hyp linear map and read back bit 1 of that word through PSR_TCO in the > vCPU's CPSR, or panic the hypervisor with an unmapped one. > > Get the VM through a get/put pair around the read: a loaded vCPU's is > the hyp VM, an unloaded host vCPU's is read once and pinned, and a > pointer the host never shared leaves TCO clear. > > Fixes: ea7fc1bb1cd1b ("KVM: arm64: Introduce MTE VM feature") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/all/20260914070536.877D91F000FF@smtp.kernel.org/ > Cc: stable@vger.kernel.org > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> > --- > arch/arm64/kvm/hyp/exception.c | 5 ++- > arch/arm64/kvm/hyp/include/hyp/adjust_pc.h | 18 ++++++++++ > arch/arm64/kvm/hyp/nvhe/pkvm.c | 39 ++++++++++++++++++++++ > 3 files changed, 61 insertions(+), 1 deletion(-) > > diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c > index 754e2dc1df54a..6e60d890afa4a 100644 > --- a/arch/arm64/kvm/hyp/exception.c > +++ b/arch/arm64/kvm/hyp/exception.c > @@ -70,6 +70,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, > enum exception_type type) > { > unsigned long sctlr, vbar, old, new, mode; > + struct kvm *kvm; > u64 exc_offset; > > mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT); > @@ -109,8 +110,10 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, > new |= (old & PSR_C_BIT); > new |= (old & PSR_V_BIT); > > - if (kvm_has_mte(kern_hyp_va(vcpu->kvm))) > + kvm = vcpu_get_kvm(vcpu); > + if (kvm && kvm_has_mte(kvm)) It's fine to use the "host->kvm" here because even if sets this bit on the host vCPU, the per-entry handlers in the other series won't copy across the TCO bit. Right? > new |= PSR_TCO_BIT; > + vcpu_put_kvm(vcpu, kvm); > > new |= (old & PSR_DIT_BIT); > [snip] Agree with the nits that Vincent suggested! Let's see if this sticks this time: Reviewed-by: Joey Gouly <joey.gouly@arm.com> Thanks, Joey ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it under pKVM 2026-09-15 10:17 ` Joey Gouly @ 2026-09-15 10:23 ` Fuad Tabba 0 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-09-15 10:23 UTC (permalink / raw) To: Joey Gouly Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret Hi Joey, On Tue, 15 Sept 2026 at 11:17, Joey Gouly <joey.gouly@arm.com> wrote: [...] > > - if (kvm_has_mte(kern_hyp_va(vcpu->kvm))) > > + kvm = vcpu_get_kvm(vcpu); > > + if (kvm && kvm_has_mte(kvm)) > > It's fine to use the "host->kvm" here because even if sets this bit on > the host vCPU, the per-entry handlers in the other series won't copy > across the TCO bit. > Right? Yes. > > new |= PSR_TCO_BIT; > > + vcpu_put_kvm(vcpu, kvm); > > > > new |= (old & PSR_DIT_BIT); > > > > [snip] > > Agree with the nits that Vincent suggested! > > Let's see if this sticks this time: > Reviewed-by: Joey Gouly <joey.gouly@arm.com> Carrying it over for the respin, thanks again! /fuad > Thanks, > Joey ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM 2026-09-15 7:04 [PATCH v2 0/2] KVM: arm64: Validate host pointers in __kvm_adjust_pc() under pKVM Fuad Tabba 2026-09-15 7:04 ` [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba @ 2026-09-15 7:04 ` Fuad Tabba 2026-09-15 7:41 ` Vincent Donnefort 2026-09-15 10:47 ` Joey Gouly 1 sibling, 2 replies; 10+ messages in thread From: Fuad Tabba @ 2026-09-15 7:04 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel Cc: catalin.marinas, will, joey.gouly, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret, tabba Under pKVM, a page the host shares with EL2 is mapped at EL2 only while it's pinned, and the host vCPU is pinned at its 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. A loaded hyp vCPU implies it's 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@vger.kernel.org Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 19 ++++++++++++++++++- 1 file changed, 18 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..4cb7347db2514 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -542,8 +542,25 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt) 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); + return; + } + + /* + * With no hyp vCPU loaded for it, the host vCPU may be unpinned, + * and so unmapped at EL2: its first run pins it. + */ + host_vcpu = kern_hyp_va(vcpu); + if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1)) + return; + + __kvm_adjust_pc(host_vcpu); + hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1); } static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt) -- 2.39.5 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM 2026-09-15 7:04 ` [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba @ 2026-09-15 7:41 ` Vincent Donnefort 2026-09-15 10:47 ` Joey Gouly 1 sibling, 0 replies; 10+ messages in thread From: Vincent Donnefort @ 2026-09-15 7:41 UTC (permalink / raw) To: Fuad Tabba Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, joey.gouly, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, qperret, tabba On Tue, Sep 15, 2026 at 08:04:18AM +0100, Fuad Tabba wrote: > Under pKVM, a page the host shares with EL2 is mapped at EL2 only while > it's pinned, and the host vCPU is pinned at its 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. A loaded hyp vCPU implies it's 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@vger.kernel.org > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> > --- > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 19 ++++++++++++++++++- > 1 file changed, 18 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..4cb7347db2514 100644 > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c > @@ -542,8 +542,25 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt) > 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); > + return; > + } > + > + /* > + * With no hyp vCPU loaded for it, the host vCPU may be unpinned, > + * and so unmapped at EL2: its first run pins it. > + */ > + host_vcpu = kern_hyp_va(vcpu); > + if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1)) > + return; > + > + __kvm_adjust_pc(host_vcpu); > + hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1); > } > > static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt) > -- > 2.39.5 > Reviewed-by: Vincent Donnefort <vdonnefor@google.com> -- Vincent ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM 2026-09-15 7:04 ` [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba 2026-09-15 7:41 ` Vincent Donnefort @ 2026-09-15 10:47 ` Joey Gouly 2026-09-15 11:00 ` Fuad Tabba 1 sibling, 1 reply; 10+ messages in thread From: Joey Gouly @ 2026-09-15 10:47 UTC (permalink / raw) To: Fuad Tabba Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret, tabba Hi, On Tue, Sep 15, 2026 at 08:04:18AM +0100, Fuad Tabba wrote: > Under pKVM, a page the host shares with EL2 is mapped at EL2 only while > it's pinned, and the host vCPU is pinned at its 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. A loaded hyp vCPU implies it's 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@vger.kernel.org > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> > --- > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 19 ++++++++++++++++++- > 1 file changed, 18 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..4cb7347db2514 100644 > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c > @@ -542,8 +542,25 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt) > 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); > + return; > + } > + > + /* > + * With no hyp vCPU loaded for it, the host vCPU may be unpinned, > + * and so unmapped at EL2: its first run pins it. * A pin only fails if the memory was never shared, drop the request if * it is a bad host pointer. Or something like the commit message says. > + */ > + host_vcpu = kern_hyp_va(vcpu); > + if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1)) > + return; > + > + __kvm_adjust_pc(host_vcpu); > + hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1); > } > > static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt) Reviewed-by: Joey Gouly <joey.gouly@arm.com> Thanks, Joey ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM 2026-09-15 10:47 ` Joey Gouly @ 2026-09-15 11:00 ` Fuad Tabba 0 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-09-15 11:00 UTC (permalink / raw) To: Joey Gouly Cc: maz, oupton, kvmarm, linux-arm-kernel, linux-kernel, catalin.marinas, will, seiden, suzuki.poulose, yuzenghui, mark.rutland, steven.price, vdonnefort, qperret Hi Joey, On Tue, 15 Sept 2026 at 11:48, Joey Gouly <joey.gouly@arm.com> wrote: [...] > * A pin only fails if the memory was never shared, drop the request if > * it is a bad host pointer. > > Or something like the commit message says. Ack. > > + */ > > + host_vcpu = kern_hyp_va(vcpu); > > + if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1)) > > + return; > > + > > + __kvm_adjust_pc(host_vcpu); > > + hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1); > > } > > > > static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt) > > Reviewed-by: Joey Gouly <joey.gouly@arm.com> Thanks! /fuad > > Thanks, > Joey ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-09-15 11:01 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-15 7:04 [PATCH v2 0/2] KVM: arm64: Validate host pointers in __kvm_adjust_pc() under pKVM Fuad Tabba 2026-09-15 7:04 ` [PATCH v2 1/2] KVM: arm64: Validate the host vCPU's VM before reading it " Fuad Tabba 2026-09-15 7:34 ` Vincent Donnefort 2026-09-15 10:03 ` Fuad Tabba 2026-09-15 10:17 ` Joey Gouly 2026-09-15 10:23 ` Fuad Tabba 2026-09-15 7:04 ` [PATCH v2 2/2] KVM: arm64: Pin the host vCPU before adjusting its PC " Fuad Tabba 2026-09-15 7:41 ` Vincent Donnefort 2026-09-15 10:47 ` Joey Gouly 2026-09-15 11:00 ` Fuad Tabba
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®