Re: [PATCH v2] KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses
[email protected] Thu, 06 Aug 2026 06:38:59 +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: - [Critical] Deterministic NULL pointer dereference and Use-After-Free race on the `nested_mmus` array after `kvm_arch_flush_shadow_all` executes. Pre-existing issues: - [Critical] Deterministic NULL pointer dereference in `get_s2_mmu_nested` when `nested_mmus_size` is 0. -- --- Patch [1]: [PATCH v2] KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses --- commit 70c0982ec06bd8ebb390c1b7ff665dae83887bb8 Author: Karl Mehltretter <[email protected]> KVM: arm64: nv: Keep the shadow S2 MMUs at fixed addresses This patch turns the nested_mmus array into a fixed-size pointer table allocated once during VM creation. The S2 MMUs are allocated individually during vCPU initialization to keep their addresses stable, fixing a slab-use-after-free issue. v1: https://lore.kernel.org/r/[email protected]/ > 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 > @@ -90,44 +104,44 @@ 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; > } > > ret = init_nested_s2_mmu(kvm, mmu); > if (ret) { > kfree(mmu); > goto err_free_vncr; > } > > kvm->arch.nested_mmus[kvm->arch.nested_mmus_size + i] = mmu; [Severity: Critical] Can this lead to a NULL pointer dereference or use-after-free? The patch shifts the allocation of kvm->arch.nested_mmus to VM creation in kvm_init_nested(), but kvm_arch_flush_shadow_all() still frees the array and sets the pointer to NULL and size to 0. If an unprivileged user process triggers kvm_mmu_notifier_release(), for example by passing the file descriptor to a child process and exiting, kvm_arch_flush_shadow_all() will free the array. A subsequent KVM_ARM_VCPU_INIT ioctl will bypass the size check in kvm_vcpu_init_nested() because num_mmus is greater than 0 and nested_mmus_size is 0. It will allocate an MMU and write it directly to kvm->arch.nested_mmus[kvm->arch.nested_mmus_size + i], which dereferences the now NULL pointer. Additionally, since this write happens outside mmu_lock, it could race with a concurrent kvm_arch_flush_shadow_all() execution and write into freed memory. [ ... ] > @@ -805,7 +819,7 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu) > for (i = kvm->arch.nested_mmus_next; > i < (kvm->arch.nested_mmus_size + kvm->arch.nested_mmus_next); > i++) { > - s2_mmu = &kvm->arch.nested_mmus[i % kvm->arch.nested_mmus_size]; > + s2_mmu = kvm->arch.nested_mmus[i % kvm->arch.nested_mmus_size]; > > if (atomic_read(&s2_mmu->refcnt) == 0) > break; > } [Severity: Critical] This is a pre-existing issue, but can this lead to a NULL pointer dereference when nested_mmus_size is 0? If kvm_arch_flush_shadow_all() executes while a vCPU is active, it sets kvm->arch.nested_mmus_size to 0. When the vCPU subsequently enters get_s2_mmu_nested(), lookup_s2_mmu() evaluates the empty array and returns NULL, assigning NULL to s2_mmu. The for loop condition i < (0 + next) evaluates to false, causing the loop to be completely bypassed. The function then executes BUG_ON(atomic_read(&s2_mmu->refcnt)) just below this loop, which dereferences the uninitialized or NULL s2_mmu pointer. This can be reliably triggered by terminating a VM process while it is executing nested virtualization workloads. -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1