162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author: Alexey Gladkov <gladkov.alexey@gmail.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define _GNU_SOURCE 662306a36Sopenharmony_ci#include <sys/types.h> 762306a36Sopenharmony_ci#include <sys/wait.h> 862306a36Sopenharmony_ci#include <sys/time.h> 962306a36Sopenharmony_ci#include <sys/resource.h> 1062306a36Sopenharmony_ci#include <sys/prctl.h> 1162306a36Sopenharmony_ci#include <sys/stat.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <stdlib.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <string.h> 1762306a36Sopenharmony_ci#include <sched.h> 1862306a36Sopenharmony_ci#include <signal.h> 1962306a36Sopenharmony_ci#include <limits.h> 2062306a36Sopenharmony_ci#include <fcntl.h> 2162306a36Sopenharmony_ci#include <errno.h> 2262306a36Sopenharmony_ci#include <err.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define NR_CHILDS 2 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic char *service_prog; 2762306a36Sopenharmony_cistatic uid_t user = 60000; 2862306a36Sopenharmony_cistatic uid_t group = 60000; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void setrlimit_nproc(rlim_t n) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci pid_t pid = getpid(); 3362306a36Sopenharmony_ci struct rlimit limit = { 3462306a36Sopenharmony_ci .rlim_cur = n, 3562306a36Sopenharmony_ci .rlim_max = n 3662306a36Sopenharmony_ci }; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci warnx("(pid=%d): Setting RLIMIT_NPROC=%ld", pid, n); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (setrlimit(RLIMIT_NPROC, &limit) < 0) 4162306a36Sopenharmony_ci err(EXIT_FAILURE, "(pid=%d): setrlimit(RLIMIT_NPROC)", pid); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic pid_t fork_child(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci pid_t pid = fork(); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (pid < 0) 4962306a36Sopenharmony_ci err(EXIT_FAILURE, "fork"); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (pid > 0) 5262306a36Sopenharmony_ci return pid; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci pid = getpid(); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci warnx("(pid=%d): New process starting ...", pid); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) 5962306a36Sopenharmony_ci err(EXIT_FAILURE, "(pid=%d): prctl(PR_SET_PDEATHSIG)", pid); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci signal(SIGUSR1, SIG_DFL); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci warnx("(pid=%d): Changing to uid=%d, gid=%d", pid, user, group); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (setgid(group) < 0) 6662306a36Sopenharmony_ci err(EXIT_FAILURE, "(pid=%d): setgid(%d)", pid, group); 6762306a36Sopenharmony_ci if (setuid(user) < 0) 6862306a36Sopenharmony_ci err(EXIT_FAILURE, "(pid=%d): setuid(%d)", pid, user); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci warnx("(pid=%d): Service running ...", pid); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci warnx("(pid=%d): Unshare user namespace", pid); 7362306a36Sopenharmony_ci if (unshare(CLONE_NEWUSER) < 0) 7462306a36Sopenharmony_ci err(EXIT_FAILURE, "unshare(CLONE_NEWUSER)"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci char *const argv[] = { "service", NULL }; 7762306a36Sopenharmony_ci char *const envp[] = { "I_AM_SERVICE=1", NULL }; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci warnx("(pid=%d): Executing real service ...", pid); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci execve(service_prog, argv, envp); 8262306a36Sopenharmony_ci err(EXIT_FAILURE, "(pid=%d): execve", pid); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciint main(int argc, char **argv) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci size_t i; 8862306a36Sopenharmony_ci pid_t child[NR_CHILDS]; 8962306a36Sopenharmony_ci int wstatus[NR_CHILDS]; 9062306a36Sopenharmony_ci int childs = NR_CHILDS; 9162306a36Sopenharmony_ci pid_t pid; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (getenv("I_AM_SERVICE")) { 9462306a36Sopenharmony_ci pause(); 9562306a36Sopenharmony_ci exit(EXIT_SUCCESS); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci service_prog = argv[0]; 9962306a36Sopenharmony_ci pid = getpid(); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci warnx("(pid=%d) Starting testcase", pid); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * This rlimit is not a problem for root because it can be exceeded. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci setrlimit_nproc(1); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci for (i = 0; i < NR_CHILDS; i++) { 10962306a36Sopenharmony_ci child[i] = fork_child(); 11062306a36Sopenharmony_ci wstatus[i] = 0; 11162306a36Sopenharmony_ci usleep(250000); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci while (1) { 11562306a36Sopenharmony_ci for (i = 0; i < NR_CHILDS; i++) { 11662306a36Sopenharmony_ci if (child[i] <= 0) 11762306a36Sopenharmony_ci continue; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci errno = 0; 12062306a36Sopenharmony_ci pid_t ret = waitpid(child[i], &wstatus[i], WNOHANG); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!ret || (!WIFEXITED(wstatus[i]) && !WIFSIGNALED(wstatus[i]))) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (ret < 0 && errno != ECHILD) 12662306a36Sopenharmony_ci warn("(pid=%d): waitpid(%d)", pid, child[i]); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci child[i] *= -1; 12962306a36Sopenharmony_ci childs -= 1; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!childs) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci usleep(250000); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < NR_CHILDS; i++) { 13862306a36Sopenharmony_ci if (child[i] <= 0) 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci kill(child[i], SIGUSR1); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (i = 0; i < NR_CHILDS; i++) { 14562306a36Sopenharmony_ci if (WIFEXITED(wstatus[i])) 14662306a36Sopenharmony_ci warnx("(pid=%d): pid %d exited, status=%d", 14762306a36Sopenharmony_ci pid, -child[i], WEXITSTATUS(wstatus[i])); 14862306a36Sopenharmony_ci else if (WIFSIGNALED(wstatus[i])) 14962306a36Sopenharmony_ci warnx("(pid=%d): pid %d killed by signal %d", 15062306a36Sopenharmony_ci pid, -child[i], WTERMSIG(wstatus[i])); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (WIFSIGNALED(wstatus[i]) && WTERMSIG(wstatus[i]) == SIGUSR1) 15362306a36Sopenharmony_ci continue; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci warnx("(pid=%d): Test failed", pid); 15662306a36Sopenharmony_ci exit(EXIT_FAILURE); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci warnx("(pid=%d): Test passed", pid); 16062306a36Sopenharmony_ci exit(EXIT_SUCCESS); 16162306a36Sopenharmony_ci} 162