162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2022 Google LLC 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define _GNU_SOURCE 662306a36Sopenharmony_ci#include <errno.h> 762306a36Sopenharmony_ci#include <stdbool.h> 862306a36Sopenharmony_ci#include <stdio.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <sys/syscall.h> 1162306a36Sopenharmony_ci#include <sys/wait.h> 1262306a36Sopenharmony_ci#include <unistd.h> 1362306a36Sopenharmony_ci#include <asm-generic/unistd.h> 1462306a36Sopenharmony_ci#include "vm_util.h" 1562306a36Sopenharmony_ci#include "../kselftest.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define MB(x) (x << 20) 1862306a36Sopenharmony_ci#define MAX_SIZE_MB 1024 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int alloc_noexit(unsigned long nr_pages, int pipefd) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int ppid = getppid(); 2362306a36Sopenharmony_ci int timeout = 10; /* 10sec timeout to get killed */ 2462306a36Sopenharmony_ci unsigned long i; 2562306a36Sopenharmony_ci char *buf; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci buf = (char *)mmap(NULL, nr_pages * psize(), PROT_READ | PROT_WRITE, 2862306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANON, 0, 0); 2962306a36Sopenharmony_ci if (buf == MAP_FAILED) { 3062306a36Sopenharmony_ci perror("mmap failed, halting the test"); 3162306a36Sopenharmony_ci return KSFT_FAIL; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) 3562306a36Sopenharmony_ci *((unsigned long *)(buf + (i * psize()))) = i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Signal the parent that the child is ready */ 3862306a36Sopenharmony_ci if (write(pipefd, "", 1) < 0) { 3962306a36Sopenharmony_ci perror("write"); 4062306a36Sopenharmony_ci return KSFT_FAIL; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* Wait to be killed (when reparenting happens) */ 4462306a36Sopenharmony_ci while (getppid() == ppid && timeout > 0) { 4562306a36Sopenharmony_ci sleep(1); 4662306a36Sopenharmony_ci timeout--; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci munmap(buf, nr_pages * psize()); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return (timeout > 0) ? KSFT_PASS : KSFT_FAIL; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* The process_mrelease calls in this test are expected to fail */ 5562306a36Sopenharmony_cistatic void run_negative_tests(int pidfd) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int res; 5862306a36Sopenharmony_ci /* Test invalid flags. Expect to fail with EINVAL error code. */ 5962306a36Sopenharmony_ci if (!syscall(__NR_process_mrelease, pidfd, (unsigned int)-1) || 6062306a36Sopenharmony_ci errno != EINVAL) { 6162306a36Sopenharmony_ci res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 6262306a36Sopenharmony_ci perror("process_mrelease with wrong flags"); 6362306a36Sopenharmony_ci exit(res); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Test reaping while process is alive with no pending SIGKILL. 6762306a36Sopenharmony_ci * Expect to fail with EINVAL error code. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci if (!syscall(__NR_process_mrelease, pidfd, 0) || errno != EINVAL) { 7062306a36Sopenharmony_ci res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 7162306a36Sopenharmony_ci perror("process_mrelease on a live process"); 7262306a36Sopenharmony_ci exit(res); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int child_main(int pipefd[], size_t size) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int res; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Allocate and fault-in memory and wait to be killed */ 8162306a36Sopenharmony_ci close(pipefd[0]); 8262306a36Sopenharmony_ci res = alloc_noexit(MB(size) / psize(), pipefd[1]); 8362306a36Sopenharmony_ci close(pipefd[1]); 8462306a36Sopenharmony_ci return res; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint main(void) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int pipefd[2], pidfd; 9062306a36Sopenharmony_ci bool success, retry; 9162306a36Sopenharmony_ci size_t size; 9262306a36Sopenharmony_ci pid_t pid; 9362306a36Sopenharmony_ci char byte; 9462306a36Sopenharmony_ci int res; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Test a wrong pidfd */ 9762306a36Sopenharmony_ci if (!syscall(__NR_process_mrelease, -1, 0) || errno != EBADF) { 9862306a36Sopenharmony_ci res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 9962306a36Sopenharmony_ci perror("process_mrelease with wrong pidfd"); 10062306a36Sopenharmony_ci exit(res); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Start the test with 1MB child memory allocation */ 10462306a36Sopenharmony_ci size = 1; 10562306a36Sopenharmony_ciretry: 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Pipe for the child to signal when it's done allocating 10862306a36Sopenharmony_ci * memory 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (pipe(pipefd)) { 11162306a36Sopenharmony_ci perror("pipe"); 11262306a36Sopenharmony_ci exit(KSFT_FAIL); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci pid = fork(); 11562306a36Sopenharmony_ci if (pid < 0) { 11662306a36Sopenharmony_ci perror("fork"); 11762306a36Sopenharmony_ci close(pipefd[0]); 11862306a36Sopenharmony_ci close(pipefd[1]); 11962306a36Sopenharmony_ci exit(KSFT_FAIL); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (pid == 0) { 12362306a36Sopenharmony_ci /* Child main routine */ 12462306a36Sopenharmony_ci res = child_main(pipefd, size); 12562306a36Sopenharmony_ci exit(res); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * Parent main routine: 13062306a36Sopenharmony_ci * Wait for the child to finish allocations, then kill and reap 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci close(pipefd[1]); 13362306a36Sopenharmony_ci /* Block until the child is ready */ 13462306a36Sopenharmony_ci res = read(pipefd[0], &byte, 1); 13562306a36Sopenharmony_ci close(pipefd[0]); 13662306a36Sopenharmony_ci if (res < 0) { 13762306a36Sopenharmony_ci perror("read"); 13862306a36Sopenharmony_ci if (!kill(pid, SIGKILL)) 13962306a36Sopenharmony_ci waitpid(pid, NULL, 0); 14062306a36Sopenharmony_ci exit(KSFT_FAIL); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci pidfd = syscall(__NR_pidfd_open, pid, 0); 14462306a36Sopenharmony_ci if (pidfd < 0) { 14562306a36Sopenharmony_ci perror("pidfd_open"); 14662306a36Sopenharmony_ci if (!kill(pid, SIGKILL)) 14762306a36Sopenharmony_ci waitpid(pid, NULL, 0); 14862306a36Sopenharmony_ci exit(KSFT_FAIL); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Run negative tests which require a live child */ 15262306a36Sopenharmony_ci run_negative_tests(pidfd); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (kill(pid, SIGKILL)) { 15562306a36Sopenharmony_ci res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 15662306a36Sopenharmony_ci perror("kill"); 15762306a36Sopenharmony_ci exit(res); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci success = (syscall(__NR_process_mrelease, pidfd, 0) == 0); 16162306a36Sopenharmony_ci if (!success) { 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * If we failed to reap because the child exited too soon, 16462306a36Sopenharmony_ci * before we could call process_mrelease. Double child's memory 16562306a36Sopenharmony_ci * which causes it to spend more time on cleanup and increases 16662306a36Sopenharmony_ci * our chances of reaping its memory before it exits. 16762306a36Sopenharmony_ci * Retry until we succeed or reach MAX_SIZE_MB. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (errno == ESRCH) { 17062306a36Sopenharmony_ci retry = (size <= MAX_SIZE_MB); 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL); 17362306a36Sopenharmony_ci perror("process_mrelease"); 17462306a36Sopenharmony_ci waitpid(pid, NULL, 0); 17562306a36Sopenharmony_ci exit(res); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Cleanup to prevent zombies */ 18062306a36Sopenharmony_ci if (waitpid(pid, NULL, 0) < 0) { 18162306a36Sopenharmony_ci perror("waitpid"); 18262306a36Sopenharmony_ci exit(KSFT_FAIL); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci close(pidfd); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!success) { 18762306a36Sopenharmony_ci if (retry) { 18862306a36Sopenharmony_ci size *= 2; 18962306a36Sopenharmony_ci goto retry; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci printf("All process_mrelease attempts failed!\n"); 19262306a36Sopenharmony_ci exit(KSFT_FAIL); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci printf("Success reaping a child with %zuMB of memory allocations\n", 19662306a36Sopenharmony_ci size); 19762306a36Sopenharmony_ci return KSFT_PASS; 19862306a36Sopenharmony_ci} 199