Skip to content

Commit 7a9164d

Browse files
committed
Merge tag 'kvm-x86-vcpu_array-6.14' of https://github.com/kvm-x86/linux into HEAD
KVM vcpu_array fixes and cleanups for 6.14: - Explicitly verify the target vCPU is online in kvm_get_vcpu() to fix a bug where KVM would return a pointer to a vCPU prior to it being fully online, and give kvm_for_each_vcpu() similar treatment to fix a similar flaw. - Wait for a vCPU to come online prior to executing a vCPU ioctl to fix a bug where userspace could coerce KVM into handling the ioctl on a vCPU that isn't yet onlined. - Gracefully handle xa_insert() failures even though such failuires should be impossible in practice.
2 parents 4e4f38f + 01528db commit 7a9164d

File tree

2 files changed

+65
-19
lines changed

2 files changed

+65
-19
lines changed

include/linux/kvm_host.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -963,16 +963,26 @@ static inline struct kvm_io_bus *kvm_get_bus(struct kvm *kvm, enum kvm_bus idx)
963963
static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
964964
{
965965
int num_vcpus = atomic_read(&kvm->online_vcpus);
966+
967+
/*
968+
* Explicitly verify the target vCPU is online, as the anti-speculation
969+
* logic only limits the CPU's ability to speculate, e.g. given a "bad"
970+
* index, clamping the index to 0 would return vCPU0, not NULL.
971+
*/
972+
if (i >= num_vcpus)
973+
return NULL;
974+
966975
i = array_index_nospec(i, num_vcpus);
967976

968977
/* Pairs with smp_wmb() in kvm_vm_ioctl_create_vcpu. */
969978
smp_rmb();
970979
return xa_load(&kvm->vcpu_array, i);
971980
}
972981

973-
#define kvm_for_each_vcpu(idx, vcpup, kvm) \
974-
xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0, \
975-
(atomic_read(&kvm->online_vcpus) - 1))
982+
#define kvm_for_each_vcpu(idx, vcpup, kvm) \
983+
if (atomic_read(&kvm->online_vcpus)) \
984+
xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0, \
985+
(atomic_read(&kvm->online_vcpus) - 1))
976986

977987
static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id)
978988
{

virt/kvm/kvm_main.c

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4111,48 +4111,48 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
41114111

41124112
mutex_lock(&kvm->lock);
41134113

4114-
#ifdef CONFIG_LOCKDEP
4115-
/* Ensure that lockdep knows vcpu->mutex is taken *inside* kvm->lock */
4116-
mutex_lock(&vcpu->mutex);
4117-
mutex_unlock(&vcpu->mutex);
4118-
#endif
4119-
41204114
if (kvm_get_vcpu_by_id(kvm, id)) {
41214115
r = -EEXIST;
41224116
goto unlock_vcpu_destroy;
41234117
}
41244118

41254119
vcpu->vcpu_idx = atomic_read(&kvm->online_vcpus);
4126-
r = xa_reserve(&kvm->vcpu_array, vcpu->vcpu_idx, GFP_KERNEL_ACCOUNT);
4120+
r = xa_insert(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT);
4121+
WARN_ON_ONCE(r == -EBUSY);
41274122
if (r)
41284123
goto unlock_vcpu_destroy;
41294124

4130-
/* Now it's all set up, let userspace reach it */
4125+
/*
4126+
* Now it's all set up, let userspace reach it. Grab the vCPU's mutex
4127+
* so that userspace can't invoke vCPU ioctl()s until the vCPU is fully
4128+
* visible (per online_vcpus), e.g. so that KVM doesn't get tricked
4129+
* into a NULL-pointer dereference because KVM thinks the _current_
4130+
* vCPU doesn't exist. As a bonus, taking vcpu->mutex ensures lockdep
4131+
* knows it's taken *inside* kvm->lock.
4132+
*/
4133+
mutex_lock(&vcpu->mutex);
41314134
kvm_get_kvm(kvm);
41324135
r = create_vcpu_fd(vcpu);
41334136
if (r < 0)
4134-
goto kvm_put_xa_release;
4135-
4136-
if (KVM_BUG_ON(xa_store(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, 0), kvm)) {
4137-
r = -EINVAL;
4138-
goto kvm_put_xa_release;
4139-
}
4137+
goto kvm_put_xa_erase;
41404138

41414139
/*
41424140
* Pairs with smp_rmb() in kvm_get_vcpu. Store the vcpu
41434141
* pointer before kvm->online_vcpu's incremented value.
41444142
*/
41454143
smp_wmb();
41464144
atomic_inc(&kvm->online_vcpus);
4145+
mutex_unlock(&vcpu->mutex);
41474146

41484147
mutex_unlock(&kvm->lock);
41494148
kvm_arch_vcpu_postcreate(vcpu);
41504149
kvm_create_vcpu_debugfs(vcpu);
41514150
return r;
41524151

4153-
kvm_put_xa_release:
4152+
kvm_put_xa_erase:
4153+
mutex_unlock(&vcpu->mutex);
41544154
kvm_put_kvm_no_destroy(kvm);
4155-
xa_release(&kvm->vcpu_array, vcpu->vcpu_idx);
4155+
xa_erase(&kvm->vcpu_array, vcpu->vcpu_idx);
41564156
unlock_vcpu_destroy:
41574157
mutex_unlock(&kvm->lock);
41584158
kvm_dirty_ring_free(&vcpu->dirty_ring);
@@ -4277,6 +4277,33 @@ static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
42774277
}
42784278
#endif
42794279

4280+
static int kvm_wait_for_vcpu_online(struct kvm_vcpu *vcpu)
4281+
{
4282+
struct kvm *kvm = vcpu->kvm;
4283+
4284+
/*
4285+
* In practice, this happy path will always be taken, as a well-behaved
4286+
* VMM will never invoke a vCPU ioctl() before KVM_CREATE_VCPU returns.
4287+
*/
4288+
if (likely(vcpu->vcpu_idx < atomic_read(&kvm->online_vcpus)))
4289+
return 0;
4290+
4291+
/*
4292+
* Acquire and release the vCPU's mutex to wait for vCPU creation to
4293+
* complete (kvm_vm_ioctl_create_vcpu() holds the mutex until the vCPU
4294+
* is fully online).
4295+
*/
4296+
if (mutex_lock_killable(&vcpu->mutex))
4297+
return -EINTR;
4298+
4299+
mutex_unlock(&vcpu->mutex);
4300+
4301+
if (WARN_ON_ONCE(!kvm_get_vcpu(kvm, vcpu->vcpu_idx)))
4302+
return -EIO;
4303+
4304+
return 0;
4305+
}
4306+
42804307
static long kvm_vcpu_ioctl(struct file *filp,
42814308
unsigned int ioctl, unsigned long arg)
42824309
{
@@ -4292,6 +4319,15 @@ static long kvm_vcpu_ioctl(struct file *filp,
42924319
if (unlikely(_IOC_TYPE(ioctl) != KVMIO))
42934320
return -EINVAL;
42944321

4322+
/*
4323+
* Wait for the vCPU to be online before handling the ioctl(), as KVM
4324+
* assumes the vCPU is reachable via vcpu_array, i.e. may dereference
4325+
* a NULL pointer if userspace invokes an ioctl() before KVM is ready.
4326+
*/
4327+
r = kvm_wait_for_vcpu_online(vcpu);
4328+
if (r)
4329+
return r;
4330+
42954331
/*
42964332
* Some architectures have vcpu ioctls that are asynchronous to vcpu
42974333
* execution; mutex_lock() would break them.

0 commit comments

Comments
 (0)