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@xxxxxxx> --- 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 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; +} -- 2.40.1