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}
162