Skip to content

Commit e4aebf0

Browse files
legionusebiederm
authored andcommitted
kselftests: Add test to check for rlimit changes in different user namespaces
The testcase runs few instances of the program with RLIMIT_NPROC=1 from user uid=60000, in different user namespaces. Signed-off-by: Alexey Gladkov <[email protected]> Link: https://lkml.kernel.org/r/28cafdcdd4abd8494b34a27f1970b666b30de8bf.1619094428.git.legion@kernel.org Signed-off-by: Eric W. Biederman <[email protected]>
1 parent d7c9e99 commit e4aebf0

File tree

5 files changed

+171
-0
lines changed

5 files changed

+171
-0
lines changed

tools/testing/selftests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ TARGETS += proc
4848
TARGETS += pstore
4949
TARGETS += ptrace
5050
TARGETS += openat2
51+
TARGETS += rlimits
5152
TARGETS += rseq
5253
TARGETS += rtc
5354
TARGETS += seccomp
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
rlimits-per-userns
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
CFLAGS += -Wall -O2 -g
4+
TEST_GEN_PROGS := rlimits-per-userns
5+
6+
include ../lib.mk
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_USER_NS=y
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Author: Alexey Gladkov <[email protected]>
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+
}

0 commit comments

Comments
 (0)