VYPR
Unrated severityNVD Advisory· Published Jun 9, 2026

CVE-2026-46317

CVE-2026-46317

Description

Linux kernel KVM vulnerability allows use-after-free in nested MMU handling, potentially leading to system instability or compromise.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Linux kernel KVM vulnerability allows use-after-free in nested MMU handling, potentially leading to system instability or compromise.

Vulnerability

The Linux kernel's Kernel-based Virtual Machine (KVM) component contains a use-after-free vulnerability in the arm64 architecture. The kvm->arch.nested_mmus[] array can be reallocated and freed while a walker, potentially running from the MMU notifier path, is still referencing the old buffer. This race condition affects the handling of nested virtual machine memory management units [1].

Exploitation

An attacker with the ability to trigger memory unmapping operations within a guest VM could potentially exploit this vulnerability. The race condition occurs when kvm_vcpu_init_nested() reallocates the nested_mmus array under kvm->arch.config_lock, while a walker operating under kvm->mmu_lock is active. If the walker attempts to access the array after it has been freed, a use-after-free condition occurs [1].

Impact

Successful exploitation of this vulnerability could lead to system instability, crashes, or potentially arbitrary code execution within the context of the host kernel. The use-after-free condition allows an attacker to corrupt memory structures, leading to unpredictable behavior and a potential security compromise of the host system [1].

Mitigation

This vulnerability has been resolved in the Linux kernel. The fix involves reassigning the nested_mmus array behind the mmu_lock by allocating the new array outside the lock, copying entries, fixing back pointers, and then reassigning the array before freeing the old buffer after dropping the lock. Users should update to a patched version of the Linux kernel. No specific version information or patch release date is available in the provided references [1].

AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

6
918450ad6010

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index f04cda40545b1..df956559fd064 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    
4424dbcb06d6

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index fb2b2bcc5dc26..f7092e9a6fa24 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    
70543358fa08

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index 38f672e940878..6f7bc9a9992e0 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    
4424dbcb06d6

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index fb2b2bcc5dc26..f7092e9a6fa24 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    
70543358fa08

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index 38f672e940878..6f7bc9a9992e0 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    
918450ad6010

KVM: arm64: Reassign nested_mmus array behind mmu_lock

1 file changed · +20 14
  • arch/arm64/kvm/nested.c+20 14 modified
    diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
    index f04cda40545b1..df956559fd064 100644
    --- a/arch/arm64/kvm/nested.c
    +++ b/arch/arm64/kvm/nested.c
    @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
     	 * again, and there is no reason to affect the whole VM for this.
     	 */
     	num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
    -	tmp = kvrealloc(kvm->arch.nested_mmus,
    -			size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
    -			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    -	if (!tmp)
    -		return -ENOMEM;
     
    -	swap(kvm->arch.nested_mmus, tmp);
    +	if (num_mmus > kvm->arch.nested_mmus_size) {
    +		tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT);
    +		if (!tmp)
    +			return -ENOMEM;
     
    -	/*
    -	 * If we went through a realocation, adjust the MMU back-pointers in
    -	 * the previously initialised kvm_pgtable structures.
    -	 */
    -	if (kvm->arch.nested_mmus != tmp)
    -		for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    -			kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i];
    +		write_lock(&kvm->mmu_lock);
    +
    +		if (kvm->arch.nested_mmus_size) {
    +			memcpy(tmp, kvm->arch.nested_mmus,
    +			       size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size));
    +
    +			for (int i = 0; i < kvm->arch.nested_mmus_size; i++)
    +				tmp[i].pgt->mmu = &tmp[i];
    +		}
    +
    +		swap(kvm->arch.nested_mmus, tmp);
    +
    +		write_unlock(&kvm->mmu_lock);
    +
    +		kvfree(tmp);
    +	}
     
     	for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++)
     		ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]);
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"The reallocation of the nested MMU array occurred without proper locking, allowing concurrent access to a freed buffer."

Attack vector

An attacker can trigger this vulnerability by causing the reallocation of the `kvm->arch.nested_mmus` array. This reallocation happens during `kvm_vcpu_init_nested()` when the number of required MMUs increases. If an MMU notifier path, such as `kvm_nested_s2_unmap()`, is concurrently accessing the `kvm->arch.nested_mmus` array under the `kvm->mmu_lock`, it may reference freed memory.

Affected code

The vulnerability lies within the `kvm_vcpu_init_nested()` function in `arch/arm64/kvm/nested.c`. Specifically, the reallocation and freeing of the `kvm->arch.nested_mmus` array were not adequately protected by the `kvm->mmu_lock`, leading to a race condition with paths that walk this array under the same lock.

What the fix does

The patch modifies `kvm_vcpu_init_nested()` to allocate the new `nested_mmus` array outside of the `mmu_lock`. The existing entries are then copied to the new array, back-pointers are fixed, and the array is reassigned under the `mmu_lock`. Finally, the old buffer is freed after the lock is released. This ensures that the array is not accessed while it is being reallocated and freed, preventing a race condition [patch_id=5354443].

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

1