Skip to content

New option to create init process #263

Description

@mattmart3

Hi all,

I'm involved in a project where nsjail is used to isolate processes in a Linux system and it runs with the default ONCE mode. Part of the system testing involves running stress-ng in sequence mode inside the nsjail container. In this case stress-ng runs some tests that spawn zombies whose are never reaped since there is no init process running inside the container, leading to file description exhaustion. While digging in the nsjail code I've noticed that nsjail in EXECVE mode, already creates an init process in the container. However EXECVE mode might not always be desirable as for instance the execve'd target process would not run in an isolated pid namespace.

Therefore I thought to add an --init option that would spawn an init reaper process inside the container for the other modes as well. A similar feature is provided for instance by docker run --init which relies on tini (see also this write up on tini for other uses cases where this feature could be useful).

I came up with this draft patch mattmart3@2b53c1a that only adds the feature for the ONCE mode. It first calls unshare() on the nsjail parent process, then spawns the init process and only then it spawns the child for the actual target process. In this way both the init process and the target process run in the isolated environment. I tried to enable it for the LISTEN and RERUN modes as well, but after the first instance it doesn't work anymore, I'm not sure why but it looks like it has something to do with how the namespace setup affects the parent nsjail process that called unshare().

Following an example using a simple program that creates zombies:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid = fork();
    if (pid == 0) {
        for (int i = 0; i < 10; i++) {
            pid_t pid = fork();
            if (pid == 0) {
                printf("[grandchild] exit\n");
                exit(0);
            }
        }
        printf("[child] exit\n");
        exit(0);
    }
    printf("[parent] reap child and sleep for 10 secs\n");
    int status;
    wait(&status);
    sleep(10);
    printf("[parent] exit\n");
    exit(0);
}

Launching njail without --init

> ./nsjail  --chroot / -- /usr/bin/zombies
[I][2026-04-16T18:12:01+0200] Mode: STANDALONE_ONCE
[I][2026-04-16T18:12:01+0200] Jail parameters: hostname:'NSJAIL', chroot:'/', process:'/usr/bin/zombies', bind:[::]:0, max_conns:0, max_conns_per_ip:0, time_limit:600, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clone_newuts:true, cgroupv2:false, keep_caps:false, disable_no_new_privs:false, max_cpus:0
[I][2026-04-16T18:12:01+0200] Mount: '/' -> '/' type:'' options:'' dir:true
[I][2026-04-16T18:12:01+0200] Mount: '/proc' type:'proc' options:'' dir:true
[I][2026-04-16T18:12:01+0200] Uid map: inside_uid:1000 outside_uid:1000 count:1 newuidmap:false
[I][2026-04-16T18:12:01+0200] Gid map: inside_gid:1000 outside_gid:1000 count:1 newgidmap:false
[I][2026-04-16T18:12:01+0200] Executing '/usr/bin/zombies' for '[STANDALONE MODE]'
[parent] reap child and sleep for 10 secs
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[child] exit
[grandchild] exit
[grandchild] exit

Zombies are not reaped:

> ps ax -o ppid,pid,command | grep 'nsjail\|zombies'
 196270  242439 ./nsjail --chroot / -- /usr/bin/zombies
 242439  242440 /usr/bin/zombies
 242440  242442 [zombies] <defunct>
 242440  242443 [zombies] <defunct>
 242440  242444 [zombies] <defunct>
 242440  242445 [zombies] <defunct>
 242440  242446 [zombies] <defunct>
 242440  242447 [zombies] <defunct>
 242440  242448 [zombies] <defunct>
 242440  242449 [zombies] <defunct>
 242440  242450 [zombies] <defunct>
 242440  242451 [zombies] <defunct>

Launching nsjail with --init

> ./nsjail --chroot / --init -- /usr/bin/zombies                                                                1
[I][2026-04-16T18:13:32+0200] Mode: STANDALONE_ONCE
[I][2026-04-16T18:13:32+0200] Jail parameters: hostname:'NSJAIL', chroot:'/', process:'/usr/bin/zombies', bind:[::]:0, max_conns:0, max_conns_per_ip:0, time_limit:600, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clone_newuts:true, cgroupv2:false, keep_caps:false, disable_no_new_privs:false, max_cpus:0
[I][2026-04-16T18:13:32+0200] Mount: '/' -> '/' type:'' options:'' dir:true
[I][2026-04-16T18:13:32+0200] Mount: '/proc' type:'proc' options:'' dir:true
[I][2026-04-16T18:13:32+0200] Uid map: inside_uid:1000 outside_uid:1000 count:1 newuidmap:false
[I][2026-04-16T18:13:32+0200] Gid map: inside_gid:1000 outside_gid:1000 count:1 newgidmap:false
[I][2026-04-16T18:13:32+0200] Executing '/usr/bin/zombies' for '[STANDALONE MODE]'
[parent] reap child and sleep for 10 secs
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[grandchild] exit
[child] exit

Zombies are reaped:

> ps ax -o ppid,pid,command | grep 'nsjail\|zombies'
 196270  243217 ./nsjail --chroot / --init -- /usr/bin/zombies
 243217  243218 ./nsjail --chroot / --init -- /usr/bin/zombies
 243217  243219 /usr/bin/zombies

Would a feature like this be considered? I would really appreciate some feedback and your thoughts on this. Also please consider that this was the first time I looked on how container creation works at this level, so I might definitely have missed some important detail.

As a side note, in my patch I kept the logic to avoid creating the init process if the --disable_clone_newpid option is provided. This was introduced in #57, even though I am not sure that is actually necessary (see my last recent comment on that issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions