diff options
author | Marek Jarycki <marek.jarycki@nsn.com> | 2017-01-25 16:40:49 +0100 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2017-02-27 21:15:11 +0000 |
commit | b6370de0fc4be6bb801206e39437ad1f2f5c0be7 (patch) | |
tree | c2b535b408a890a885fef52c19d747c2fb61d28f | |
parent | 3b51f382626c645f1d929213393251f85dfe4d1f (diff) | |
download | bubblewrap-b6370de0fc4be6bb801206e39437ad1f2f5c0be7.tar.gz |
Add --die-with-parent
In scenarios such as running bwrap in test frameworks (`bwrap make check`),
one wants all of the processes to go away if the parent process
dies, or if the bwrap process is directly killed.
This ensures that in all cases (both with `--unshare-pid` and without), we use
`prctl(PR_SET_PDEATHSIG)` on both our outer and inner init procesesses if
`--die-with-parent` is specified.
Tests-by: Colin Walters <walters@verbum.org>
Closes: #165
Approved by: emdej
-rw-r--r-- | bubblewrap.c | 27 | ||||
-rw-r--r-- | bwrap.xml | 9 | ||||
-rw-r--r-- | completions/bash/bwrap | 1 | ||||
-rwxr-xr-x | tests/test-run.sh | 45 |
4 files changed, 82 insertions, 0 deletions
diff --git a/bubblewrap.c b/bubblewrap.c index 0335968..81ebd96 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -65,6 +65,7 @@ bool opt_unshare_cgroup = FALSE; bool opt_unshare_cgroup_try = FALSE; bool opt_needs_devpts = FALSE; bool opt_new_session = FALSE; +bool opt_die_with_parent = FALSE; uid_t opt_sandbox_uid = -1; gid_t opt_sandbox_gid = -1; int opt_sync_fd = -1; @@ -217,10 +218,21 @@ usage (int ecode, FILE *out) " --block-fd FD Block on FD until some data to read is available\n" " --info-fd FD Write information about the running container to FD\n" " --new-session Create a new terminal session\n" + " --die-with-parent Kills with SIGKILL child process (COMMAND) when bwrap or bwrap's parent dies.\n" ); exit (ecode); } +/* If --die-with-parent was specified, use PDEATHSIG to ensure SIGKILL + * is sent to the current process when our parent dies. + */ +static void +handle_die_with_parent (void) +{ + if (opt_die_with_parent && prctl (PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != 0) + die_with_error ("prctl"); +} + static void block_sigchild (void) { @@ -399,6 +411,9 @@ do_init (int event_fd, pid_t initial_pid, struct sock_fprog *seccomp_prog) /* Keep fd open to hang on to lock */ } + /* Optionally bind our lifecycle to that of the caller */ + handle_die_with_parent (); + if (seccomp_prog != NULL && prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, seccomp_prog) != 0) die_with_error ("prctl(PR_SET_SECCOMP)"); @@ -1627,6 +1642,10 @@ parse_args_recurse (int *argcp, { opt_new_session = TRUE; } + else if (strcmp (arg, "--die-with-parent") == 0) + { + opt_die_with_parent = TRUE; + } else if (*arg == '-') { die ("Unknown option %s", arg); @@ -1877,6 +1896,9 @@ main (int argc, /* We don't need any privileges in the launcher, drop them immediately. */ drop_privs (); + /* Optionally bind our lifecycle to that of the parent */ + handle_die_with_parent (); + /* Let child run now that the uid maps are set up */ val = 1; res = write (child_wait_fd, &val, 8); @@ -2171,6 +2193,11 @@ main (int argc, /* We want sigchild in the child */ unblock_sigchild (); + /* Optionally bind our lifecycle */ + handle_die_with_parent (); + + /* Should be the last thing before execve() so that filters don't + * need to handle anything above */ if (seccomp_data != NULL && prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_prog) != 0) die_with_error ("prctl(PR_SET_SECCOMP)"); @@ -280,6 +280,15 @@ the application can feed keyboard input to the terminal. </para></listitem> </varlistentry> + <varlistentry> + <term><option>--die-with-parent</option></term> + <listitem><para> + Ensures child process (COMMAND) dies when bwrap's parent dies. Kills (SIGKILL) + all bwrap sandbox processes in sequence from parent to child + including COMMAND process when bwrap or bwrap's parent dies. + See prctl, PR_SET_PDEATHSIG. + </para></listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/completions/bash/bwrap b/completions/bash/bwrap index 34780ba..57d9677 100644 --- a/completions/bash/bwrap +++ b/completions/bash/bwrap @@ -48,6 +48,7 @@ _bwrap() { --unsetenv --seccomp --symlink + --die-with-parent " if [[ "$cur" == -* ]]; then diff --git a/tests/test-run.sh b/tests/test-run.sh index 9f90c64..59de516 100755 --- a/tests/test-run.sh +++ b/tests/test-run.sh @@ -84,4 +84,49 @@ for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshar # bind dest in symlink (https://github.com/projectatomic/bubblewrap/pull/119) $RUN $ALT --dir /tmp/dir --symlink dir /tmp/link --bind /etc /tmp/link true done + +# Test --die-with-parent + +cat >lockf-n.py <<EOF +#!/usr/bin/env python +import struct,fcntl,sys +path = sys.argv[1] +if sys.argv[2] == 'wait': + locktype = fcntl.F_SETLKW +else: + locktype = fcntl.F_SETLK +lockdata = struct.pack("hhllhh", fcntl.F_WRLCK, 0, 0, 0, 0, 0) +fd=open(sys.argv[1], 'a') +try: + fcntl.fcntl(fd.fileno(), locktype, lockdata) +except IOError as e: + sys.exit(1) +sys.exit(0) +EOF +chmod a+x lockf-n.py +touch lock + +for die_with_parent_argv in "--die-with-parent" "--die-with-parent --unshare-pid"; do + /bin/bash -c "$RUN ${die_with_parent_argv} --lock-file $(pwd)/lock sleep 1h && true" & + childshellpid=$! + + # Wait for lock to be taken (yes hacky) + for x in $(seq 10); do + if ./lockf-n.py ./lock nowait; then + sleep 1 + else + break + fi + done + if ./lockf-n.py ./lock nowait; then + assert_not_reached "timed out waiting for lock" + fi + + # Kill the shell, which should kill bwrap (and the sleep) + kill -9 ${childshellpid} + # Lock file should be unlocked + ./lockf-n.py ./lock wait + echo "ok die with parent ${die_with_parent_argv}" +done + echo OK |