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 
26 static char *service_prog;
27 static uid_t user   = 60000;
28 static uid_t group  = 60000;
29 
30 static 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 
44 static 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 
85 int 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