Skip to content

Commit

Permalink
kselftests: Add test to check for rlimit changes in different user na…
Browse files Browse the repository at this point in the history
…mespaces

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>
  • Loading branch information
legionus authored and ebiederm committed Apr 30, 2021
1 parent d7c9e99 commit e4aebf0
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions tools/testing/selftests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ TARGETS += proc
TARGETS += pstore
TARGETS += ptrace
TARGETS += openat2
TARGETS += rlimits
TARGETS += rseq
TARGETS += rtc
TARGETS += seccomp
Expand Down
2 changes: 2 additions & 0 deletions tools/testing/selftests/rlimits/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
rlimits-per-userns
6 changes: 6 additions & 0 deletions tools/testing/selftests/rlimits/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later

CFLAGS += -Wall -O2 -g
TEST_GEN_PROGS := rlimits-per-userns

include ../lib.mk
1 change: 1 addition & 0 deletions tools/testing/selftests/rlimits/config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_USER_NS=y
161 changes: 161 additions & 0 deletions tools/testing/selftests/rlimits/rlimits-per-userns.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Author: Alexey Gladkov <gladkov.alexey@gmail.com>
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/stat.h>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <signal.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>

#define NR_CHILDS 2

static char *service_prog;
static uid_t user = 60000;
static uid_t group = 60000;

static void setrlimit_nproc(rlim_t n)
{
pid_t pid = getpid();
struct rlimit limit = {
.rlim_cur = n,
.rlim_max = n
};

warnx("(pid=%d): Setting RLIMIT_NPROC=%ld", pid, n);

if (setrlimit(RLIMIT_NPROC, &limit) < 0)
err(EXIT_FAILURE, "(pid=%d): setrlimit(RLIMIT_NPROC)", pid);
}

static pid_t fork_child(void)
{
pid_t pid = fork();

if (pid < 0)
err(EXIT_FAILURE, "fork");

if (pid > 0)
return pid;

pid = getpid();

warnx("(pid=%d): New process starting ...", pid);

if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
err(EXIT_FAILURE, "(pid=%d): prctl(PR_SET_PDEATHSIG)", pid);

signal(SIGUSR1, SIG_DFL);

warnx("(pid=%d): Changing to uid=%d, gid=%d", pid, user, group);

if (setgid(group) < 0)
err(EXIT_FAILURE, "(pid=%d): setgid(%d)", pid, group);
if (setuid(user) < 0)
err(EXIT_FAILURE, "(pid=%d): setuid(%d)", pid, user);

warnx("(pid=%d): Service running ...", pid);

warnx("(pid=%d): Unshare user namespace", pid);
if (unshare(CLONE_NEWUSER) < 0)
err(EXIT_FAILURE, "unshare(CLONE_NEWUSER)");

char *const argv[] = { "service", NULL };
char *const envp[] = { "I_AM_SERVICE=1", NULL };

warnx("(pid=%d): Executing real service ...", pid);

execve(service_prog, argv, envp);
err(EXIT_FAILURE, "(pid=%d): execve", pid);
}

int main(int argc, char **argv)
{
size_t i;
pid_t child[NR_CHILDS];
int wstatus[NR_CHILDS];
int childs = NR_CHILDS;
pid_t pid;

if (getenv("I_AM_SERVICE")) {
pause();
exit(EXIT_SUCCESS);
}

service_prog = argv[0];
pid = getpid();

warnx("(pid=%d) Starting testcase", pid);

/*
* This rlimit is not a problem for root because it can be exceeded.
*/
setrlimit_nproc(1);

for (i = 0; i < NR_CHILDS; i++) {
child[i] = fork_child();
wstatus[i] = 0;
usleep(250000);
}

while (1) {
for (i = 0; i < NR_CHILDS; i++) {
if (child[i] <= 0)
continue;

errno = 0;
pid_t ret = waitpid(child[i], &wstatus[i], WNOHANG);

if (!ret || (!WIFEXITED(wstatus[i]) && !WIFSIGNALED(wstatus[i])))
continue;

if (ret < 0 && errno != ECHILD)
warn("(pid=%d): waitpid(%d)", pid, child[i]);

child[i] *= -1;
childs -= 1;
}

if (!childs)
break;

usleep(250000);

for (i = 0; i < NR_CHILDS; i++) {
if (child[i] <= 0)
continue;
kill(child[i], SIGUSR1);
}
}

for (i = 0; i < NR_CHILDS; i++) {
if (WIFEXITED(wstatus[i]))
warnx("(pid=%d): pid %d exited, status=%d",
pid, -child[i], WEXITSTATUS(wstatus[i]));
else if (WIFSIGNALED(wstatus[i]))
warnx("(pid=%d): pid %d killed by signal %d",
pid, -child[i], WTERMSIG(wstatus[i]));

if (WIFSIGNALED(wstatus[i]) && WTERMSIG(wstatus[i]) == SIGUSR1)
continue;

warnx("(pid=%d): Test failed", pid);
exit(EXIT_FAILURE);
}

warnx("(pid=%d): Test passed", pid);
exit(EXIT_SUCCESS);
}

0 comments on commit e4aebf0

Please sign in to comment.