Skip to content

Commit 2031f28

Browse files
sean-jcbonzini
authored andcommitted
KVM: Add helpers to wrap vcpu->srcu_idx and yell if it's abused
Add wrappers to acquire/release KVM's SRCU lock when stashing the index in vcpu->src_idx, along with rudimentary detection of illegal usage, e.g. re-acquiring SRCU and thus overwriting vcpu->src_idx. Because the SRCU index is (currently) either 0 or 1, illegal nesting bugs can go unnoticed for quite some time and only cause problems when the nested lock happens to get a different index. Wrap the WARNs in PROVE_RCU=y, and make them ONCE, otherwise KVM will likely yell so loudly that it will bring the kernel to its knees. Signed-off-by: Sean Christopherson <seanjc@google.com> Tested-by: Fabiano Rosas <farosas@linux.ibm.com> Message-Id: <20220415004343.2203171-4-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent fdd6f6a commit 2031f28

11 files changed

Lines changed: 71 additions & 50 deletions

File tree

arch/powerpc/kvm/book3s_64_mmu_radix.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,10 @@ int kvmppc_mmu_walk_radix_tree(struct kvm_vcpu *vcpu, gva_t eaddr,
168168
return -EINVAL;
169169
/* Read the entry from guest memory */
170170
addr = base + (index * sizeof(rpte));
171-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
171+
172+
kvm_vcpu_srcu_read_lock(vcpu);
172173
ret = kvm_read_guest(kvm, addr, &rpte, sizeof(rpte));
173-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
174+
kvm_vcpu_srcu_read_unlock(vcpu);
174175
if (ret) {
175176
if (pte_ret_p)
176177
*pte_ret_p = addr;
@@ -246,9 +247,9 @@ int kvmppc_mmu_radix_translate_table(struct kvm_vcpu *vcpu, gva_t eaddr,
246247

247248
/* Read the table to find the root of the radix tree */
248249
ptbl = (table & PRTB_MASK) + (table_index * sizeof(entry));
249-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
250+
kvm_vcpu_srcu_read_lock(vcpu);
250251
ret = kvm_read_guest(kvm, ptbl, &entry, sizeof(entry));
251-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
252+
kvm_vcpu_srcu_read_unlock(vcpu);
252253
if (ret)
253254
return ret;
254255

arch/powerpc/kvm/book3s_hv_nested.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,10 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
306306
/* copy parameters in */
307307
hv_ptr = kvmppc_get_gpr(vcpu, 4);
308308
regs_ptr = kvmppc_get_gpr(vcpu, 5);
309-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
309+
kvm_vcpu_srcu_read_lock(vcpu);
310310
err = kvmhv_read_guest_state_and_regs(vcpu, &l2_hv, &l2_regs,
311311
hv_ptr, regs_ptr);
312-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
312+
kvm_vcpu_srcu_read_unlock(vcpu);
313313
if (err)
314314
return H_PARAMETER;
315315

@@ -410,10 +410,10 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
410410
byteswap_hv_regs(&l2_hv);
411411
byteswap_pt_regs(&l2_regs);
412412
}
413-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
413+
kvm_vcpu_srcu_read_lock(vcpu);
414414
err = kvmhv_write_guest_state_and_regs(vcpu, &l2_hv, &l2_regs,
415415
hv_ptr, regs_ptr);
416-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
416+
kvm_vcpu_srcu_read_unlock(vcpu);
417417
if (err)
418418
return H_AUTHORITY;
419419

@@ -600,16 +600,16 @@ long kvmhv_copy_tofrom_guest_nested(struct kvm_vcpu *vcpu)
600600
goto not_found;
601601

602602
/* Write what was loaded into our buffer back to the L1 guest */
603-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
603+
kvm_vcpu_srcu_read_lock(vcpu);
604604
rc = kvm_vcpu_write_guest(vcpu, gp_to, buf, n);
605-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
605+
kvm_vcpu_srcu_read_unlock(vcpu);
606606
if (rc)
607607
goto not_found;
608608
} else {
609609
/* Load the data to be stored from the L1 guest into our buf */
610-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
610+
kvm_vcpu_srcu_read_lock(vcpu);
611611
rc = kvm_vcpu_read_guest(vcpu, gp_from, buf, n);
612-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
612+
kvm_vcpu_srcu_read_unlock(vcpu);
613613
if (rc)
614614
goto not_found;
615615

arch/powerpc/kvm/book3s_rtas.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
229229
*/
230230
args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;
231231

232-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
232+
kvm_vcpu_srcu_read_lock(vcpu);
233233
rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
234-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
234+
kvm_vcpu_srcu_read_unlock(vcpu);
235235
if (rc)
236236
goto fail;
237237

arch/powerpc/kvm/powerpc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,9 @@ int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
425425
return EMULATE_DONE;
426426
}
427427

428-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
428+
kvm_vcpu_srcu_read_lock(vcpu);
429429
rc = kvm_read_guest(vcpu->kvm, pte.raddr, ptr, size);
430-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
430+
kvm_vcpu_srcu_read_unlock(vcpu);
431431
if (rc)
432432
return EMULATE_DO_MMIO;
433433

arch/riscv/kvm/vcpu.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -727,13 +727,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
727727
/* Mark this VCPU ran at least once */
728728
vcpu->arch.ran_atleast_once = true;
729729

730-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
730+
kvm_vcpu_srcu_read_lock(vcpu);
731731

732732
/* Process MMIO value returned from user-space */
733733
if (run->exit_reason == KVM_EXIT_MMIO) {
734734
ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run);
735735
if (ret) {
736-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
736+
kvm_vcpu_srcu_read_unlock(vcpu);
737737
return ret;
738738
}
739739
}
@@ -742,13 +742,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
742742
if (run->exit_reason == KVM_EXIT_RISCV_SBI) {
743743
ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run);
744744
if (ret) {
745-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
745+
kvm_vcpu_srcu_read_unlock(vcpu);
746746
return ret;
747747
}
748748
}
749749

750750
if (run->immediate_exit) {
751-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
751+
kvm_vcpu_srcu_read_unlock(vcpu);
752752
return -EINTR;
753753
}
754754

@@ -787,7 +787,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
787787
*/
788788
vcpu->mode = IN_GUEST_MODE;
789789

790-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
790+
kvm_vcpu_srcu_read_unlock(vcpu);
791791
smp_mb__after_srcu_read_unlock();
792792

793793
/*
@@ -805,7 +805,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
805805
vcpu->mode = OUTSIDE_GUEST_MODE;
806806
local_irq_enable();
807807
preempt_enable();
808-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
808+
kvm_vcpu_srcu_read_lock(vcpu);
809809
continue;
810810
}
811811

@@ -849,7 +849,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
849849

850850
preempt_enable();
851851

852-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
852+
kvm_vcpu_srcu_read_lock(vcpu);
853853

854854
ret = kvm_riscv_vcpu_exit(vcpu, run, &trap);
855855
}
@@ -858,7 +858,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
858858

859859
vcpu_put(vcpu);
860860

861-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
861+
kvm_vcpu_srcu_read_unlock(vcpu);
862862

863863
return ret;
864864
}

arch/riscv/kvm/vcpu_exit.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,9 @@ static int stage2_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
456456
void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu)
457457
{
458458
if (!kvm_arch_vcpu_runnable(vcpu)) {
459-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
459+
kvm_vcpu_srcu_read_unlock(vcpu);
460460
kvm_vcpu_halt(vcpu);
461-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
461+
kvm_vcpu_srcu_read_lock(vcpu);
462462
kvm_clear_request(KVM_REQ_UNHALT, vcpu);
463463
}
464464
}

arch/s390/kvm/interrupt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,11 +1334,11 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
13341334
hrtimer_start(&vcpu->arch.ckc_timer, sltime, HRTIMER_MODE_REL);
13351335
VCPU_EVENT(vcpu, 4, "enabled wait: %llu ns", sltime);
13361336
no_timer:
1337-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
1337+
kvm_vcpu_srcu_read_unlock(vcpu);
13381338
kvm_vcpu_halt(vcpu);
13391339
vcpu->valid_wakeup = false;
13401340
__unset_cpu_idle(vcpu);
1341-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
1341+
kvm_vcpu_srcu_read_lock(vcpu);
13421342

13431343
hrtimer_cancel(&vcpu->arch.ckc_timer);
13441344
return 0;

arch/s390/kvm/kvm-s390.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4237,14 +4237,14 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
42374237
* We try to hold kvm->srcu during most of vcpu_run (except when run-
42384238
* ning the guest), so that memslots (and other stuff) are protected
42394239
*/
4240-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
4240+
kvm_vcpu_srcu_read_lock(vcpu);
42414241

42424242
do {
42434243
rc = vcpu_pre_run(vcpu);
42444244
if (rc)
42454245
break;
42464246

4247-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
4247+
kvm_vcpu_srcu_read_unlock(vcpu);
42484248
/*
42494249
* As PF_VCPU will be used in fault handler, between
42504250
* guest_enter and guest_exit should be no uaccess.
@@ -4281,12 +4281,12 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
42814281
__enable_cpu_timer_accounting(vcpu);
42824282
guest_exit_irqoff();
42834283
local_irq_enable();
4284-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
4284+
kvm_vcpu_srcu_read_lock(vcpu);
42854285

42864286
rc = vcpu_post_run(vcpu, exit_reason);
42874287
} while (!signal_pending(current) && !guestdbg_exit_pending(vcpu) && !rc);
42884288

4289-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
4289+
kvm_vcpu_srcu_read_unlock(vcpu);
42904290
return rc;
42914291
}
42924292

arch/s390/kvm/vsie.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,7 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
10911091

10921092
handle_last_fault(vcpu, vsie_page);
10931093

1094-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
1094+
kvm_vcpu_srcu_read_unlock(vcpu);
10951095

10961096
/* save current guest state of bp isolation override */
10971097
guest_bp_isolation = test_thread_flag(TIF_ISOLATE_BP_GUEST);
@@ -1133,7 +1133,7 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
11331133
if (!guest_bp_isolation)
11341134
clear_thread_flag(TIF_ISOLATE_BP_GUEST);
11351135

1136-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
1136+
kvm_vcpu_srcu_read_lock(vcpu);
11371137

11381138
if (rc == -EINTR) {
11391139
VCPU_EVENT(vcpu, 3, "%s", "machine check");

arch/x86/kvm/x86.c

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10097,7 +10097,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1009710097
/* Store vcpu->apicv_active before vcpu->mode. */
1009810098
smp_store_release(&vcpu->mode, IN_GUEST_MODE);
1009910099

10100-
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
10100+
kvm_vcpu_srcu_read_unlock(vcpu);
1010110101

1010210102
/*
1010310103
* 1) We should set ->mode before checking ->requests. Please see
@@ -10128,7 +10128,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1012810128
smp_wmb();
1012910129
local_irq_enable();
1013010130
preempt_enable();
10131-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
10131+
kvm_vcpu_srcu_read_lock(vcpu);
1013210132
r = 1;
1013310133
goto cancel_injection;
1013410134
}
@@ -10254,7 +10254,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1025410254
local_irq_enable();
1025510255
preempt_enable();
1025610256

10257-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
10257+
kvm_vcpu_srcu_read_lock(vcpu);
1025810258

1025910259
/*
1026010260
* Profile KVM exit RIPs:
@@ -10284,7 +10284,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1028410284
}
1028510285

1028610286
/* Called within kvm->srcu read side. */
10287-
static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu)
10287+
static inline int vcpu_block(struct kvm_vcpu *vcpu)
1028810288
{
1028910289
bool hv_timer;
1029010290

@@ -10300,12 +10300,12 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu)
1030010300
if (hv_timer)
1030110301
kvm_lapic_switch_to_sw_timer(vcpu);
1030210302

10303-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
10303+
kvm_vcpu_srcu_read_unlock(vcpu);
1030410304
if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED)
1030510305
kvm_vcpu_halt(vcpu);
1030610306
else
1030710307
kvm_vcpu_block(vcpu);
10308-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
10308+
kvm_vcpu_srcu_read_lock(vcpu);
1030910309

1031010310
if (hv_timer)
1031110311
kvm_lapic_switch_to_hv_timer(vcpu);
@@ -10347,15 +10347,14 @@ static inline bool kvm_vcpu_running(struct kvm_vcpu *vcpu)
1034710347
static int vcpu_run(struct kvm_vcpu *vcpu)
1034810348
{
1034910349
int r;
10350-
struct kvm *kvm = vcpu->kvm;
1035110350

1035210351
vcpu->arch.l1tf_flush_l1d = true;
1035310352

1035410353
for (;;) {
1035510354
if (kvm_vcpu_running(vcpu)) {
1035610355
r = vcpu_enter_guest(vcpu);
1035710356
} else {
10358-
r = vcpu_block(kvm, vcpu);
10357+
r = vcpu_block(vcpu);
1035910358
}
1036010359

1036110360
if (r <= 0)
@@ -10374,9 +10373,9 @@ static int vcpu_run(struct kvm_vcpu *vcpu)
1037410373
}
1037510374

1037610375
if (__xfer_to_guest_mode_work_pending()) {
10377-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
10376+
kvm_vcpu_srcu_read_unlock(vcpu);
1037810377
r = xfer_to_guest_mode_handle_work(vcpu);
10379-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
10378+
kvm_vcpu_srcu_read_lock(vcpu);
1038010379
if (r)
1038110380
return r;
1038210381
}
@@ -10479,15 +10478,14 @@ static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
1047910478
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
1048010479
{
1048110480
struct kvm_run *kvm_run = vcpu->run;
10482-
struct kvm *kvm = vcpu->kvm;
1048310481
int r;
1048410482

1048510483
vcpu_load(vcpu);
1048610484
kvm_sigset_activate(vcpu);
1048710485
kvm_run->flags = 0;
1048810486
kvm_load_guest_fpu(vcpu);
1048910487

10490-
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
10488+
kvm_vcpu_srcu_read_lock(vcpu);
1049110489
if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
1049210490
if (kvm_run->immediate_exit) {
1049310491
r = -EINTR;
@@ -10499,9 +10497,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
1049910497
*/
1050010498
WARN_ON_ONCE(kvm_lapic_hv_timer_in_use(vcpu));
1050110499

10502-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
10500+
kvm_vcpu_srcu_read_unlock(vcpu);
1050310501
kvm_vcpu_block(vcpu);
10504-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
10502+
kvm_vcpu_srcu_read_lock(vcpu);
1050510503

1050610504
if (kvm_apic_accept_events(vcpu) < 0) {
1050710505
r = 0;
@@ -10562,7 +10560,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
1056210560
if (kvm_run->kvm_valid_regs)
1056310561
store_regs(vcpu);
1056410562
post_kvm_run_save(vcpu);
10565-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
10563+
kvm_vcpu_srcu_read_unlock(vcpu);
1056610564

1056710565
kvm_sigset_deactivate(vcpu);
1056810566
vcpu_put(vcpu);

0 commit comments

Comments
 (0)