123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Author: Alexey Gladkov <[email protected]>
- */
- #define _GNU_SOURCE
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include <sys/prctl.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <sched.h>
- #include <signal.h>
- #include <limits.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <err.h>
- #define NR_CHILDS 2
- static char *service_prog;
- static uid_t user = 60000;
- static uid_t group = 60000;
- static void setrlimit_nproc(rlim_t n)
- {
- pid_t pid = getpid();
- struct rlimit limit = {
- .rlim_cur = n,
- .rlim_max = n
- };
- warnx("(pid=%d): Setting RLIMIT_NPROC=%ld", pid, n);
- if (setrlimit(RLIMIT_NPROC, &limit) < 0)
- err(EXIT_FAILURE, "(pid=%d): setrlimit(RLIMIT_NPROC)", pid);
- }
- static pid_t fork_child(void)
- {
- pid_t pid = fork();
- if (pid < 0)
- err(EXIT_FAILURE, "fork");
- if (pid > 0)
- return pid;
- pid = getpid();
- warnx("(pid=%d): New process starting ...", pid);
- if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
- err(EXIT_FAILURE, "(pid=%d): prctl(PR_SET_PDEATHSIG)", pid);
- signal(SIGUSR1, SIG_DFL);
- warnx("(pid=%d): Changing to uid=%d, gid=%d", pid, user, group);
- if (setgid(group) < 0)
- err(EXIT_FAILURE, "(pid=%d): setgid(%d)", pid, group);
- if (setuid(user) < 0)
- err(EXIT_FAILURE, "(pid=%d): setuid(%d)", pid, user);
- warnx("(pid=%d): Service running ...", pid);
- warnx("(pid=%d): Unshare user namespace", pid);
- if (unshare(CLONE_NEWUSER) < 0)
- err(EXIT_FAILURE, "unshare(CLONE_NEWUSER)");
- char *const argv[] = { "service", NULL };
- char *const envp[] = { "I_AM_SERVICE=1", NULL };
- warnx("(pid=%d): Executing real service ...", pid);
- execve(service_prog, argv, envp);
- err(EXIT_FAILURE, "(pid=%d): execve", pid);
- }
- int main(int argc, char **argv)
- {
- size_t i;
- pid_t child[NR_CHILDS];
- int wstatus[NR_CHILDS];
- int childs = NR_CHILDS;
- pid_t pid;
- if (getenv("I_AM_SERVICE")) {
- pause();
- exit(EXIT_SUCCESS);
- }
- service_prog = argv[0];
- pid = getpid();
- warnx("(pid=%d) Starting testcase", pid);
- /*
- * This rlimit is not a problem for root because it can be exceeded.
- */
- setrlimit_nproc(1);
- for (i = 0; i < NR_CHILDS; i++) {
- child[i] = fork_child();
- wstatus[i] = 0;
- usleep(250000);
- }
- while (1) {
- for (i = 0; i < NR_CHILDS; i++) {
- if (child[i] <= 0)
- continue;
- errno = 0;
- pid_t ret = waitpid(child[i], &wstatus[i], WNOHANG);
- if (!ret || (!WIFEXITED(wstatus[i]) && !WIFSIGNALED(wstatus[i])))
- continue;
- if (ret < 0 && errno != ECHILD)
- warn("(pid=%d): waitpid(%d)", pid, child[i]);
- child[i] *= -1;
- childs -= 1;
- }
- if (!childs)
- break;
- usleep(250000);
- for (i = 0; i < NR_CHILDS; i++) {
- if (child[i] <= 0)
- continue;
- kill(child[i], SIGUSR1);
- }
- }
- for (i = 0; i < NR_CHILDS; i++) {
- if (WIFEXITED(wstatus[i]))
- warnx("(pid=%d): pid %d exited, status=%d",
- pid, -child[i], WEXITSTATUS(wstatus[i]));
- else if (WIFSIGNALED(wstatus[i]))
- warnx("(pid=%d): pid %d killed by signal %d",
- pid, -child[i], WTERMSIG(wstatus[i]));
- if (WIFSIGNALED(wstatus[i]) && WTERMSIG(wstatus[i]) == SIGUSR1)
- continue;
- warnx("(pid=%d): Test failed", pid);
- exit(EXIT_FAILURE);
- }
- warnx("(pid=%d): Test passed", pid);
- exit(EXIT_SUCCESS);
- }
|