KVM: arm64: Restore the VM's feature bitmap when kvm_setup_vcpu() fails
Fuad Tabba
reviewerPATCH v2 1/1 KVM: arm64: Restore the VM's feature bitmap when kvm_setup_vcpu() fails
__kvm_vcpu_set_target() copies the requested features into the VM-wide bitmap before kvm_setup_vcpu() runs and doesn't undo it when setup fails, so a rejected KVM_ARM_VCPU_INIT leaves the VM recording features that were never set up. With HAS_EL2 | HAS_EL2_E2H0 on a host without FEAT_NV1, kvm_vcpu_init_nested() returns -EINVAL before it allocates any nested stage-2 MMU, and vcpu_has_nv() is then true with nested_mmus_size == 0; its -ENOMEM paths do the same on a VM's first INIT.
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables KVM_PRE_FAULT_MEMORY for arm64 exposes
it: the generic kvm_vcpu_pre_fault_memory() calls vcpu_load() whether or not the vCPU has been initialised. After the rejected INIT, the first call's vcpu_load() finds hw_mmu still set to the canonical MMU and leaves it alone, but its vcpu_put() takes the vcpu_has_nv() branch into kvm_vcpu_put_hw_mmu(), which clears hw_mmu. The second call's vcpu_load() finds hw_mmu NULL and takes the nested branch into get_s2_mmu_nested(), whose search over nested_mmus_size == 0 leaves s2_mmu NULL for the BUG_ON(atomic_read(&s2_mmu->refcnt)), under mmu_lock. On kvmarm/next with the series applied:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000074 Call trace: kvm_vcpu_load_hw_mmu (arch/arm64/kvm/nested.c:891) (P) kvm_arch_vcpu_load (arch/arm64/kvm/arm.c:662) kvm_vcpu_pre_fault_memory (virt/kvm/kvm_main.c:170 virt/kvm/kvm_main.c:4349) kvm_vcpu_ioctl (virt/kvm/kvm_main.c:4639)
Setup reads the VM-wide bitmap, so the copy can't be deferred; restore the previous value instead when kvm_setup_vcpu() fails.
---
v2:
- Commit message: say what the prefault series enables, bring the two-ioctl walk and the trace up from below the fold, the trace decoded, and drop the Fixes: on 1de10b7d13a97, which had nothing fallible after the copy (Lorenzo).
- Fold Lorenzo's Reviewed-by.
Reproduced on kvmarm/next plus the series under QEMU (-cpu max with an Apple M2 MIDR, which has_nv1() denies; kvm-arm.mode=nested):KVM_ARM_VCPU_INIT with HAS_EL2 | HAS_EL2_E2H0 returns -EINVAL, then KVM_PRE_FAULT_MEMORY twice. With the fix both calls return -ENOENT and the host is unaffected. Applies unchanged to v7.3-rc3.
v1: https://lore.kernel.org/r/[email protected]/
arch/arm64/kvm/arm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
arch/arm64/kvm/arm.c +6 -1
@@ -1685,6 +1685,7 @@ static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)static int __kvm_vcpu_set_target (struct kvm_vcpu *vcpu,const struct kvm_vcpu_init *init) { +DECLARE_BITMAP (old_features,KVM_VCPU_MAX_FEATURES );unsigned long features = init->features[0 ];struct kvm *kvm = vcpu->kvm;int ret = -EINVAL ;
@@ -1695,11 +1696,15 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, kvm_vcpu_init_changed(vcpu, init)) goto out_unlock; +/* Setup reads the VM-wide bitmap, so undo the copy if setup fails. */ +bitmap_copy (old_features, kvm->arch.vcpu_features,KVM_VCPU_MAX_FEATURES );bitmap_copy (kvm->arch.vcpu_features, &features,KVM_VCPU_MAX_FEATURES ); ret =kvm_setup_vcpu (vcpu); -if (ret) +if (ret) { +bitmap_copy (kvm->arch.vcpu_features, old_features,KVM_VCPU_MAX_FEATURES ); goto out_unlock; + }/* Now we know what it is, we can reset it. */ kvm_reset_vcpu (vcpu);
base-commit: 089e4f3c4862ba3f29dff2361caa8084879194fd
Signature · 1 line
2.39.5
Oliver Upton
maintainerHi Fuad,
Quoted 9 lines · Fuad Tabba
__kvm_vcpu_set_target()copies the requested features into the VM-wide bitmap beforekvm_setup_vcpu()runs and doesn't undo it when setup fails, so a rejectedKVM_ARM_VCPU_INITleaves the VM recording features that were never set up. WithHAS_EL2|HAS_EL2_E2H0on a host withoutFEAT_NV1,kvm_vcpu_init_nested()returns-EINVALbefore it allocates any nested stage-2 MMU, andvcpu_has_nv()is then true withnested_mmus_size== 0; its-ENOMEMpaths do the same on a VM's first INIT.
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.
That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
Quoted 16 lines
arch/arm64/kvm/arm.c +1
-0@@ -1685,6 +1685,7 @@ static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)static int __kvm_vcpu_set_target (struct kvm_vcpu *vcpu,const struct kvm_vcpu_init *init) { +DECLARE_BITMAP (old_features,KVM_VCPU_MAX_FEATURES );unsigned long features = init->features[0 ];struct kvm *kvm = vcpu->kvm;int ret = -EINVAL ;@@ -1695,11 +1696,15 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, kvm_vcpu_init_changed(vcpu, init)) goto out_unlock;
arch/arm64/kvm/arm.c static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, · line 1707
Quoted 10 lines
arch/arm64/kvm/arm.c +5
-1+/* Setup reads the VM-wide bitmap, so undo the copy if setup fails. */ +bitmap_copy (old_features, kvm->arch.vcpu_features,KVM_VCPU_MAX_FEATURES );bitmap_copy (kvm->arch.vcpu_features, &features,KVM_VCPU_MAX_FEATURES ); ret =kvm_setup_vcpu (vcpu); -if (ret) +if (ret) { +bitmap_copy (kvm->arch.vcpu_features, old_features,KVM_VCPU_MAX_FEATURES ); goto out_unlock; + }
The bitmap copy is a bit confusing because there's only two possible situations:
- KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED is unset and the bitmap was previously zero
- KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED is set and @init->features is identical to vcpu_features (see kvm_vcpu_init_changed())
While there's nothing wrong with your diff, I'd prefer if the above detail was represented directly.
ret = kvm_setup_vcpu(vcpu);
if (ret) {
/*
* Clear the bitmap if setup fails on the first vCPU to be
* initialized.
*/
if (!test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags))
bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
goto out_unlock;
}Thanks,
Oliver
Fuad Tabba
reviewerHi Oliver,
On Mon, 21 Sep 2026 00:09:17 -0700, Oliver Upton <[email protected]> wrote:
[...]
Quoted 7 lines
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
That one is for Lorenzo then :) I'll reword the message so the stale bitmap comes first and the pre-fault crash is just how it showed up.
[...]
Quoted 12 lines
The bitmap copy is a bit confusing because there's only two possible situations:
-
KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGUREDis unset and the bitmap was previously zero-
KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGUREDis set and @init->features is identical tovcpu_features(seekvm_vcpu_init_changed())While there's nothing wrong with your diff, I'd prefer if the above detail was represented directly.
Quoted 11 lines
ret = kvm_setup_vcpu(vcpu);
if (ret) {
/*
* Clear the bitmap if setup fails on the first vCPU to be
* initialized.
*/
if (!test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags))
bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
goto out_unlock;
}I'd gone for the restore so the failure path didn't depend on the flag, but those two states are all there is and your version makes that explicit. I'll use it in v3.
Thanks,
/fuad
Quoted 9 lines · Fuad Tabba
Hi Oliver,
On Mon, 21 Sep 2026 00:09:17 -0700, Oliver Upton <[email protected]> wrote:
[...]Quoted 5 lines
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.
Quoted 5 lines
Quoted 2 lines
That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
That one is for Lorenzo then :) I'll reword the message so the stale bitmap comes first and the pre-fault crash is just how it showed up.
Let me dig into this.
--
Cheers, Lorenzo
Quoted 9 lines · Fuad Tabba
Hi Oliver,
On Mon, 21 Sep 2026 00:09:17 -0700, Oliver Upton <[email protected]> wrote:
[...]Quoted 5 lines
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.
Quoted 5 lines
Quoted 2 lines
That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
That one is for Lorenzo then :) I'll reword the message so the stale bitmap comes first and the pre-fault crash is just how it showed up.
In general this is the behaviour of _generic_ KVM code (kvm_vcpu_pre_fault_memory()), which itself does not require an initialised vCPU, and no other arch seems to requires that either in the arch-specific pre-fault code.
The actual pre-faulting implementation intentionally only targets the canonical MMU to fault in S2 page tables, which doesn't need initialisation to have taken place AFAICT.
However I could add:
if (!kvm_vcpu_initialized(vcpu)) return -ENOEXEC;
To kvm_arch_vcpu_pre_fault_memory(), which would make this a requirement, though that would bring us out of line with other archs.
OTOH, I don't see why anybody would pre-fault BEFORE init, feels like something that should be done later on in VMM bring up right?
So I doubt doing this would really break any sane user.
Let me know if you want that change in the series and I can respin it.
--
Cheers, Lorenzo
Oliver Upton
maintainerHey Lorenzo,
Quoted 25 lines · Lorenzo Stoakes (ARM)
Quoted 9 lines · Fuad Tabba
Hi Oliver,
On Mon, 21 Sep 2026 00:09:17 -0700, Oliver Upton <[email protected]> wrote:
[...]Quoted 5 lines
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.Quoted 5 lines
Quoted 2 lines
That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
That one is for Lorenzo then :) I'll reword the message so the stale bitmap comes first and the pre-fault crash is just how it showed up.
In general this is the behaviour of _generic_ KVM code (
kvm_vcpu_pre_fault_memory()), which itself does not require an initialised vCPU, and no other arch seems to requires that either in the arch-specific pre-fault code.The actual pre-faulting implementation intentionally only targets the canonical MMU to fault in S2 page tables, which doesn't need initialisation to have taken place AFAICT.
Quoted 7 lines
However I could add:
if (!kvm_vcpu_initialized(vcpu)) return -ENOEXEC;To
kvm_arch_vcpu_pre_fault_memory(), which would make this a requirement, though that would bring us out of line with other archs.
Actually, we need this enforcement to happen before the arch-generic implementation calls vcpu_load(). So that would be some amount of kvm_arch_* boilerplate added to the generic code to enforce this requirement like we do on the other vCPU ioctls.
I'm not too worried about aligning this with other architectures; the idea of a half-baked vCPU getting loaded is worrying.
Quoted 6 lines
OTOH, I don't see why anybody would pre-fault BEFORE init, feels like something that should be done later on in VMM bring up right?
So I doubt doing this would really break any sane user.
Let me know if you want that change in the series and I can respin it.
Please do, thank you for addressing this.
Best,
Oliver
Quoted 33 lines · Oliver Upton
Hey Lorenzo,
Quoted 30 lines · Lorenzo Stoakes (ARM)
Quoted 9 lines · Fuad Tabba
Hi Oliver,
On Mon, 21 Sep 2026 00:09:17 -0700, Oliver Upton <[email protected]> wrote:
[...]Quoted 5 lines
Quoted 4 lines
Nothing in the tree loads a vCPU whose init failed, so this is latent. The upcoming series that enables
KVM_PRE_FAULT_MEMORYfor arm64 exposes
it: the generickvm_vcpu_pre_fault_memory()callsvcpu_load()whether or not the vCPU has been initialised.Quoted 5 lines
Quoted 2 lines
That's a bug, not a feature. We should require an initialized vcpu for the ioctl.
That one is for Lorenzo then :) I'll reword the message so the stale bitmap comes first and the pre-fault crash is just how it showed up.
In general this is the behaviour of _generic_ KVM code (
kvm_vcpu_pre_fault_memory()), which itself does not require an initialised vCPU, and no other arch seems to requires that either in the arch-specific pre-fault code.The actual pre-faulting implementation intentionally only targets the canonical MMU to fault in S2 page tables, which doesn't need initialisation to have taken place AFAICT.
However I could add:
if (!kvm_vcpu_initialized(vcpu)) return -ENOEXEC;
Quoted 7 lines
Quoted 2 lines
To
kvm_arch_vcpu_pre_fault_memory(), which would make this a requirement, though that would bring us out of line with other archs.Actually, we need this enforcement to happen before the arch-generic implementation calls
vcpu_load(). So that would be some amount of kvm_arch_* boilerplate added to the generic code to enforce this requirement like we do on the other vCPU ioctls.
Ack, will do!
Quoted 3 lines
I'm not too worried about aligning this with other architectures; the idea of a half-baked vCPU getting loaded is worrying.
Yeah and I don't think anybody in their right mind is relying on being able to pre-fault before init anyway.
Quoted 9 lines
Quoted 7 lines
OTOH, I don't see why anybody would pre-fault BEFORE init, feels like something that should be done later on in VMM bring up right?
So I doubt doing this would really break any sane user.
Let me know if you want that change in the series and I can respin it.
Please do, thank you for addressing this.
No worries, will put a commit or 2 at the start of the series to action this and respin.
Quoted 3 lines
Best,
Oliver
--
Cheers, Lorenzo