Re: [PATCH v4] KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses

[email protected] Fri, 07 Aug 2026 20:17:08 +0000
Newsgroups dev.linux.lists.kvmarm
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] The `kvm->arch.nested_mmus` array is leaked if VM creation fails after `kvm_arch_init_vm()` completes successfully.

Pre-existing issues:
- [High] If `kvm->arch.nested_mmus_size` is 0 or `nested_mmus` is freed, a nested stage-2 fault causes a host kernel panic in `get_s2_mmu_nested()` due to a race condition with `kvm_arch_flush_shadow_all()`.
--

--- Patch [1]: [PATCH v4] KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses ---
commit 46aa9c715387bbae64560eea52655690ecd90bb6
Author: Karl Mehltretter <[email protected]>
Subject: KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses

This commit turns nested_mmus into a pointer table allocated once during VM
creation. The MMUs are allocated separately as VCPUs are initialized to keep
their addresses stable, fixing a slab-use-after-free issue.

> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index fb54f6dad995c..3c9b5c5b02d1f 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -43,11 +43,18 @@ struct vncr_tlb {
>   */
>  #define S2_MMU_PER_VCPU		2
>  
> -void kvm_init_nested(struct kvm *kvm)
> +int kvm_init_nested(struct kvm *kvm)
>  {
> -	kvm->arch.nested_mmus = NULL;
> +	kvm->arch.nested_mmus = kvcalloc(KVM_MAX_VCPUS * S2_MMU_PER_VCPU,
> +					 sizeof(*kvm->arch.nested_mmus),
> +					 GFP_KERNEL_ACCOUNT);
> +	if (!kvm->arch.nested_mmus)
> +		return -ENOMEM;
> +

[Severity: Medium]
Does this allocation leak if VM creation fails later in the process?

During kvm_create_vm(), kvm_arch_init_vm() calls kvm_init_nested() which
allocates kvm->arch.nested_mmus. If a subsequent initialization step in
kvm_create_vm() fails (e.g., hardware_enable_all() or
kvm_init_mmu_notifier()), the error path bypasses mmu_notifier_unregister()
and directly calls kvm_arch_destroy_vm().

Since kvm_arch_destroy_vm() on ARM64 does not free kvm->arch.nested_mmus,
could the array be permanently leaked on those error paths?

[ ... ]

> @@ -1272,10 +1279,10 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
>  	int i;
>  
>  	for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
> -		struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
> +		struct kvm_s2_mmu *mmu = kvm->arch.nested_mmus[i];
>  
>  		if (!WARN_ON(atomic_read(&mmu->refcnt)))
> -			kvm_free_stage2_pgd(mmu);
> +			free_nested_s2_mmu(mmu);
>  	}
>  	kvfree(kvm->arch.nested_mmus);
>  	kvm->arch.nested_mmus = NULL;

[Severity: High]
This is a pre-existing issue, but does this function have a race
condition with VCPU threads?

When a KVM process is killed, exit_mm() invokes mmu_notifier_release(), which
calls kvm_arch_flush_shadow_all(). This function frees kvm->arch.nested_mmus
and sets nested_mmus_size = 0 without acquiring mmu_lock.

Since file descriptors are not closed until later, VCPU threads might still be
executing. If a VCPU thread calls vcpu_load() -> kvm_vcpu_load_hw_mmu() ->
get_s2_mmu_nested(), it acquires mmu_lock but not the SRCU lock. It can read
the concurrently modified nested_mmus_size as 0, skip the lookup loop, and
execute BUG_ON(atomic_read(&s2_mmu->refcnt)) on an uninitialized or NULL
pointer.

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1