1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2022 Google LLC 4 */ 5 #define _GNU_SOURCE 6 #include <errno.h> 7 #include <stdbool.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <sys/syscall.h> 11 #include <sys/wait.h> 12 #include <unistd.h> 13 #include <asm-generic/unistd.h> 14 #include "vm_util.h" 15 #include "../kselftest.h" 16 17 #define MB(x) (x << 20) 18 #define MAX_SIZE_MB 1024 19 20 static int alloc_noexit(unsigned long nr_pages, int pipefd) 21 { 22 int ppid = getppid(); 23 int timeout = 10; /* 10sec timeout to get killed */ 24 unsigned long i; 25 char *buf; 26 27 buf = (char *)mmap(NULL, nr_pages * psize(), PROT_READ | PROT_WRITE, 28 MAP_PRIVATE | MAP_ANON, 0, 0); 29 if (buf == MAP_FAILED) { 30 perror("mmap failed, halting the test"); 31 return KSFT_FAIL; 32 } 33 34 for (i = 0; i < nr_pages; i++) 35 *((unsigned long *)(buf + (i * psize()))) = i; 36 37 /* Signal the parent that the child is ready */ 38 if (write(pipefd, "", 1) < 0) { 39 perror("write"); 40 return KSFT_FAIL; 41 } 42 43 /* Wait to be killed (when reparenting happens) */ 44 while (getppid() == ppid && timeout > 0) { 45 sleep(1); 46 timeout--; 47 } 48 49 munmap(buf, nr_pages * psize()); 50 51 return (timeout > 0) ? KSFT_PASS : KSFT_FAIL; 52 } 53 54 /* The process_mrelease calls in this test are expected to fail */ 55 static void run_negative_tests(int pidfd) 56 { 57 int res; 58 /* Test invalid flags. Expect to fail with EINVAL error code. */ 59 if (!syscall(__NR_process_mrelease, pidfd, (unsigned int)-1) || 60 errno != EINVAL) { 61 res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 62 perror("process_mrelease with wrong flags"); 63 exit(res); 64 } 65 /* 66 * Test reaping while process is alive with no pending SIGKILL. 67 * Expect to fail with EINVAL error code. 68 */ 69 if (!syscall(__NR_process_mrelease, pidfd, 0) || errno != EINVAL) { 70 res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 71 perror("process_mrelease on a live process"); 72 exit(res); 73 } 74 } 75 76 static int child_main(int pipefd[], size_t size) 77 { 78 int res; 79 80 /* Allocate and fault-in memory and wait to be killed */ 81 close(pipefd[0]); 82 res = alloc_noexit(MB(size) / psize(), pipefd[1]); 83 close(pipefd[1]); 84 return res; 85 } 86 87 int main(void) 88 { 89 int pipefd[2], pidfd; 90 bool success, retry; 91 size_t size; 92 pid_t pid; 93 char byte; 94 int res; 95 96 /* Test a wrong pidfd */ 97 if (!syscall(__NR_process_mrelease, -1, 0) || errno != EBADF) { 98 res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 99 perror("process_mrelease with wrong pidfd"); 100 exit(res); 101 } 102 103 /* Start the test with 1MB child memory allocation */ 104 size = 1; 105 retry: 106 /* 107 * Pipe for the child to signal when it's done allocating 108 * memory 109 */ 110 if (pipe(pipefd)) { 111 perror("pipe"); 112 exit(KSFT_FAIL); 113 } 114 pid = fork(); 115 if (pid < 0) { 116 perror("fork"); 117 close(pipefd[0]); 118 close(pipefd[1]); 119 exit(KSFT_FAIL); 120 } 121 122 if (pid == 0) { 123 /* Child main routine */ 124 res = child_main(pipefd, size); 125 exit(res); 126 } 127 128 /* 129 * Parent main routine: 130 * Wait for the child to finish allocations, then kill and reap 131 */ 132 close(pipefd[1]); 133 /* Block until the child is ready */ 134 res = read(pipefd[0], &byte, 1); 135 close(pipefd[0]); 136 if (res < 0) { 137 perror("read"); 138 if (!kill(pid, SIGKILL)) 139 waitpid(pid, NULL, 0); 140 exit(KSFT_FAIL); 141 } 142 143 pidfd = syscall(__NR_pidfd_open, pid, 0); 144 if (pidfd < 0) { 145 perror("pidfd_open"); 146 if (!kill(pid, SIGKILL)) 147 waitpid(pid, NULL, 0); 148 exit(KSFT_FAIL); 149 } 150 151 /* Run negative tests which require a live child */ 152 run_negative_tests(pidfd); 153 154 if (kill(pid, SIGKILL)) { 155 res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 156 perror("kill"); 157 exit(res); 158 } 159 160 success = (syscall(__NR_process_mrelease, pidfd, 0) == 0); 161 if (!success) { 162 /* 163 * If we failed to reap because the child exited too soon, 164 * before we could call process_mrelease. Double child's memory 165 * which causes it to spend more time on cleanup and increases 166 * our chances of reaping its memory before it exits. 167 * Retry until we succeed or reach MAX_SIZE_MB. 168 */ 169 if (errno == ESRCH) { 170 retry = (size <= MAX_SIZE_MB); 171 } else { 172 res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 173 perror("process_mrelease"); 174 waitpid(pid, NULL, 0); 175 exit(res); 176 } 177 } 178 179 /* Cleanup to prevent zombies */ 180 if (waitpid(pid, NULL, 0) < 0) { 181 perror("waitpid"); 182 exit(KSFT_FAIL); 183 } 184 close(pidfd); 185 186 if (!success) { 187 if (retry) { 188 size *= 2; 189 goto retry; 190 } 191 printf("All process_mrelease attempts failed!\n"); 192 exit(KSFT_FAIL); 193 } 194 195 printf("Success reaping a child with %zuMB of memory allocations\n", 196 size); 197 return KSFT_PASS; 198 } 199