diff mbox series

[2/2] KVM: selftests: Add tests for vcpu_array[0] races

Message ID 20230510140410.1093987-3-mhal@rbox.co (mailing list archive)
State New, archived
Headers show
Series KVM: vcpu_array[0] races | expand

Commit Message

Michal Luczaj May 10, 2023, 2:04 p.m. UTC
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

Comments

Sean Christopherson May 31, 2023, 10:08 p.m. UTC | #1
On Wed, May 10, 2023, Michal Luczaj wrote:
> 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.

Heh, yeah.  I'm inclined to leave this as a test that's archived on lkml, but
not merged into mainline.
diff mbox series

Patch

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 7a5ff646e7e7..6c253c0bb589 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -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
diff --git a/tools/testing/selftests/kvm/vcpu_array_races.c b/tools/testing/selftests/kvm/vcpu_array_races.c
new file mode 100644
index 000000000000..b1a4f6fcead5
--- /dev/null
+++ b/tools/testing/selftests/kvm/vcpu_array_races.c
@@ -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;
+}