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

[email protected] Sat, 08 Aug 2026 00:59:13 +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:
- [High] `kvm_arch_flush_shadow_all` incorrectly frees the fixed `nested_mmus` pointer table, causing a guaranteed NULL pointer dereference on subsequent VCPU initialization.
- [Low] Inconsistent error path label ordering in `kvm_vcpu_init_nested` bypasses the cleanup of `vncr_array` if MMU object allocation fails.
--

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

KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses

This commit refactors nested S2 MMUs to use a fixed-size pointer table
allocated during VM creation. The MMU objects are allocated separately so
their addresses remain stable, fixing a use-after-free bug.

> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
[ ... ]
> @@ -82,52 +95,46 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
[ ... ]
> +	for (i = 0; i < S2_MMU_PER_VCPU; i++) {
> +		mmu = kzalloc_obj(*mmu, GFP_KERNEL_ACCOUNT);
> +		if (!mmu) {
> +			ret = -ENOMEM;
> +			goto err_free_mmus;
>  		}
[ ... ]
> +err_free_vncr:
> +	free_page((unsigned long)vcpu->arch.ctxt.vncr_array);
> +	vcpu->arch.ctxt.vncr_array = NULL;
>  
> -	kvm->arch.nested_mmus_size = num_mmus;
> +err_free_mmus:
> +	while (i--)
> +		free_nested_s2_mmu(kvm->arch.nested_mmus[kvm->arch.nested_mmus_size + i]);
>  
> -	return 0;
> +	return ret;
>  }

[Severity: Low]
Does this error path unwind in the correct order?

If kzalloc_obj() fails during initialization, the code jumps to the
err_free_mmus label, which completely bypasses the cleanup in err_free_vncr.

This leaves the newly allocated vncr_array anchored to
vcpu->arch.ctxt.vncr_array instead of freeing it immediately. While it will
eventually be freed when the VCPU is destroyed, this seems to create an
inconsistent state compared to the other error paths in this function.

[ ... ]
> @@ -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]
Can this lead to a NULL pointer dereference on subsequent VCPU
initialization?

The patch redesigns nested_mmus to be a fixed-size pointer table allocated
once at VM creation. However, it appears this legacy cleanup remains, freeing
and nulling kvm->arch.nested_mmus when kvm_arch_flush_shadow_all() runs.

If a VM's file descriptor is kept alive by another process, and a
subsequent KVM_ARM_VCPU_INIT ioctl is issued, kvm_vcpu_init_nested() will
attempt to append to the array:

    kvm->arch.nested_mmus[kvm->arch.nested_mmus_size + i] = mmu;

Since the dynamic reallocation logic was removed in this patch, nested_mmus
will still be NULL here, triggering a kernel panic. Should this teardown
path avoid freeing the array itself now?

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