From 1a7fcd8a66039fdff367954a76989fb567e2c5cc Mon Sep 17 00:00:00 2001 From: Matteo Martelli Date: Thu, 16 Apr 2026 18:30:55 +0200 Subject: [PATCH] Add option to create init inside the container In EXECVE execution mode, nsjail already creates an init process that reaps zombie processes inside the container. Add a new --init command line option to allow the creation of the init process also in the ONCE execution mode. --- cmdline.cc | 20 ++++++++++++++++++++ config.proto | 3 +++ pid.cc | 31 ++++++++++++++++++------------- pid.h | 1 + subproc.cc | 13 +++++++++++++ 5 files changed, 55 insertions(+), 13 deletions(-) diff --git a/cmdline.cc b/cmdline.cc index f6171977..ef73c886 100644 --- a/cmdline.cc +++ b/cmdline.cc @@ -177,6 +177,8 @@ static const struct custom_option custom_opts[] = { { { "use_core_scheduling", no_argument, nullptr, 0x709 }, "Enable Linux core scheduling (PR_SCHED_CORE, requires kernel >= 5.14)" }, { { "user_net", no_argument, nullptr, 0x70A }, "Enable user-mode networking (nstun backend)" }, { { "oom_score_adj", required_argument, nullptr, 0x800 }, "OOM score adjustment for the sandbox (-1000 to 1000) (default: not set)" }, + { { "init", no_argument, NULL, 0x907 }, "Run an init process inside the container. Incompatible with disable_clone_newpid and with RERUN and LISTEN modes. Implicitly enabled in EXECVE mode unless disable_clone_newpid is also provided." }, + }; // clang-format on @@ -992,6 +994,9 @@ std::unique_ptr parseArgs(int argc, char* argv[]) { case 0x800: nsj->njc.set_oom_score_adj((int32_t)strtol(optarg, NULL, 0)); break; + case 0x907: + nsj->njc.set_init(true); + break; default: cmdlineUsage(argv[0]); return nullptr; @@ -1015,6 +1020,21 @@ std::unique_ptr parseArgs(int argc, char* argv[]) { LOG_F("cannot set both cgroup_mem_memsw_max and cgroup_mem_swap_max"); } + if (nsj->njc.init()) { + if (nsj->njc.mode() == nsjail::Mode::LISTEN || + nsj->njc.mode() == nsjail::Mode::RERUN) { + LOG_F("cannot enable init process with mode LISTEN or RERUN"); + } + if (!nsj->njc.clone_newpid()) { + LOG_F("cannot enable init process with disable_clone_newpid"); + } + } + + if (nsj->njc.mode() == nsjail::Mode::EXECVE && nsj->njc.clone_newpid()) { + nsj->njc.set_init(true); + LOG_D("enabling init process for execve mode"); + } + return nsj; } diff --git a/config.proto b/config.proto index 9011ae0c..cfbda2ec 100644 --- a/config.proto +++ b/config.proto @@ -435,6 +435,9 @@ message NsJailConfig { /* Binary path (with arguments) to be executed. If not specified here, it can be specified with cmd-line as "-- /path/to/command arg1 arg2" */ optional Exe exec_bin = 98; + + /* Run an init process inside the container. */ + optional bool init = 102 [default = false]; } // vim: set noexpandtab ts=4 sw=4: diff --git a/pid.cc b/pid.cc index 9381499b..e66e4184 100644 --- a/pid.cc +++ b/pid.cc @@ -33,21 +33,9 @@ namespace pid { -bool initNs(nsj_t* nsj) { - if (nsj->njc.mode() != nsjail::Mode::EXECVE) { - return true; - } - if (!nsj->njc.clone_newpid()) { - return true; - } - +bool newInitProc() { LOG_D("Creating a dummy 'init' process"); - /* - * If -Me is used then we need to create permanent init inside PID ns, otherwise only the - * first clone/fork will work, and the rest will fail with ENOMEM (see 'man pid_namespaces' - * for details on this behavior) - */ pid_t pid = subproc::cloneProc(CLONE_FS, 0); if (pid == -1) { PLOG_E("Couldn't create a dummy init process"); @@ -84,4 +72,21 @@ bool initNs(nsj_t* nsj) { } } +bool initNs(nsj_t* nsj) { + if (nsj->njc.mode() != nsjail::Mode::EXECVE) { + return true; + } + if (!nsj->njc.init()) { + return true; + } + + /* + * If -Me is used then we need to create permanent init inside PID ns, otherwise only the + * first clone/fork will work, and the rest will fail with ENOMEM (see 'man pid_namespaces' + * for details on this behavior) + */ + + return newInitProc(); +} + } // namespace pid diff --git a/pid.h b/pid.h index 3c31d6d7..d337a9f0 100644 --- a/pid.h +++ b/pid.h @@ -28,6 +28,7 @@ namespace pid { +bool newInitProc(); bool initNs(nsj_t* nsj); } // namespace pid diff --git a/subproc.cc b/subproc.cc index d96827de..7a26700b 100644 --- a/subproc.cc +++ b/subproc.cc @@ -51,6 +51,7 @@ #include "macros.h" #include "net.h" #include "nstun/nstun.h" +#include "pid.h" #include "sandbox.h" #include "unotify/unotify.h" #include "user.h" @@ -535,6 +536,18 @@ pid_t runChild(nsj_t* nsj, int netfd, int fd_in, int fd_out, int fd_err) { LOG_D("Creating new process with clone flags:%s and exit_signal:SIGCHLD", cloneFlagsToStr(flags).c_str()); + if (nsj->njc.init() && nsj->njc.mode() == nsjail::Mode::ONCE) { + LOG_D("unshare(flags: %s)", cloneFlagsToStr(flags).c_str()); + if (unshare(flags) == -1) { + PLOG_F("unshare(%s)", cloneFlagsToStr(flags).c_str()); + } + if (!pid::newInitProc()) { + return -1; + } + // Clear clone flags as they have already been applied by unshare() + flags = 0; + } + int sv[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == -1) { PLOG_E("socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC) failed");