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

source code of linux/tools/testing/selftests/rlimits/rlimits-per-userns.c