@@ -131,6 +131,7 @@ TEST_GEN_PROGS_x86_64 += set_memory_region_test
TEST_GEN_PROGS_x86_64 += steal_time
TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
TEST_GEN_PROGS_x86_64 += system_counter_offset_test
+TEST_GEN_PROGS_x86_64 += vcpu_array_races
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test
new file mode 100644
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * vcpu_array_races
+ *
+ * Tests for vcpu_array[0] races between KVM_CREATE_VCPU and
+ * KVM_IRQ_ROUTING_XEN_EVTCHN, KVM_RESET_DIRTY_RINGS,
+ * KVM_SET_PMU_EVENT_FILTER, KVM_X86_SET_MSR_FILTER.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include "test_util.h"
+
+#include "kvm_util.h"
+#include "asm/kvm.h"
+#include "linux/kvm.h"
+
+struct rlimit rl;
+
+static struct kvm_vm *setup_vm(void)
+{
+ struct rlimit reduced;
+ struct kvm_vm *vm;
+
+ vm = vm_create_barebones();
+
+ /* Required for racing against DIRTY_RINGS. */
+ vm_enable_cap(vm, KVM_CAP_DIRTY_LOG_RING, 1 << 16);
+
+ /* Required for racing against XEN_EVTCHN. */
+ vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL);
+
+ /* Make KVM_CREATE_VCPU fail. */
+ reduced = (struct rlimit) {0, rl.rlim_max};
+ TEST_ASSERT(!setrlimit(RLIMIT_NOFILE, &reduced), "setrlimit() failed");
+
+ return vm;
+}
+
+static void vcpu_array_race(void *(*racer)(void *), int tout)
+{
+ struct kvm_vm *vm;
+ pthread_t thread;
+ time_t t;
+ int ret;
+
+ vm = setup_vm();
+
+ TEST_ASSERT(!pthread_create(&thread, NULL, racer, (void *)vm),
+ "pthread_create() failed");
+
+ while (tout--) {
+ for (t = time(NULL); t == time(NULL);) {
+ ret = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)0);
+ TEST_ASSERT(ret == -1 && errno == EMFILE,
+ "KVM_CREATE_VCPU ret: %d, errno: %d",
+ ret, errno);
+ };
+ pr_info(".");
+ }
+
+ TEST_ASSERT(!pthread_cancel(thread), "pthread_cancel() failed");
+ TEST_ASSERT(!pthread_join(thread, NULL), "pthread_join() failed");
+
+ pr_info("\n");
+ kvm_vm_release(vm);
+
+ TEST_ASSERT(!setrlimit(RLIMIT_NOFILE, &rl), "setrlimit() failed");
+}
+
+static void *dirty_rings(void *arg)
+{
+ struct kvm_vm *vm = (struct kvm_vm *)arg;
+
+ while (1) {
+ vm_ioctl(vm, KVM_RESET_DIRTY_RINGS, NULL);
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+static void *xen_evtchn(void *arg)
+{
+ struct kvm_vm *vm = (struct kvm_vm *)arg;
+
+ struct {
+ struct kvm_irq_routing info;
+ struct kvm_irq_routing_entry entry;
+ } routing = {
+ .info = {
+ .nr = 1,
+ .flags = 0
+ },
+ .entry = {
+ .gsi = 0,
+ .type = KVM_IRQ_ROUTING_XEN_EVTCHN,
+ .flags = 0,
+ .u.xen_evtchn = {
+ .port = 0,
+ .vcpu = 0,
+ .priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL
+ }
+ }
+ };
+
+ struct kvm_irq_level irq = {
+ .irq = 0,
+ .level = 1
+ };
+
+ while (1) {
+ vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing.info);
+ vm_ioctl(vm, KVM_IRQ_LINE, &irq);
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+static void *pmu_event_filter(void *arg)
+{
+ struct kvm_vm *vm = (struct kvm_vm *)arg;
+
+ struct kvm_pmu_event_filter filter = {
+ .action = KVM_PMU_EVENT_ALLOW,
+ .flags = 0,
+ .nevents = 0
+ };
+
+ while (1) {
+ vm_ioctl(vm, KVM_SET_PMU_EVENT_FILTER, &filter);
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+static void *msr_filter(void *arg)
+{
+ struct kvm_vm *vm = (struct kvm_vm *)arg;
+
+ struct kvm_msr_filter filter = {
+ .flags = 0,
+ .ranges = {{0}}
+ };
+
+ while (1) {
+ vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter);
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+int main(void)
+{
+ TEST_ASSERT(!getrlimit(RLIMIT_NOFILE, &rl), "getrlimit() failed");
+
+ pr_info("Testing vcpu_array races\n");
+
+ /*
+ * BUG: KASAN: user-memory-access in kvm_xen_set_evtchn_fast+0xce/0x660 [kvm]
+ * Read of size 1 at addr 00000000000011ec by task a.out/954
+ */
+ pr_info("KVM_IRQ_ROUTING_XEN_EVTCHN\n");
+ vcpu_array_race(xen_evtchn, 5);
+
+ /*
+ * BUG: KASAN: vmalloc-out-of-bounds in kvm_dirty_ring_reset+0x6c/0x2b0 [kvm]
+ * Read of size 4 at addr ffffc90009150000 by task a.out/954
+ */
+ pr_info("KVM_RESET_DIRTY_RINGS\n");
+ vcpu_array_race(dirty_rings, 15);
+
+ /*
+ * BUG: KASAN: slab-use-after-free in rcuwait_wake_up+0x47/0x160
+ * Read of size 8 at addr ffff888149545260 by task a.out/952
+ */
+ pr_info("KVM_SET_PMU_EVENT_FILTER (takes 10 minutes)\n");
+ vcpu_array_race(pmu_event_filter, 10 * 60);
+
+ /*
+ * BUG: KASAN: slab-use-after-free in kvm_make_vcpu_request+0x6b/0x120 [kvm]
+ * Write of size 4 at addr ffff88810a15d1b4 by task a.out/955
+ */
+ pr_info("KVM_X86_SET_MSR_FILTER (takes 15 minutes)\n");
+ vcpu_array_race(msr_filter, 15 * 60);
+
+ return 0;
+}
Exercise races between xa_insert()+xa_erase() in KVM_CREATE_VCPU vs. users of kvm_get_vcpu() and kvm_for_each_vcpu(): KVM_IRQ_ROUTING_XEN_EVTCHN, KVM_RESET_DIRTY_RINGS, KVM_SET_PMU_EVENT_FILTER, KVM_X86_SET_MSR_FILTER. Warning: long time-outs. Signed-off-by: Michal Luczaj <mhal@rbox.co> --- tools/testing/selftests/kvm/Makefile | 1 + .../testing/selftests/kvm/vcpu_array_races.c | 198 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 tools/testing/selftests/kvm/vcpu_array_races.c