Skip to content

Commit a51abbb

Browse files
mmhalbonzini
authored andcommitted
KVM: selftests: Add tests in xen_shinfo_test to detect lock races
Tests for races between shinfo_cache (de)activation and hypercall+ioctl() processing. KVM has had bugs where activating the shared info cache multiple times and/or with concurrent users results in lock corruption, NULL pointer dereferences, and other fun. For the timer injection testcase (#22), re-arm the timer until the IRQ is successfully injected. If the timer expires while the shared info is deactivated (invalid), KVM will drop the event. Signed-off-by: Michal Luczaj <mhal@rbox.co> Co-developed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20221013211234.1318131-16-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent ecbcf03 commit a51abbb

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c

+140
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
#include <time.h>
1616
#include <sched.h>
1717
#include <signal.h>
18+
#include <pthread.h>
1819

1920
#include <sys/eventfd.h>
2021

22+
/* Defined in include/linux/kvm_types.h */
23+
#define GPA_INVALID (~(ulong)0)
24+
2125
#define SHINFO_REGION_GVA 0xc0000000ULL
2226
#define SHINFO_REGION_GPA 0xc0000000ULL
2327
#define SHINFO_REGION_SLOT 10
@@ -44,6 +48,8 @@
4448

4549
#define MIN_STEAL_TIME 50000
4650

51+
#define SHINFO_RACE_TIMEOUT 2 /* seconds */
52+
4753
#define __HYPERVISOR_set_timer_op 15
4854
#define __HYPERVISOR_sched_op 29
4955
#define __HYPERVISOR_event_channel_op 32
@@ -148,6 +154,7 @@ static void guest_wait_for_irq(void)
148154
static void guest_code(void)
149155
{
150156
struct vcpu_runstate_info *rs = (void *)RUNSTATE_VADDR;
157+
int i;
151158

152159
__asm__ __volatile__(
153160
"sti\n"
@@ -325,6 +332,49 @@ static void guest_code(void)
325332
guest_wait_for_irq();
326333

327334
GUEST_SYNC(21);
335+
/* Racing host ioctls */
336+
337+
guest_wait_for_irq();
338+
339+
GUEST_SYNC(22);
340+
/* Racing vmcall against host ioctl */
341+
342+
ports[0] = 0;
343+
344+
p = (struct sched_poll) {
345+
.ports = ports,
346+
.nr_ports = 1,
347+
.timeout = 0
348+
};
349+
350+
wait_for_timer:
351+
/*
352+
* Poll for a timer wake event while the worker thread is mucking with
353+
* the shared info. KVM XEN drops timer IRQs if the shared info is
354+
* invalid when the timer expires. Arbitrarily poll 100 times before
355+
* giving up and asking the VMM to re-arm the timer. 100 polls should
356+
* consume enough time to beat on KVM without taking too long if the
357+
* timer IRQ is dropped due to an invalid event channel.
358+
*/
359+
for (i = 0; i < 100 && !guest_saw_irq; i++)
360+
asm volatile("vmcall"
361+
: "=a" (rax)
362+
: "a" (__HYPERVISOR_sched_op),
363+
"D" (SCHEDOP_poll),
364+
"S" (&p)
365+
: "memory");
366+
367+
/*
368+
* Re-send the timer IRQ if it was (likely) dropped due to the timer
369+
* expiring while the event channel was invalid.
370+
*/
371+
if (!guest_saw_irq) {
372+
GUEST_SYNC(23);
373+
goto wait_for_timer;
374+
}
375+
guest_saw_irq = false;
376+
377+
GUEST_SYNC(24);
328378
}
329379

330380
static int cmp_timespec(struct timespec *a, struct timespec *b)
@@ -352,11 +402,36 @@ static void handle_alrm(int sig)
352402
TEST_FAIL("IRQ delivery timed out");
353403
}
354404

405+
static void *juggle_shinfo_state(void *arg)
406+
{
407+
struct kvm_vm *vm = (struct kvm_vm *)arg;
408+
409+
struct kvm_xen_hvm_attr cache_init = {
410+
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
411+
.u.shared_info.gfn = SHINFO_REGION_GPA / PAGE_SIZE
412+
};
413+
414+
struct kvm_xen_hvm_attr cache_destroy = {
415+
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
416+
.u.shared_info.gfn = GPA_INVALID
417+
};
418+
419+
for (;;) {
420+
__vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_init);
421+
__vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_destroy);
422+
pthread_testcancel();
423+
};
424+
425+
return NULL;
426+
}
427+
355428
int main(int argc, char *argv[])
356429
{
357430
struct timespec min_ts, max_ts, vm_ts;
358431
struct kvm_vm *vm;
432+
pthread_t thread;
359433
bool verbose;
434+
int ret;
360435

361436
verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) ||
362437
!strncmp(argv[1], "--verbose", 10));
@@ -785,6 +860,71 @@ int main(int argc, char *argv[])
785860
case 21:
786861
TEST_ASSERT(!evtchn_irq_expected,
787862
"Expected event channel IRQ but it didn't happen");
863+
alarm(0);
864+
865+
if (verbose)
866+
printf("Testing shinfo lock corruption (KVM_XEN_HVM_EVTCHN_SEND)\n");
867+
868+
ret = pthread_create(&thread, NULL, &juggle_shinfo_state, (void *)vm);
869+
TEST_ASSERT(ret == 0, "pthread_create() failed: %s", strerror(ret));
870+
871+
struct kvm_irq_routing_xen_evtchn uxe = {
872+
.port = 1,
873+
.vcpu = vcpu->id,
874+
.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL
875+
};
876+
877+
evtchn_irq_expected = true;
878+
for (time_t t = time(NULL) + SHINFO_RACE_TIMEOUT; time(NULL) < t;)
879+
__vm_ioctl(vm, KVM_XEN_HVM_EVTCHN_SEND, &uxe);
880+
break;
881+
882+
case 22:
883+
TEST_ASSERT(!evtchn_irq_expected,
884+
"Expected event channel IRQ but it didn't happen");
885+
886+
if (verbose)
887+
printf("Testing shinfo lock corruption (SCHEDOP_poll)\n");
888+
889+
shinfo->evtchn_pending[0] = 1;
890+
891+
evtchn_irq_expected = true;
892+
tmr.u.timer.expires_ns = rs->state_entry_time +
893+
SHINFO_RACE_TIMEOUT * 1000000000ULL;
894+
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr);
895+
break;
896+
897+
case 23:
898+
/*
899+
* Optional and possibly repeated sync point.
900+
* Injecting the timer IRQ may fail if the
901+
* shinfo is invalid when the timer expires.
902+
* If the timer has expired but the IRQ hasn't
903+
* been delivered, rearm the timer and retry.
904+
*/
905+
vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr);
906+
907+
/* Resume the guest if the timer is still pending. */
908+
if (tmr.u.timer.expires_ns)
909+
break;
910+
911+
/* All done if the IRQ was delivered. */
912+
if (!evtchn_irq_expected)
913+
break;
914+
915+
tmr.u.timer.expires_ns = rs->state_entry_time +
916+
SHINFO_RACE_TIMEOUT * 1000000000ULL;
917+
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr);
918+
break;
919+
case 24:
920+
TEST_ASSERT(!evtchn_irq_expected,
921+
"Expected event channel IRQ but it didn't happen");
922+
923+
ret = pthread_cancel(thread);
924+
TEST_ASSERT(ret == 0, "pthread_cancel() failed: %s", strerror(ret));
925+
926+
ret = pthread_join(thread, 0);
927+
TEST_ASSERT(ret == 0, "pthread_join() failed: %s", strerror(ret));
788928
goto done;
789929

790930
case 0x20:

0 commit comments

Comments
 (0)