162 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Author: Alexey Gladkov <gladkov.alexey@gmail.com>4 */5#define _GNU_SOURCE6#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 225 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 = n36 };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}162