mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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

* [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 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 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 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

* 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®