linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Fixes for pKVM NP-guest support
@ 2025-02-07 14:54 Quentin Perret
  2025-02-07 14:54 ` [PATCH 1/2] KVM: arm64: Improve error handling from check_host_shared_guest() Quentin Perret
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Quentin Perret @ 2025-02-07 14:54 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon
  Cc: Fuad Tabba, Vincent Donnefort, linux-arm-kernel, kvmarm,
	linux-kernel

Hi all,

Here are two patches to deal with a race between the handling of
permission faults and MMU notifiers with pKVM that I found by
inspection. Specifically, pKVM gets thoroughly confused when it doesn't
find a page mapped in its relax_perm path, while standard KVM deals
with that trivially thanks to the -EAGAIN special case in
user_mem_abort(). The second patch addresses the problem by simplifying
the implementation of multiple pKVM hypercalls, which also has the nice
side effect of improving locking by not taking the global host stage-2
lock as much.

Patches based on 6.14-rc1, tested in qemu on on Google Pixel 6.

Thanks!
Quentin

Quentin Perret (2):
  KVM: arm64: Improve error handling from check_host_shared_guest()
  KVM: arm64: Simplify np-guest hypercalls

 arch/arm64/kvm/hyp/nvhe/mem_protect.c | 73 +++++++++++++++------------
 1 file changed, 40 insertions(+), 33 deletions(-)

-- 
2.48.1.502.g6dc24dfdaf-goog



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] KVM: arm64: Improve error handling from check_host_shared_guest()
  2025-02-07 14:54 [PATCH 0/2] Fixes for pKVM NP-guest support Quentin Perret
@ 2025-02-07 14:54 ` Quentin Perret
  2025-02-07 14:54 ` [PATCH 2/2] KVM: arm64: Simplify np-guest hypercalls Quentin Perret
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Quentin Perret @ 2025-02-07 14:54 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon
  Cc: Fuad Tabba, Vincent Donnefort, linux-arm-kernel, kvmarm,
	linux-kernel

The check_host_shared_guest() path expects to find a last-level valid
PTE in the guest's stage-2 page-table. However, it checks the PTE's
level before its validity, which makes it hard for callers to figure out
what went wrong.

To make error handling simpler, check the PTE's validity first.

Signed-off-by: Quentin Perret <qperret@google.com>
---
 arch/arm64/kvm/hyp/nvhe/mem_protect.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 7ad7b133b81a..41847c04b270 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -943,10 +943,10 @@ static int __check_host_shared_guest(struct pkvm_hyp_vm *vm, u64 *__phys, u64 ip
 	ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, &level);
 	if (ret)
 		return ret;
-	if (level != KVM_PGTABLE_LAST_LEVEL)
-		return -E2BIG;
 	if (!kvm_pte_valid(pte))
 		return -ENOENT;
+	if (level != KVM_PGTABLE_LAST_LEVEL)
+		return -E2BIG;
 
 	state = guest_get_page_state(pte, ipa);
 	if (state != PKVM_PAGE_SHARED_BORROWED)
-- 
2.48.1.502.g6dc24dfdaf-goog



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] KVM: arm64: Simplify np-guest hypercalls
  2025-02-07 14:54 [PATCH 0/2] Fixes for pKVM NP-guest support Quentin Perret
  2025-02-07 14:54 ` [PATCH 1/2] KVM: arm64: Improve error handling from check_host_shared_guest() Quentin Perret
@ 2025-02-07 14:54 ` Quentin Perret
  2025-02-07 17:58 ` [PATCH 0/2] Fixes for pKVM NP-guest support Oliver Upton
  2025-02-09 10:21 ` Marc Zyngier
  3 siblings, 0 replies; 5+ messages in thread
From: Quentin Perret @ 2025-02-07 14:54 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon
  Cc: Fuad Tabba, Vincent Donnefort, linux-arm-kernel, kvmarm,
	linux-kernel

When the handling of a guest stage-2 permission fault races with an MMU
notifier, the faulting page might be gone from the guest's stage-2 by
the point we attempt to call (p)kvm_pgtable_stage2_relax_perms(). In the
normal KVM case, this leads to returning -EAGAIN which user_mem_abort()
handles correctly by simply re-entering the guest. However, the pKVM
hypercall implementation has additional logic to check the page state
using __check_host_shared_guest() which gets confused with absence of a
page mapped at the requested IPA and returns -ENOENT, hence breaking
user_mem_abort() and hilarity ensues.

Luckily, several of the hypercalls for managing the stage-2 page-table
of NP guests have no effect on the pKVM ownership tracking (wrprotect,
test_clear_young, mkyoung, and crucially relax_perms), so the extra
state checking logic is in fact not strictly necessary. So, to fix the
discrepancy between standard KVM and pKVM, let's just drop the
superfluous __check_host_shared_guest() logic from those hypercalls and
make the extra state checking a debug assertion dependent on
CONFIG_NVHE_EL2_DEBUG as we already do for other transitions.

Signed-off-by: Quentin Perret <qperret@google.com>
---
 arch/arm64/kvm/hyp/nvhe/mem_protect.c | 69 +++++++++++++++------------
 1 file changed, 38 insertions(+), 31 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 41847c04b270..4c2f6a6a2efe 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -998,63 +998,73 @@ int __pkvm_host_unshare_guest(u64 gfn, struct pkvm_hyp_vm *vm)
 	return ret;
 }
 
-int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot)
+static void assert_host_shared_guest(struct pkvm_hyp_vm *vm, u64 ipa)
 {
-	struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
-	u64 ipa = hyp_pfn_to_phys(gfn);
 	u64 phys;
 	int ret;
 
-	if (prot & ~KVM_PGTABLE_PROT_RWX)
-		return -EINVAL;
+	if (!IS_ENABLED(CONFIG_NVHE_EL2_DEBUG))
+		return;
 
 	host_lock_component();
 	guest_lock_component(vm);
 
 	ret = __check_host_shared_guest(vm, &phys, ipa);
-	if (!ret)
-		ret = kvm_pgtable_stage2_relax_perms(&vm->pgt, ipa, prot, 0);
 
 	guest_unlock_component(vm);
 	host_unlock_component();
 
-	return ret;
+	WARN_ON(ret && ret != -ENOENT);
 }
 
-int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *vm)
+int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot)
 {
+	struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
 	u64 ipa = hyp_pfn_to_phys(gfn);
-	u64 phys;
 	int ret;
 
-	host_lock_component();
-	guest_lock_component(vm);
+	if (pkvm_hyp_vm_is_protected(vm))
+		return -EPERM;
 
-	ret = __check_host_shared_guest(vm, &phys, ipa);
-	if (!ret)
-		ret = kvm_pgtable_stage2_wrprotect(&vm->pgt, ipa, PAGE_SIZE);
+	if (prot & ~KVM_PGTABLE_PROT_RWX)
+		return -EINVAL;
 
+	assert_host_shared_guest(vm, ipa);
+	guest_lock_component(vm);
+	ret = kvm_pgtable_stage2_relax_perms(&vm->pgt, ipa, prot, 0);
 	guest_unlock_component(vm);
-	host_unlock_component();
 
 	return ret;
 }
 
-int __pkvm_host_test_clear_young_guest(u64 gfn, bool mkold, struct pkvm_hyp_vm *vm)
+int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *vm)
 {
 	u64 ipa = hyp_pfn_to_phys(gfn);
-	u64 phys;
 	int ret;
 
-	host_lock_component();
+	if (pkvm_hyp_vm_is_protected(vm))
+		return -EPERM;
+
+	assert_host_shared_guest(vm, ipa);
 	guest_lock_component(vm);
+	ret = kvm_pgtable_stage2_wrprotect(&vm->pgt, ipa, PAGE_SIZE);
+	guest_unlock_component(vm);
 
-	ret = __check_host_shared_guest(vm, &phys, ipa);
-	if (!ret)
-		ret = kvm_pgtable_stage2_test_clear_young(&vm->pgt, ipa, PAGE_SIZE, mkold);
+	return ret;
+}
 
+int __pkvm_host_test_clear_young_guest(u64 gfn, bool mkold, struct pkvm_hyp_vm *vm)
+{
+	u64 ipa = hyp_pfn_to_phys(gfn);
+	int ret;
+
+	if (pkvm_hyp_vm_is_protected(vm))
+		return -EPERM;
+
+	assert_host_shared_guest(vm, ipa);
+	guest_lock_component(vm);
+	ret = kvm_pgtable_stage2_test_clear_young(&vm->pgt, ipa, PAGE_SIZE, mkold);
 	guest_unlock_component(vm);
-	host_unlock_component();
 
 	return ret;
 }
@@ -1063,18 +1073,15 @@ int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
 {
 	struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
 	u64 ipa = hyp_pfn_to_phys(gfn);
-	u64 phys;
 	int ret;
 
-	host_lock_component();
-	guest_lock_component(vm);
-
-	ret = __check_host_shared_guest(vm, &phys, ipa);
-	if (!ret)
-		kvm_pgtable_stage2_mkyoung(&vm->pgt, ipa, 0);
+	if (pkvm_hyp_vm_is_protected(vm))
+		return -EPERM;
 
+	assert_host_shared_guest(vm, ipa);
+	guest_lock_component(vm);
+	kvm_pgtable_stage2_mkyoung(&vm->pgt, ipa, 0);
 	guest_unlock_component(vm);
-	host_unlock_component();
 
 	return ret;
 }
-- 
2.48.1.502.g6dc24dfdaf-goog



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] Fixes for pKVM NP-guest support
  2025-02-07 14:54 [PATCH 0/2] Fixes for pKVM NP-guest support Quentin Perret
  2025-02-07 14:54 ` [PATCH 1/2] KVM: arm64: Improve error handling from check_host_shared_guest() Quentin Perret
  2025-02-07 14:54 ` [PATCH 2/2] KVM: arm64: Simplify np-guest hypercalls Quentin Perret
@ 2025-02-07 17:58 ` Oliver Upton
  2025-02-09 10:21 ` Marc Zyngier
  3 siblings, 0 replies; 5+ messages in thread
From: Oliver Upton @ 2025-02-07 17:58 UTC (permalink / raw)
  To: Quentin Perret
  Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Fuad Tabba, Vincent Donnefort,
	linux-arm-kernel, kvmarm, linux-kernel

On Fri, Feb 07, 2025 at 02:54:36PM +0000, Quentin Perret wrote:
> Hi all,
> 
> Here are two patches to deal with a race between the handling of
> permission faults and MMU notifiers with pKVM that I found by
> inspection. Specifically, pKVM gets thoroughly confused when it doesn't
> find a page mapped in its relax_perm path, while standard KVM deals
> with that trivially thanks to the -EAGAIN special case in
> user_mem_abort(). The second patch addresses the problem by simplifying
> the implementation of multiple pKVM hypercalls, which also has the nice
> side effect of improving locking by not taking the global host stage-2
> lock as much.
> 
> Patches based on 6.14-rc1, tested in qemu on on Google Pixel 6.

Looks reasonable to me, the locking improvements are certainly an added
bonus.

Reviewed-by: Oliver Upton <oliver.upton@linux.dev>

-- 
Thanks,
Oliver


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] Fixes for pKVM NP-guest support
  2025-02-07 14:54 [PATCH 0/2] Fixes for pKVM NP-guest support Quentin Perret
                   ` (2 preceding siblings ...)
  2025-02-07 17:58 ` [PATCH 0/2] Fixes for pKVM NP-guest support Oliver Upton
@ 2025-02-09 10:21 ` Marc Zyngier
  3 siblings, 0 replies; 5+ messages in thread
From: Marc Zyngier @ 2025-02-09 10:21 UTC (permalink / raw)
  To: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Catalin Marinas, Will Deacon, Quentin Perret
  Cc: Fuad Tabba, Vincent Donnefort, linux-arm-kernel, kvmarm,
	linux-kernel

On Fri, 07 Feb 2025 14:54:36 +0000, Quentin Perret wrote:
> Here are two patches to deal with a race between the handling of
> permission faults and MMU notifiers with pKVM that I found by
> inspection. Specifically, pKVM gets thoroughly confused when it doesn't
> find a page mapped in its relax_perm path, while standard KVM deals
> with that trivially thanks to the -EAGAIN special case in
> user_mem_abort(). The second patch addresses the problem by simplifying
> the implementation of multiple pKVM hypercalls, which also has the nice
> side effect of improving locking by not taking the global host stage-2
> lock as much.
> 
> [...]

Applied to fixes, thanks!

[1/2] KVM: arm64: Improve error handling from check_host_shared_guest()
      commit: c53fbdb60fb61fd6bda2bc0dc89837966625c5dc
[2/2] KVM: arm64: Simplify np-guest hypercalls
      commit: eabc7aaef7a553b64bf6e631ce04526af6c8d104

Cheers,

	M.
-- 
Without deviation from the norm, progress is not possible.




^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-02-09 10:24 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-07 14:54 [PATCH 0/2] Fixes for pKVM NP-guest support Quentin Perret
2025-02-07 14:54 ` [PATCH 1/2] KVM: arm64: Improve error handling from check_host_shared_guest() Quentin Perret
2025-02-07 14:54 ` [PATCH 2/2] KVM: arm64: Simplify np-guest hypercalls Quentin Perret
2025-02-07 17:58 ` [PATCH 0/2] Fixes for pKVM NP-guest support Oliver Upton
2025-02-09 10:21 ` Marc Zyngier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).