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