Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2022 Google LLC
0004  */
0005 #define _GNU_SOURCE
0006 #include <errno.h>
0007 #include <stdbool.h>
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <sys/wait.h>
0011 #include <unistd.h>
0012 
0013 #include "util.h"
0014 
0015 #include "../kselftest.h"
0016 
0017 #ifndef __NR_pidfd_open
0018 #define __NR_pidfd_open -1
0019 #endif
0020 
0021 #ifndef __NR_process_mrelease
0022 #define __NR_process_mrelease -1
0023 #endif
0024 
0025 #define MB(x) (x << 20)
0026 #define MAX_SIZE_MB 1024
0027 
0028 static int alloc_noexit(unsigned long nr_pages, int pipefd)
0029 {
0030     int ppid = getppid();
0031     int timeout = 10; /* 10sec timeout to get killed */
0032     unsigned long i;
0033     char *buf;
0034 
0035     buf = (char *)mmap(NULL, nr_pages * PAGE_SIZE, PROT_READ | PROT_WRITE,
0036                MAP_PRIVATE | MAP_ANON, 0, 0);
0037     if (buf == MAP_FAILED) {
0038         perror("mmap failed, halting the test");
0039         return KSFT_FAIL;
0040     }
0041 
0042     for (i = 0; i < nr_pages; i++)
0043         *((unsigned long *)(buf + (i * PAGE_SIZE))) = i;
0044 
0045     /* Signal the parent that the child is ready */
0046     if (write(pipefd, "", 1) < 0) {
0047         perror("write");
0048         return KSFT_FAIL;
0049     }
0050 
0051     /* Wait to be killed (when reparenting happens) */
0052     while (getppid() == ppid && timeout > 0) {
0053         sleep(1);
0054         timeout--;
0055     }
0056 
0057     munmap(buf, nr_pages * PAGE_SIZE);
0058 
0059     return (timeout > 0) ? KSFT_PASS : KSFT_FAIL;
0060 }
0061 
0062 /* The process_mrelease calls in this test are expected to fail */
0063 static void run_negative_tests(int pidfd)
0064 {
0065     int res;
0066     /* Test invalid flags. Expect to fail with EINVAL error code. */
0067     if (!syscall(__NR_process_mrelease, pidfd, (unsigned int)-1) ||
0068             errno != EINVAL) {
0069         res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
0070         perror("process_mrelease with wrong flags");
0071         exit(res);
0072     }
0073     /*
0074      * Test reaping while process is alive with no pending SIGKILL.
0075      * Expect to fail with EINVAL error code.
0076      */
0077     if (!syscall(__NR_process_mrelease, pidfd, 0) || errno != EINVAL) {
0078         res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
0079         perror("process_mrelease on a live process");
0080         exit(res);
0081     }
0082 }
0083 
0084 static int child_main(int pipefd[], size_t size)
0085 {
0086     int res;
0087 
0088     /* Allocate and fault-in memory and wait to be killed */
0089     close(pipefd[0]);
0090     res = alloc_noexit(MB(size) / PAGE_SIZE, pipefd[1]);
0091     close(pipefd[1]);
0092     return res;
0093 }
0094 
0095 int main(void)
0096 {
0097     int pipefd[2], pidfd;
0098     bool success, retry;
0099     size_t size;
0100     pid_t pid;
0101     char byte;
0102     int res;
0103 
0104     /* Test a wrong pidfd */
0105     if (!syscall(__NR_process_mrelease, -1, 0) || errno != EBADF) {
0106         res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
0107         perror("process_mrelease with wrong pidfd");
0108         exit(res);
0109     }
0110 
0111     /* Start the test with 1MB child memory allocation */
0112     size = 1;
0113 retry:
0114     /*
0115      * Pipe for the child to signal when it's done allocating
0116      * memory
0117      */
0118     if (pipe(pipefd)) {
0119         perror("pipe");
0120         exit(KSFT_FAIL);
0121     }
0122     pid = fork();
0123     if (pid < 0) {
0124         perror("fork");
0125         close(pipefd[0]);
0126         close(pipefd[1]);
0127         exit(KSFT_FAIL);
0128     }
0129 
0130     if (pid == 0) {
0131         /* Child main routine */
0132         res = child_main(pipefd, size);
0133         exit(res);
0134     }
0135 
0136     /*
0137      * Parent main routine:
0138      * Wait for the child to finish allocations, then kill and reap
0139      */
0140     close(pipefd[1]);
0141     /* Block until the child is ready */
0142     res = read(pipefd[0], &byte, 1);
0143     close(pipefd[0]);
0144     if (res < 0) {
0145         perror("read");
0146         if (!kill(pid, SIGKILL))
0147             waitpid(pid, NULL, 0);
0148         exit(KSFT_FAIL);
0149     }
0150 
0151     pidfd = syscall(__NR_pidfd_open, pid, 0);
0152     if (pidfd < 0) {
0153         perror("pidfd_open");
0154         if (!kill(pid, SIGKILL))
0155             waitpid(pid, NULL, 0);
0156         exit(KSFT_FAIL);
0157     }
0158 
0159     /* Run negative tests which require a live child */
0160     run_negative_tests(pidfd);
0161 
0162     if (kill(pid, SIGKILL)) {
0163         res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
0164         perror("kill");
0165         exit(res);
0166     }
0167 
0168     success = (syscall(__NR_process_mrelease, pidfd, 0) == 0);
0169     if (!success) {
0170         /*
0171          * If we failed to reap because the child exited too soon,
0172          * before we could call process_mrelease. Double child's memory
0173          * which causes it to spend more time on cleanup and increases
0174          * our chances of reaping its memory before it exits.
0175          * Retry until we succeed or reach MAX_SIZE_MB.
0176          */
0177         if (errno == ESRCH) {
0178             retry = (size <= MAX_SIZE_MB);
0179         } else {
0180             res = (errno == ENOSYS ? KSFT_SKIP : KSFT_FAIL);
0181             perror("process_mrelease");
0182             waitpid(pid, NULL, 0);
0183             exit(res);
0184         }
0185     }
0186 
0187     /* Cleanup to prevent zombies */
0188     if (waitpid(pid, NULL, 0) < 0) {
0189         perror("waitpid");
0190         exit(KSFT_FAIL);
0191     }
0192     close(pidfd);
0193 
0194     if (!success) {
0195         if (retry) {
0196             size *= 2;
0197             goto retry;
0198         }
0199         printf("All process_mrelease attempts failed!\n");
0200         exit(KSFT_FAIL);
0201     }
0202 
0203     printf("Success reaping a child with %zuMB of memory allocations\n",
0204            size);
0205     return KSFT_PASS;
0206 }