* [PATCH v4 01/11] KVM: x86/hyperv: Get target FIFO in hv_tlb_flush_enqueue(), not caller
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 02/11] KVM: x86/hyperv: Check for NULL vCPU Hyper-V object in kvm_hv_get_tlb_flush_fifo() Sean Christopherson
` (9 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
When handling Hyper-V PV TLB flushes, retrieve the to-be-used FIFO in
hv_tlb_flush_enqueue() instead of having the caller pass in the FIFO. This
will make it easier to fix a cross-vCPU race where KVM can access a vCPU's
FIFO before it's fully initialized.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/hyperv.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 1ee0d23f8949..2dc3e64b3f2f 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -1935,16 +1935,18 @@ static int kvm_hv_get_tlb_flush_entries(struct kvm *kvm, struct kvm_hv_hcall *hc
return kvm_hv_get_hc_data(kvm, hc, hc->rep_cnt, hc->rep_cnt, entries);
}
-static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu,
- struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo,
- u64 *entries, int count)
+static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu, u64 *entries, int count,
+ bool is_guest_mode)
{
+ struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
u64 flush_all_entry = KVM_HV_TLB_FLUSHALL_ENTRY;
if (!hv_vcpu)
return;
+ tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(vcpu, is_guest_mode);
+
spin_lock(&tlb_flush_fifo->write_lock);
/*
@@ -2017,7 +2019,6 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
struct kvm *kvm = vcpu->kvm;
struct hv_tlb_flush_ex flush_ex;
struct hv_tlb_flush flush;
- struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
/*
* Normally, there can be no more than 'KVM_HV_TLB_FLUSH_FIFO_SIZE'
* entries on the TLB flush fifo. The last entry, however, needs to be
@@ -2144,11 +2145,8 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
* analyze it here, flush TLB regardless of the specified address space.
*/
if (all_cpus && !is_guest_mode(vcpu)) {
- kvm_for_each_vcpu(i, v, kvm) {
- tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
- hv_tlb_flush_enqueue(v, tlb_flush_fifo,
- tlb_flush_entries, hc->rep_cnt);
- }
+ kvm_for_each_vcpu(i, v, kvm)
+ hv_tlb_flush_enqueue(v, tlb_flush_entries, hc->rep_cnt, false);
kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
} else if (!is_guest_mode(vcpu)) {
@@ -2158,9 +2156,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
v = kvm_get_vcpu(kvm, i);
if (!v)
continue;
- tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
- hv_tlb_flush_enqueue(v, tlb_flush_fifo,
- tlb_flush_entries, hc->rep_cnt);
+ hv_tlb_flush_enqueue(v, tlb_flush_entries, hc->rep_cnt, false);
}
kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH, vcpu_mask);
@@ -2191,9 +2187,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
continue;
__set_bit(i, vcpu_mask);
- tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, true);
- hv_tlb_flush_enqueue(v, tlb_flush_fifo,
- tlb_flush_entries, hc->rep_cnt);
+ hv_tlb_flush_enqueue(v, tlb_flush_entries, hc->rep_cnt, true);
}
kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH, vcpu_mask);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 02/11] KVM: x86/hyperv: Check for NULL vCPU Hyper-V object in kvm_hv_get_tlb_flush_fifo()
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 01/11] KVM: x86/hyperv: Get target FIFO in hv_tlb_flush_enqueue(), not caller Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-07-01 15:02 ` Philippe Mathieu-Daudé
2026-06-30 22:56 ` [PATCH v4 03/11] KVM: x86/hyperv: Ensure vCPU's Hyper-V object is initialized on cross-vCPU accesses Sean Christopherson
` (8 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Check for a NULL Hyper-V object in kvm_hv_get_tlb_flush_fifo() instead of
relying on the caller to do so. This will allow fixing a cross-vCPU race
where KVM can access a vCPU's FIFO before it's fully initialized, without
having to jump through too many cognitive hoops to reason about the
correctness of the logic.
Ignoring changes in ordering that only affect the aforementioned race, no
functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/hyperv.c | 11 +++++------
arch/x86/kvm/hyperv.h | 7 ++++++-
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 2dc3e64b3f2f..49b1154366ce 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -1939,13 +1939,11 @@ static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu, u64 *entries, int count,
bool is_guest_mode)
{
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
- struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
u64 flush_all_entry = KVM_HV_TLB_FLUSHALL_ENTRY;
- if (!hv_vcpu)
- return;
-
tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(vcpu, is_guest_mode);
+ if (!tlb_flush_fifo)
+ return;
spin_lock(&tlb_flush_fifo->write_lock);
@@ -1972,15 +1970,16 @@ static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu, u64 *entries, int count,
int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
- struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
u64 entries[KVM_HV_TLB_FLUSH_FIFO_SIZE];
int i, j, count;
gva_t gva;
- if (!tdp_enabled || !hv_vcpu)
+ if (!tdp_enabled)
return -EINVAL;
tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(vcpu, is_guest_mode(vcpu));
+ if (!tlb_flush_fifo)
+ return -EINVAL;
count = kfifo_out(&tlb_flush_fifo->entries, entries, KVM_HV_TLB_FLUSH_FIFO_SIZE);
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 1c8f7aaab063..2da11b967c41 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -202,6 +202,9 @@ static inline struct kvm_vcpu_hv_tlb_flush_fifo *kvm_hv_get_tlb_flush_fifo(struc
int i = is_guest_mode ? HV_L2_TLB_FLUSH_FIFO :
HV_L1_TLB_FLUSH_FIFO;
+ if (!hv_vcpu)
+ return NULL;
+
return &hv_vcpu->tlb_flush_fifo[i];
}
@@ -209,10 +212,12 @@ static inline void kvm_hv_vcpu_purge_flush_tlb(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
- if (!to_hv_vcpu(vcpu) || !kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu))
+ if (!kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu))
return;
tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(vcpu, is_guest_mode(vcpu));
+ if (!tlb_flush_fifo)
+ return;
kfifo_reset_out(&tlb_flush_fifo->entries);
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 02/11] KVM: x86/hyperv: Check for NULL vCPU Hyper-V object in kvm_hv_get_tlb_flush_fifo()
2026-06-30 22:56 ` [PATCH v4 02/11] KVM: x86/hyperv: Check for NULL vCPU Hyper-V object in kvm_hv_get_tlb_flush_fifo() Sean Christopherson
@ 2026-07-01 15:02 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-01 15:02 UTC (permalink / raw)
To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
On 1/7/26 00:56, Sean Christopherson wrote:
> Check for a NULL Hyper-V object in kvm_hv_get_tlb_flush_fifo() instead of
> relying on the caller to do so. This will allow fixing a cross-vCPU race
> where KVM can access a vCPU's FIFO before it's fully initialized, without
> having to jump through too many cognitive hoops to reason about the
> correctness of the logic.
>
> Ignoring changes in ordering that only affect the aforementioned race, no
> functional change intended.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> arch/x86/kvm/hyperv.c | 11 +++++------
> arch/x86/kvm/hyperv.h | 7 ++++++-
> 2 files changed, 11 insertions(+), 7 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 03/11] KVM: x86/hyperv: Ensure vCPU's Hyper-V object is initialized on cross-vCPU accesses
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 01/11] KVM: x86/hyperv: Get target FIFO in hv_tlb_flush_enqueue(), not caller Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 02/11] KVM: x86/hyperv: Check for NULL vCPU Hyper-V object in kvm_hv_get_tlb_flush_fifo() Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 04/11] KVM: x86/xen: Always route non-singleshot-timer vCPU hypercalls to userspace Sean Christopherson
` (7 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
When initializing a vCPU's Hyper-V object, ensure the object is fully
initialized prior to exposing it through the vCPU, and ensure accesses from
other tasks (e.g. other vCPUs) see the fully initialized object if
vcpu->arch.hyperv is non-NULL.
Lack of ordering manifests as a lockdep splat due to attempting to lock a
TLB flush FIFO before the spinlock is initialized.
INFO: trying to register non-static key.
The code is fine but needs lockdep annotation, or maybe
you didn't initialize this object before use?
turning off the locking correctness validator.
CPU: 1 PID: 5005 Comm: syz-executor189 Not tainted 6.6.120-smp-DEV #1
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026
Call Trace:
<TASK>
[<ffffffff810dd10c>] dump_stack_lvl+0xcc/0x130 lib/dump_stack.c:106
[<ffffffff8192bddd>] assign_lock_key+0x1fd/0x230 kernel/locking/lockdep.c:977
[<ffffffff8191cb97>] register_lock_class+0x187/0x7a0 kernel/locking/lockdep.c:1291
[<ffffffff8191e7a9>] __lock_acquire+0x179/0x7650 kernel/locking/lockdep.c:5016
[<ffffffff8191e28f>] lock_acquire+0x13f/0x3d0 kernel/locking/lockdep.c:5756
[<ffffffff8101a65b>] __raw_spin_lock include/linux/spinlock_api_smp.h:133 [inline]
[<ffffffff8101a65b>] _raw_spin_lock+0x2b/0x40 kernel/locking/spinlock.c:154
[<ffffffff81319d44>] spin_lock include/linux/spinlock.h:351 [inline]
[<ffffffff81319d44>] hv_tlb_flush_enqueue+0xb4/0x270 arch/x86/kvm/hyperv.c:1946
[<ffffffff813160c6>] kvm_hv_flush_tlb+0xa96/0x1dc0 arch/x86/kvm/hyperv.c:2145
[<ffffffff8131438b>] kvm_hv_hypercall+0x103b/0x1fe0 arch/x86/kvm/hyperv.c:-1
[<ffffffff8133bff3>] __vmx_handle_exit arch/x86/kvm/vmx/vmx.c:6624 [inline]
[<ffffffff8133bff3>] vmx_handle_exit+0x12e3/0x21f0 arch/x86/kvm/vmx/vmx.c:6641
[<ffffffff81215d11>] vcpu_enter_guest arch/x86/kvm/x86.c:11649 [inline]
[<ffffffff81215d11>] vcpu_run+0x4d01/0x79c0 arch/x86/kvm/x86.c:11832
[<ffffffff8120fe39>] kvm_arch_vcpu_ioctl_run+0xb49/0x1c80 arch/x86/kvm/x86.c:12179
[<ffffffff8119cd60>] kvm_vcpu_ioctl+0xc80/0xff0 virt/kvm/kvm_main.c:6029
[<ffffffff8226fefd>] vfs_ioctl fs/ioctl.c:52 [inline]
[<ffffffff8226fefd>] __do_sys_ioctl fs/ioctl.c:872 [inline]
[<ffffffff8226fefd>] __se_sys_ioctl+0xfd/0x170 fs/ioctl.c:858
[<ffffffff85ac97d9>] do_syscall_x64 arch/x86/entry/common.c:52 [inline]
[<ffffffff85ac97d9>] do_syscall_64+0x69/0xb0 arch/x86/entry/common.c:93
[<ffffffff85c000d0>] entry_SYSCALL_64_after_hwframe+0x68/0xd2
</TASK>
Use the "safe" variant in all paths that are known to access the Hyper-V
object, as detected by an upcoming lockdep assertion, with an assist or two
from Sashiko.
Link: https://lore.kernel.org/all/20260612232258.0D9131F000E9@smtp.kernel.org
Fixes: 0823570f0198 ("KVM: x86: hyper-v: Introduce TLB flush fifo")
Fixes: fc08b628d7c9 ("KVM: x86: hyper-v: Allocate Hyper-V context lazily")
Reported-by: syzbot+5b32c49cd8f005e65654@syzkaller.appspotmail.com
Reported-by: syzbot+5d2b94b77112148d1744@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/6a396a66.52ae72c2.136ac7.0002.GAE@google.com
Tested-by: syzbot+5d2b94b77112148d1744@syzkaller.appspotmail.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/hyperv.c | 23 ++++++++++++++++++-----
arch/x86/kvm/hyperv.h | 18 +++++++++++++++---
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 49b1154366ce..888526ce4dab 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -206,13 +206,19 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vpidx)
{
- struct kvm_vcpu *vcpu;
struct kvm_vcpu_hv_synic *synic;
+ struct kvm_vcpu_hv *hv_vcpu;
+ struct kvm_vcpu *vcpu;
vcpu = get_vcpu_by_vpidx(kvm, vpidx);
- if (!vcpu || !to_hv_vcpu(vcpu))
+ if (!vcpu)
return NULL;
- synic = to_hv_synic(vcpu);
+
+ hv_vcpu = to_hv_vcpu_safe(vcpu);
+ if (!hv_vcpu)
+ return NULL;
+
+ synic = &hv_vcpu->synic;
return (synic->active) ? synic : NULL;
}
@@ -972,7 +978,6 @@ int kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
if (!hv_vcpu)
return -ENOMEM;
- vcpu->arch.hyperv = hv_vcpu;
hv_vcpu->vcpu = vcpu;
synic_init(&hv_vcpu->synic);
@@ -988,6 +993,14 @@ int kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
spin_lock_init(&hv_vcpu->tlb_flush_fifo[i].write_lock);
}
+ /*
+ * Ensure the structure is fully initialized before it's visible to
+ * other tasks, as much of the state can be legally accessed without
+ * holding vcpu->mutex.
+ *
+ * Pairs with the smp_load_acquire() in to_hv_vcpu_safe().
+ */
+ smp_store_release(&vcpu->arch.hyperv, hv_vcpu);
return 0;
}
@@ -2165,7 +2178,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
bitmap_zero(vcpu_mask, KVM_MAX_VCPUS);
kvm_for_each_vcpu(i, v, kvm) {
- hv_v = to_hv_vcpu(v);
+ hv_v = to_hv_vcpu_safe(v);
/*
* The following check races with nested vCPUs entering/exiting
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 2da11b967c41..ea9c81d76dd3 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -62,6 +62,18 @@ static inline struct kvm_hv *to_kvm_hv(struct kvm *kvm)
return &kvm->arch.hyperv;
}
+static inline struct kvm_vcpu_hv *to_hv_vcpu_safe(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Ensure the HyperV structure is fully initialized when accessing it
+ * without holding vcpu->mutex (or some other guarantee that KVM can't
+ * concurrently instantiate the structure).
+ *
+ * Pairs with the smp_store_release() in kvm_hv_vcpu_init().
+ */
+ return smp_load_acquire(&vcpu->arch.hyperv);
+}
+
static inline struct kvm_vcpu_hv *to_hv_vcpu(struct kvm_vcpu *vcpu)
{
return vcpu->arch.hyperv;
@@ -88,7 +100,7 @@ static inline struct kvm_hv_syndbg *to_hv_syndbg(struct kvm_vcpu *vcpu)
static inline u32 kvm_hv_get_vpindex(struct kvm_vcpu *vcpu)
{
- struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
+ struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu_safe(vcpu);
return hv_vcpu ? hv_vcpu->vp_index : vcpu->vcpu_idx;
}
@@ -142,7 +154,7 @@ static inline struct kvm_vcpu *hv_stimer_to_vcpu(struct kvm_vcpu_hv_stimer *stim
static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu)
{
- struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
+ struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu_safe(vcpu);
if (!hv_vcpu)
return false;
@@ -198,7 +210,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
static inline struct kvm_vcpu_hv_tlb_flush_fifo *kvm_hv_get_tlb_flush_fifo(struct kvm_vcpu *vcpu,
bool is_guest_mode)
{
- struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
+ struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu_safe(vcpu);
int i = is_guest_mode ? HV_L2_TLB_FLUSH_FIFO :
HV_L1_TLB_FLUSH_FIFO;
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 04/11] KVM: x86/xen: Always route non-singleshot-timer vCPU hypercalls to userspace
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (2 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 03/11] KVM: x86/hyperv: Ensure vCPU's Hyper-V object is initialized on cross-vCPU accesses Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 05/11] KVM: x86/xen: Consolidate checks on Xen vCPU ID for singleshot timer hypercalls Sean Christopherson
` (6 subsequent siblings)
10 siblings, 0 replies; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
When handling Xen vCPU hypercalls, explicitly route non-singleshot-timer
commands to userspace, *before* checking if in-kernel emulation of the Xen
timer is enabled. Punting hypercalls that are never accelerated by KVM
because some other hypercall happens to be disabled is confusing and
actively dangerous, e.g. it's easy to miss that the only reason KVM can
bail early is because the timer-disabled case provides the same semantics
as the implicit "default" path in the switch-statement.
Opportunistically convert the switch-statement to an if-else-statement to
avoid having to carry code for an impossible "default" case.
For all intents and purposes, no functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/xen.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 694b31c1fcc9..3ed6686e0a1a 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -1607,11 +1607,14 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
struct vcpu_set_singleshot_timer oneshot;
struct x86_exception e;
+ if (cmd != VCPUOP_set_singleshot_timer &&
+ cmd != VCPUOP_stop_singleshot_timer)
+ return false;
+
if (!kvm_xen_timer_enabled(vcpu))
return false;
- switch (cmd) {
- case VCPUOP_set_singleshot_timer:
+ if (cmd == VCPUOP_set_singleshot_timer) {
if (vcpu->arch.xen.vcpu_id != vcpu_id) {
*r = -EINVAL;
return true;
@@ -1640,20 +1643,16 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
}
kvm_xen_start_timer(vcpu, oneshot.timeout_abs_ns, false);
- *r = 0;
- return true;
-
- case VCPUOP_stop_singleshot_timer:
+ } else {
if (vcpu->arch.xen.vcpu_id != vcpu_id) {
*r = -EINVAL;
return true;
}
kvm_xen_stop_timer(vcpu);
- *r = 0;
- return true;
}
- return false;
+ *r = 0;
+ return true;
}
static bool kvm_xen_hcall_set_timer_op(struct kvm_vcpu *vcpu, uint64_t timeout,
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 05/11] KVM: x86/xen: Consolidate checks on Xen vCPU ID for singleshot timer hypercalls
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (3 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 04/11] KVM: x86/xen: Always route non-singleshot-timer vCPU hypercalls to userspace Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-07-01 15:03 ` Philippe Mathieu-Daudé
2026-06-30 22:56 ` [PATCH v4 06/11] KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set Sean Christopherson
` (5 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Hoist the checks on the Xen vCPU ID when handling set_singleshot_timer and
stop_singleshot_timer hypercalls out of their individual if-statements,
so that both checks on the ID are in common code. kvm_xen_hcall_vcpu_op()
is already doubly committed to handling only singleshot timer hypercalls,
and even if that were to change in the future, the function could simply
be renamed and turned into a helper specifically for timer hypercalls.
Opportunistically add a comment to explain why the check exists; the code
looks rather nonsensical without the knowledge that @vcpu_id is a common
param for all per-vCPU hypercalls.
No functional change intended.
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/xen.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 3ed6686e0a1a..7a0d89faca85 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -1614,12 +1614,18 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
if (!kvm_xen_timer_enabled(vcpu))
return false;
+ /*
+ * Reject the hypercall if the guest is trying to start/stop the timer
+ * for a different vCPU. Xen per-vCPU hypercalls take a target vCPU as
+ * a common parameter, as all per-vCPU hypercalls *except* single-shot
+ * timer updates can be cross-vCPU.
+ */
+ if (vcpu->arch.xen.vcpu_id != vcpu_id) {
+ *r = -EINVAL;
+ return true;
+ }
+
if (cmd == VCPUOP_set_singleshot_timer) {
- if (vcpu->arch.xen.vcpu_id != vcpu_id) {
- *r = -EINVAL;
- return true;
- }
-
/*
* The only difference for 32-bit compat is the 4 bytes of
* padding after the interesting part of the structure. So
@@ -1644,10 +1650,6 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
kvm_xen_start_timer(vcpu, oneshot.timeout_abs_ns, false);
} else {
- if (vcpu->arch.xen.vcpu_id != vcpu_id) {
- *r = -EINVAL;
- return true;
- }
kvm_xen_stop_timer(vcpu);
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 05/11] KVM: x86/xen: Consolidate checks on Xen vCPU ID for singleshot timer hypercalls
2026-06-30 22:56 ` [PATCH v4 05/11] KVM: x86/xen: Consolidate checks on Xen vCPU ID for singleshot timer hypercalls Sean Christopherson
@ 2026-07-01 15:03 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-01 15:03 UTC (permalink / raw)
To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
On 1/7/26 00:56, Sean Christopherson wrote:
> Hoist the checks on the Xen vCPU ID when handling set_singleshot_timer and
> stop_singleshot_timer hypercalls out of their individual if-statements,
> so that both checks on the ID are in common code. kvm_xen_hcall_vcpu_op()
> is already doubly committed to handling only singleshot timer hypercalls,
> and even if that were to change in the future, the function could simply
> be renamed and turned into a helper specifically for timer hypercalls.
>
> Opportunistically add a comment to explain why the check exists; the code
> looks rather nonsensical without the knowledge that @vcpu_id is a common
> param for all per-vCPU hypercalls.
>
> No functional change intended.
>
> Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> arch/x86/kvm/xen.c | 20 +++++++++++---------
> 1 file changed, 11 insertions(+), 9 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 06/11] KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (4 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 05/11] KVM: x86/xen: Consolidate checks on Xen vCPU ID for singleshot timer hypercalls Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-06-30 23:08 ` sashiko-bot
2026-06-30 22:56 ` [PATCH v4 07/11] KVM: Initialize a vCPU's index to '-1' while it's being created Sean Christopherson
` (4 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Explicitly invalidate KVM's internal Xen vCPU ID during vCPU creation
instead of *trying* to set the Xen ID to the vCPU index by default, and
forward singleshot timer hypercalls to userspace if the VMM hasn't set the
Xen ID via KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID. Using the vCPU's index as its
default Xen ID is reasonable in concept, but in practice is horribly flawed
as the index is left as '0' until after vCPU initialization completes, i.e.
every vCPU gets a Xen ID of '0' by default.
Forward hypercalls to userspace instead of trying to salvage any kind of
default behavior, as all userspace implementations that support multiple
vCPUs either don't enable the timer, are guaranteed to set Xen ID, or work
only because *all* guests also screw up the singleshot timer hypercalls.
The last scenarios is extremely unlikely given that Linux-as-a-guest uses
the actual Xen vCPU ID when making timer hypercalls. In other words, for
all intents and purposes, KVM's ABI is already that userspace must set the
Xen vCPU ID, so just commit to that ABI.
Note, KVM's handling of KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID restricts the ID to
KVM_MAX_VCPUS, so there's no chance of a valid ID colliding with U32_MAX.
Add a compile-time assertion to ensure this holds true in the future (KVM
doesn't care what value is used for "invalid", only that there can't be a
collision).
Link: https://lore.kernel.org/all/20260612233017.1F9771F000E9@smtp.kernel.org
Suggested-by: David Woodhouse <dwmw2@infradead.org>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/xen.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 7a0d89faca85..eef378d0bb45 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -22,6 +22,7 @@
#include <xen/interface/version.h>
#include <xen/interface/event_channel.h>
#include <xen/interface/sched.h>
+#include <xen/xen-ops.h>
#include <asm/xen/cpuid.h>
#include <asm/pvclock.h>
@@ -1103,6 +1104,8 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID:
+ BUILD_BUG_ON(XEN_VCPU_ID_INVALID < KVM_MAX_VCPUS);
+
if (data->u.vcpu_id >= KVM_MAX_VCPUS)
r = -EINVAL;
else {
@@ -1614,6 +1617,9 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
if (!kvm_xen_timer_enabled(vcpu))
return false;
+ if (vcpu->arch.xen.vcpu_id == XEN_VCPU_ID_INVALID)
+ return false;
+
/*
* Reject the hypercall if the guest is trying to start/stop the timer
* for a different vCPU. Xen per-vCPU hypercalls take a target vCPU as
@@ -2300,7 +2306,7 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu)
{
- vcpu->arch.xen.vcpu_id = vcpu->vcpu_idx;
+ vcpu->arch.xen.vcpu_id = XEN_VCPU_ID_INVALID;
vcpu->arch.xen.poll_evtchn = 0;
timer_setup(&vcpu->arch.xen.poll_timer, cancel_evtchn_poll, 0);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 06/11] KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set
2026-06-30 22:56 ` [PATCH v4 06/11] KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set Sean Christopherson
@ 2026-06-30 23:08 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-06-30 23:08 UTC (permalink / raw)
To: Sean Christopherson; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] KVM_XEN_VCPU_SET_ATTR unconditionally rejects the uninitialized state (XEN_VCPU_ID_INVALID) returned by KVM_XEN_VCPU_GET_ATTR, breaking live migration.
--
commit a0e30df39328c44b4229534da902c6e3bed5d228
Author: Sean Christopherson <seanjc@google.com>
KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set
This commit explicitly invalidates the Xen vCPU ID at creation instead of
trying to default it to the vCPU index. It also forwards singleshot timer
hypercalls to userspace if the VMM hasn't explicitly set the Xen vCPU ID.
Link: https://lore.kernel.org/all/20260612233017.1F9771F000E9@smtp.kernel.org
> diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
> --- a/arch/x86/kvm/xen.c
> +++ b/arch/x86/kvm/xen.c
> @@ -1103,6 +1104,8 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
> break;
>
> case KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID:
> + BUILD_BUG_ON(XEN_VCPU_ID_INVALID < KVM_MAX_VCPUS);
> +
> if (data->u.vcpu_id >= KVM_MAX_VCPUS)
> r = -EINVAL;
> else {
[Severity: High]
Will this break live migration by making GET and SET operations asymmetric?
Since kvm_xen_init_vcpu() now initializes the vCPU ID to XEN_VCPU_ID_INVALID:
kvm_xen_init_vcpu()
vcpu->arch.xen.vcpu_id = XEN_VCPU_ID_INVALID;
During live migration, if the VMM hasn't explicitly configured the ID, the
source VMM will read this initial state via kvm_xen_vcpu_get_attr(), receiving
the U32_MAX value.
When the destination VMM attempts to restore this exact state using
kvm_xen_vcpu_set_attr(), it appears it will be unconditionally rejected here
because U32_MAX is >= KVM_MAX_VCPUS, returning -EINVAL.
Should kvm_xen_vcpu_set_attr() be updated to explicitly allow restoring
XEN_VCPU_ID_INVALID to preserve standard save and restore loops?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260630225619.511632-1-seanjc@google.com?part=6
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 07/11] KVM: Initialize a vCPU's index to '-1' while it's being created
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (5 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 06/11] KVM: x86/xen: Punt singleshot timer hcalls to userspace if Xen vCPU ID isn't set Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-07-01 15:06 ` Philippe Mathieu-Daudé
2026-06-30 22:56 ` [PATCH v4 08/11] KVM: Move nVMX's lockdep logic for vcpu->mutex to a common helper Sean Christopherson
` (3 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Invalidate a vCPU's index immediately after allocating storage for the vCPU
so that KVM doesn't incorrectly treat a vCPU that is the process of being
created as being vCPU0. This will also allow detecting that a vCPU is in
the process of being created and thus otherwise unreachable, which is
useful for avoiding false positives in lockdep assertions on vcpu->mutex.
Unwind the index back to -1 if inserting the vCPU into the array or adding
the vCPU to the fd table fails, so that kvm_arch_vcpu_destroy() sees the
vCPU as unreachable, i.e. so that teardown logic doesn't hit false positive
lockdep assertions. Opportunistically add a comment to call out that the
"real" index needs to be set before making the vCPU visible to other tasks.
Note, kvm_wait_for_vcpu_online() naturally does the right thing thanks to
vcpu->vcpu_idx and kvm->online_vcpus being signed values.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
virt/kvm/kvm_main.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index e44c20c04961..05275d318bfb 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4188,6 +4188,8 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
goto vcpu_decrement;
}
+ vcpu->vcpu_idx = -1;
+
BUILD_BUG_ON(sizeof(struct kvm_run) > PAGE_SIZE);
page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
if (!page) {
@@ -4216,6 +4218,11 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
goto unlock_vcpu_destroy;
}
+ /*
+ * Set the vCPU's index *before* the vCPU is reachable by other tasks.
+ * Unwind the index back to -1 on failure so that KVM can use the index
+ * to detect that the vCPU is unreachable, e.g. for lockdep asserts.
+ */
vcpu->vcpu_idx = atomic_read(&kvm->online_vcpus);
r = xa_insert(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT);
WARN_ON_ONCE(r == -EBUSY);
@@ -4254,6 +4261,7 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
kvm_put_kvm_no_destroy(kvm);
xa_erase(&kvm->vcpu_array, vcpu->vcpu_idx);
unlock_vcpu_destroy:
+ vcpu->vcpu_idx = -1;
mutex_unlock(&kvm->lock);
kvm_dirty_ring_free(&vcpu->dirty_ring);
arch_vcpu_destroy:
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 07/11] KVM: Initialize a vCPU's index to '-1' while it's being created
2026-06-30 22:56 ` [PATCH v4 07/11] KVM: Initialize a vCPU's index to '-1' while it's being created Sean Christopherson
@ 2026-07-01 15:06 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-01 15:06 UTC (permalink / raw)
To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
On 1/7/26 00:56, Sean Christopherson wrote:
> Invalidate a vCPU's index immediately after allocating storage for the vCPU
> so that KVM doesn't incorrectly treat a vCPU that is the process of being
> created as being vCPU0. This will also allow detecting that a vCPU is in
> the process of being created and thus otherwise unreachable, which is
> useful for avoiding false positives in lockdep assertions on vcpu->mutex.
>
> Unwind the index back to -1 if inserting the vCPU into the array or adding
> the vCPU to the fd table fails, so that kvm_arch_vcpu_destroy() sees the
> vCPU as unreachable, i.e. so that teardown logic doesn't hit false positive
> lockdep assertions. Opportunistically add a comment to call out that the
> "real" index needs to be set before making the vCPU visible to other tasks.
>
> Note, kvm_wait_for_vcpu_online() naturally does the right thing thanks to
> vcpu->vcpu_idx and kvm->online_vcpus being signed values.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> virt/kvm/kvm_main.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 08/11] KVM: Move nVMX's lockdep logic for vcpu->mutex to a common helper
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (6 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 07/11] KVM: Initialize a vCPU's index to '-1' while it's being created Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-07-01 15:04 ` Philippe Mathieu-Daudé
2026-06-30 22:56 ` [PATCH v4 09/11] KVM: x86: Treat a vCPU as unreachable if its index is invalid Sean Christopherson
` (2 subsequent siblings)
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Extract nVMX's lockdep assertion that a vCPU is locked or otherwise
unreachable into a common helper, as KVM x86 is about to gain another user,
but there is nothing x86-specific about the logic, i.e. the assertion may
be useful for other architectures.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/vmx/nested.h | 6 ++----
include/linux/kvm_host.h | 6 ++++++
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index 6d6cd5904ddf..c6de848bd9ce 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -57,16 +57,14 @@ bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port,
static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
{
- lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
- !refcount_read(&vcpu->kvm->users_count));
+ kvm_lockdep_assert_vcpu_is_locked_or_unreachable(vcpu);
return to_vmx(vcpu)->nested.cached_vmcs12;
}
static inline struct vmcs12 *get_shadow_vmcs12(struct kvm_vcpu *vcpu)
{
- lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
- !refcount_read(&vcpu->kvm->users_count));
+ kvm_lockdep_assert_vcpu_is_locked_or_unreachable(vcpu);
return to_vmx(vcpu)->nested.cached_shadow_vmcs12;
}
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ab8cfaec82d3..b10814f99a50 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -989,6 +989,12 @@ static inline struct kvm_io_bus *kvm_get_bus(struct kvm *kvm, enum kvm_bus idx)
lockdep_is_held(&kvm->slots_lock));
}
+static inline void kvm_lockdep_assert_vcpu_is_locked_or_unreachable(struct kvm_vcpu *vcpu)
+{
+ lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
+ !refcount_read(&vcpu->kvm->users_count));
+}
+
static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
{
int num_vcpus = atomic_read(&kvm->online_vcpus);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 08/11] KVM: Move nVMX's lockdep logic for vcpu->mutex to a common helper
2026-06-30 22:56 ` [PATCH v4 08/11] KVM: Move nVMX's lockdep logic for vcpu->mutex to a common helper Sean Christopherson
@ 2026-07-01 15:04 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-01 15:04 UTC (permalink / raw)
To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
On 1/7/26 00:56, Sean Christopherson wrote:
> Extract nVMX's lockdep assertion that a vCPU is locked or otherwise
> unreachable into a common helper, as KVM x86 is about to gain another user,
> but there is nothing x86-specific about the logic, i.e. the assertion may
> be useful for other architectures.
>
> No functional change intended.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> arch/x86/kvm/vmx/nested.h | 6 ++----
> include/linux/kvm_host.h | 6 ++++++
> 2 files changed, 8 insertions(+), 4 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 09/11] KVM: x86: Treat a vCPU as unreachable if its index is invalid
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (7 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 08/11] KVM: Move nVMX's lockdep logic for vcpu->mutex to a common helper Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-07-01 15:07 ` Philippe Mathieu-Daudé
2026-06-30 22:56 ` [PATCH v4 10/11] KVM: x86/hyperv: Assert vCPU's mutex is held in to_hv_vcpu() Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 11/11] KVM: x86/hyperv: Use {READ,WRITE}_ONCE for cross-task synic->active accesses Sean Christopherson
10 siblings, 1 reply; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
In the "vCPU locked or unreachable" lockdep assertion, treat a vCPU as
unreachable if its index is invalid, i.e. if the vCPU is in the process of
being created. Until the vCPU is inserted into the array of vCPUs, the
only way to get at the vCPU is via kvm_vm_ioctl_create_vcpu(). Note, the
actual index is set _before_ adding the vCPU to the array, i.e. there's no
risk of a false negative on the lockdep assertion.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
include/linux/kvm_host.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b10814f99a50..0bdfa3699352 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -992,6 +992,7 @@ static inline struct kvm_io_bus *kvm_get_bus(struct kvm *kvm, enum kvm_bus idx)
static inline void kvm_lockdep_assert_vcpu_is_locked_or_unreachable(struct kvm_vcpu *vcpu)
{
lockdep_assert_once(lockdep_is_held(&vcpu->mutex) ||
+ vcpu->vcpu_idx < 0 ||
!refcount_read(&vcpu->kvm->users_count));
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v4 09/11] KVM: x86: Treat a vCPU as unreachable if its index is invalid
2026-06-30 22:56 ` [PATCH v4 09/11] KVM: x86: Treat a vCPU as unreachable if its index is invalid Sean Christopherson
@ 2026-07-01 15:07 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-01 15:07 UTC (permalink / raw)
To: Sean Christopherson, Vitaly Kuznetsov, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
On 1/7/26 00:56, Sean Christopherson wrote:
> In the "vCPU locked or unreachable" lockdep assertion, treat a vCPU as
> unreachable if its index is invalid, i.e. if the vCPU is in the process of
> being created. Until the vCPU is inserted into the array of vCPUs, the
> only way to get at the vCPU is via kvm_vm_ioctl_create_vcpu(). Note, the
> actual index is set _before_ adding the vCPU to the array, i.e. there's no
> risk of a false negative on the lockdep assertion.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> include/linux/kvm_host.h | 1 +
> 1 file changed, 1 insertion(+)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 10/11] KVM: x86/hyperv: Assert vCPU's mutex is held in to_hv_vcpu()
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (8 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 09/11] KVM: x86: Treat a vCPU as unreachable if its index is invalid Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
2026-06-30 22:56 ` [PATCH v4 11/11] KVM: x86/hyperv: Use {READ,WRITE}_ONCE for cross-task synic->active accesses Sean Christopherson
10 siblings, 0 replies; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
Assert that either vcpu->mutex is held or the VM is otherwise unreachable
when using the normal vCPU => HyperV accessor to help detect improper
cross-task usage of the HyperV structure. When accessing the structure
without holding the vCPU's mutex, e.g. to send interrupts or to queue TLB
flushes, KVM needs to use the more paranoid to_hv_vcpu_safe() to guarantee
that it can't see a half-baked structure.
To avoid false positives, open code accesses to vcpu->arch.hyperv in the
Synthetic Timer callbacks (can be reached if and only if HyperV state is
fully initialized).
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/hyperv.c | 6 ++----
arch/x86/kvm/hyperv.h | 2 ++
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 888526ce4dab..f765c3bb9b1f 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -599,8 +599,7 @@ static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
{
struct kvm_vcpu *vcpu = hv_stimer_to_vcpu(stimer);
- set_bit(stimer->index,
- to_hv_vcpu(vcpu)->stimer_pending_bitmap);
+ set_bit(stimer->index, vcpu->arch.hyperv->stimer_pending_bitmap);
kvm_make_request(KVM_REQ_HV_STIMER, vcpu);
if (vcpu_kick)
kvm_vcpu_kick(vcpu);
@@ -614,8 +613,7 @@ static void stimer_cleanup(struct kvm_vcpu_hv_stimer *stimer)
stimer->index);
hrtimer_cancel(&stimer->timer);
- clear_bit(stimer->index,
- to_hv_vcpu(vcpu)->stimer_pending_bitmap);
+ clear_bit(stimer->index, vcpu->arch.hyperv->stimer_pending_bitmap);
stimer->msg_pending = false;
stimer->exp_time = 0;
}
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index ea9c81d76dd3..37a0bcf03e28 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -76,6 +76,8 @@ static inline struct kvm_vcpu_hv *to_hv_vcpu_safe(struct kvm_vcpu *vcpu)
static inline struct kvm_vcpu_hv *to_hv_vcpu(struct kvm_vcpu *vcpu)
{
+ kvm_lockdep_assert_vcpu_is_locked_or_unreachable(vcpu);
+
return vcpu->arch.hyperv;
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v4 11/11] KVM: x86/hyperv: Use {READ,WRITE}_ONCE for cross-task synic->active accesses
2026-06-30 22:56 [PATCH v4 00/11] KVM: x86/hyperv: Fix racy usage of vcpu->arch.hyperv Sean Christopherson
` (9 preceding siblings ...)
2026-06-30 22:56 ` [PATCH v4 10/11] KVM: x86/hyperv: Assert vCPU's mutex is held in to_hv_vcpu() Sean Christopherson
@ 2026-06-30 22:56 ` Sean Christopherson
10 siblings, 0 replies; 18+ messages in thread
From: Sean Christopherson @ 2026-06-30 22:56 UTC (permalink / raw)
To: Vitaly Kuznetsov, Sean Christopherson, Paolo Bonzini,
David Woodhouse, Paul Durrant
Cc: kvm, linux-kernel, syzbot+5b32c49cd8f005e65654,
syzbot+5d2b94b77112148d1744, David Woodhouse
When activating Hyper-V's Synthetic Interrupt Controller (SynIC), mark it
active with WRITE_ONCE() and query it using READ_ONCE() in synic_get(),
the only known cross-task reader, to document that the flag is accessed
without holding the vCPU's mutex.
Note, there are no data dependencies on the SynIC being marked active,
e.g. the vector read by synic_set_irq() is set (usually in response to
guest activity) long after the SynIC is initially activated, and a false
negative on the SynIC being active would be benign (ignoring that such a
race is likely to be problematic for the guest irrespective of what KVM
does).
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/hyperv.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index f765c3bb9b1f..9d38cb644668 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -219,7 +219,7 @@ static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vpidx)
return NULL;
synic = &hv_vcpu->synic;
- return (synic->active) ? synic : NULL;
+ return READ_ONCE(synic->active) ? synic : NULL;
}
static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
@@ -1013,7 +1013,7 @@ int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages)
synic = to_hv_synic(vcpu);
- synic->active = true;
+ WRITE_ONCE(synic->active, true);
synic->dont_zero_synic_pages = dont_zero_synic_pages;
synic->control = HV_SYNIC_CONTROL_ENABLE;
return 0;
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread