1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * This test is intended to reproduce a crash that happens when 4 * kvm_arch_hardware_disable is called and it attempts to unregister the user 5 * return notifiers. 6 */ 7 #include <fcntl.h> 8 #include <semaphore.h> 9 #include <stdint.h> 10 #include <stdlib.h> 11 #include <unistd.h> 12 #include <sys/wait.h> 13 14 #include <test_util.h> 15 16 #include "kvm_syscalls.h" 17 #include "kvm_util.h" 18 #include "ucall_common.h" 19 20 #define NR_VCPUS 4 21 #define NR_SLEEPERS_PER_VCPU 16 22 #define NR_ITERATIONS 512 23 #define DELAY_US_MAX 2000 24 25 static cpu_set_t threads_cpu_set; 26 static sem_t *sem; 27 28 static void guest_code(void) 29 { 30 for (;;) 31 ; /* Some busy work */ 32 GUEST_ASSERT(0); 33 } 34 35 static void *run_vcpu(void *arg) 36 { 37 struct kvm_vcpu *vcpu = arg; 38 struct kvm_run *run = vcpu->run; 39 40 #ifndef _GNU_SOURCE 41 kvm_sched_setaffinity(0, sizeof(cpu_set_t), &threads_cpu_set); 42 #endif 43 44 vcpu_run(vcpu); 45 46 TEST_FAIL("vCPU%d exited with reason %d: %s", 47 vcpu->id, run->exit_reason, exit_reason_str(run->exit_reason)); 48 } 49 50 static void *sleeping_thread(void *arg) 51 { 52 int fd; 53 54 #ifndef _GNU_SOURCE 55 kvm_sched_setaffinity(0, sizeof(cpu_set_t), &threads_cpu_set); 56 #endif 57 58 while (1) { 59 fd = open("/dev/null", O_RDWR); 60 close(fd); 61 } 62 TEST_FAIL("%s: exited", __func__); 63 } 64 65 static void run_test(u32 run) 66 { 67 struct kvm_vcpu *vcpu; 68 pthread_attr_t attr; 69 struct kvm_vm *vm; 70 pthread_t thread; 71 u32 i, j; 72 73 TEST_ASSERT_EQ(pthread_attr_init(&attr), 0); 74 #ifdef _GNU_SOURCE 75 TEST_ASSERT_EQ(pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &threads_cpu_set), 0); 76 #endif 77 78 vm = vm_create(NR_VCPUS); 79 80 pr_debug("%s: [%d] start vcpus\n", __func__, run); 81 for (i = 0; i < NR_VCPUS; ++i) { 82 vcpu = vm_vcpu_add(vm, i, guest_code); 83 84 kvm_pthread_create(&thread, &attr, run_vcpu, vcpu); 85 86 for (j = 0; j < NR_SLEEPERS_PER_VCPU; ++j) 87 kvm_pthread_create(&thread, &attr, sleeping_thread, (void *)NULL); 88 } 89 pr_debug("%s: [%d] all threads launched\n", __func__, run); 90 sem_post(sem); 91 92 /* Wait for the parent to SIGKILL this child. */ 93 while (1) 94 pause(); 95 } 96 97 void wait_for_child_setup(pid_t pid) 98 { 99 /* 100 * Wait for the child to post to the semaphore, but wake up periodically 101 * to check if the child exited prematurely. 102 */ 103 for (;;) { 104 const struct timespec wait_period = { .tv_sec = 1 }; 105 int status; 106 107 if (!sem_timedwait(sem, &wait_period)) 108 return; 109 110 /* Child is still running, keep waiting. */ 111 if (pid != waitpid(pid, &status, WNOHANG)) 112 continue; 113 114 /* 115 * Child is no longer running, which is not expected. 116 * 117 * If it exited with a non-zero status, we explicitly forward 118 * the child's status in case it exited with KSFT_SKIP. 119 */ 120 if (WIFEXITED(status)) 121 exit(WEXITSTATUS(status)); 122 else 123 TEST_ASSERT(false, "Child exited unexpectedly"); 124 } 125 } 126 127 int main(int argc, char **argv) 128 { 129 cpu_set_t allowed_cpu_set; 130 int s, r, cpu, i; 131 pid_t pid; 132 133 kvm_sched_getaffinity(0, sizeof(cpu_set_t), &allowed_cpu_set); 134 135 for (i = 0; i < NR_VCPUS && CPU_COUNT(&allowed_cpu_set); i++) { 136 cpu = kvm_pick_random_cpu(&allowed_cpu_set); 137 CPU_CLR(cpu, &allowed_cpu_set); 138 CPU_SET(cpu, &threads_cpu_set); 139 } 140 141 sem = sem_open("vm_sem", O_CREAT | O_EXCL, 0644, 0); 142 sem_unlink("vm_sem"); 143 144 for (i = 0; i < NR_ITERATIONS; ++i) { 145 pid = fork(); 146 TEST_ASSERT(pid >= 0, "%s: unable to fork", __func__); 147 if (pid == 0) 148 run_test(i); /* This function always exits */ 149 150 pr_debug("%s: [%d] waiting semaphore\n", __func__, i); 151 wait_for_child_setup(pid); 152 r = (rand() % DELAY_US_MAX) + 1; 153 pr_debug("%s: [%d] waiting %dus\n", __func__, i, r); 154 usleep(r); 155 r = waitpid(pid, &s, WNOHANG); 156 TEST_ASSERT(r != pid, 157 "%s: [%d] child exited unexpectedly status: [%d]", 158 __func__, i, s); 159 pr_debug("%s: [%d] killing child\n", __func__, i); 160 kill(pid, SIGKILL); 161 } 162 163 sem_destroy(sem); 164 exit(0); 165 } 166