Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <legion@kernel.org>
Link: https://lkml.kernel.org/r/28cafdcdd4abd8494b34a27f1970b666b30de8bf.1619094428.git.legion@kernel.org
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>

authored by

Alexey Gladkov and committed by
Eric W. Biederman
e4aebf06 d7c9e99a

+171
+1
tools/testing/selftests/Makefile
··· 48 48 TARGETS += pstore 49 49 TARGETS += ptrace 50 50 TARGETS += openat2 51 + TARGETS += rlimits 51 52 TARGETS += rseq 52 53 TARGETS += rtc 53 54 TARGETS += seccomp
+2
tools/testing/selftests/rlimits/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + rlimits-per-userns
+6
tools/testing/selftests/rlimits/Makefile
··· 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
+1
tools/testing/selftests/rlimits/config
··· 1 + CONFIG_USER_NS=y
+161
tools/testing/selftests/rlimits/rlimits-per-userns.c
··· 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 + }