rlimits-per-userns.c (3325B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Author: Alexey Gladkov <gladkov.alexey@gmail.com> 4 */ 5#define _GNU_SOURCE 6#include <sys/types.h> 7#include <sys/wait.h> 8#include <sys/time.h> 9#include <sys/resource.h> 10#include <sys/prctl.h> 11#include <sys/stat.h> 12 13#include <unistd.h> 14#include <stdlib.h> 15#include <stdio.h> 16#include <string.h> 17#include <sched.h> 18#include <signal.h> 19#include <limits.h> 20#include <fcntl.h> 21#include <errno.h> 22#include <err.h> 23 24#define NR_CHILDS 2 25 26static char *service_prog; 27static uid_t user = 60000; 28static uid_t group = 60000; 29 30static void setrlimit_nproc(rlim_t n) 31{ 32 pid_t pid = getpid(); 33 struct rlimit limit = { 34 .rlim_cur = n, 35 .rlim_max = n 36 }; 37 38 warnx("(pid=%d): Setting RLIMIT_NPROC=%ld", pid, n); 39 40 if (setrlimit(RLIMIT_NPROC, &limit) < 0) 41 err(EXIT_FAILURE, "(pid=%d): setrlimit(RLIMIT_NPROC)", pid); 42} 43 44static pid_t fork_child(void) 45{ 46 pid_t pid = fork(); 47 48 if (pid < 0) 49 err(EXIT_FAILURE, "fork"); 50 51 if (pid > 0) 52 return pid; 53 54 pid = getpid(); 55 56 warnx("(pid=%d): New process starting ...", pid); 57 58 if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) 59 err(EXIT_FAILURE, "(pid=%d): prctl(PR_SET_PDEATHSIG)", pid); 60 61 signal(SIGUSR1, SIG_DFL); 62 63 warnx("(pid=%d): Changing to uid=%d, gid=%d", pid, user, group); 64 65 if (setgid(group) < 0) 66 err(EXIT_FAILURE, "(pid=%d): setgid(%d)", pid, group); 67 if (setuid(user) < 0) 68 err(EXIT_FAILURE, "(pid=%d): setuid(%d)", pid, user); 69 70 warnx("(pid=%d): Service running ...", pid); 71 72 warnx("(pid=%d): Unshare user namespace", pid); 73 if (unshare(CLONE_NEWUSER) < 0) 74 err(EXIT_FAILURE, "unshare(CLONE_NEWUSER)"); 75 76 char *const argv[] = { "service", NULL }; 77 char *const envp[] = { "I_AM_SERVICE=1", NULL }; 78 79 warnx("(pid=%d): Executing real service ...", pid); 80 81 execve(service_prog, argv, envp); 82 err(EXIT_FAILURE, "(pid=%d): execve", pid); 83} 84 85int main(int argc, char **argv) 86{ 87 size_t i; 88 pid_t child[NR_CHILDS]; 89 int wstatus[NR_CHILDS]; 90 int childs = NR_CHILDS; 91 pid_t pid; 92 93 if (getenv("I_AM_SERVICE")) { 94 pause(); 95 exit(EXIT_SUCCESS); 96 } 97 98 service_prog = argv[0]; 99 pid = getpid(); 100 101 warnx("(pid=%d) Starting testcase", pid); 102 103 /* 104 * This rlimit is not a problem for root because it can be exceeded. 105 */ 106 setrlimit_nproc(1); 107 108 for (i = 0; i < NR_CHILDS; i++) { 109 child[i] = fork_child(); 110 wstatus[i] = 0; 111 usleep(250000); 112 } 113 114 while (1) { 115 for (i = 0; i < NR_CHILDS; i++) { 116 if (child[i] <= 0) 117 continue; 118 119 errno = 0; 120 pid_t ret = waitpid(child[i], &wstatus[i], WNOHANG); 121 122 if (!ret || (!WIFEXITED(wstatus[i]) && !WIFSIGNALED(wstatus[i]))) 123 continue; 124 125 if (ret < 0 && errno != ECHILD) 126 warn("(pid=%d): waitpid(%d)", pid, child[i]); 127 128 child[i] *= -1; 129 childs -= 1; 130 } 131 132 if (!childs) 133 break; 134 135 usleep(250000); 136 137 for (i = 0; i < NR_CHILDS; i++) { 138 if (child[i] <= 0) 139 continue; 140 kill(child[i], SIGUSR1); 141 } 142 } 143 144 for (i = 0; i < NR_CHILDS; i++) { 145 if (WIFEXITED(wstatus[i])) 146 warnx("(pid=%d): pid %d exited, status=%d", 147 pid, -child[i], WEXITSTATUS(wstatus[i])); 148 else if (WIFSIGNALED(wstatus[i])) 149 warnx("(pid=%d): pid %d killed by signal %d", 150 pid, -child[i], WTERMSIG(wstatus[i])); 151 152 if (WIFSIGNALED(wstatus[i]) && WTERMSIG(wstatus[i]) == SIGUSR1) 153 continue; 154 155 warnx("(pid=%d): Test failed", pid); 156 exit(EXIT_FAILURE); 157 } 158 159 warnx("(pid=%d): Test passed", pid); 160 exit(EXIT_SUCCESS); 161}