summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Jarycki <marek.jarycki@nsn.com>2017-01-25 16:40:49 +0100
committerAtomic Bot <atomic-devel@projectatomic.io>2017-02-27 21:15:11 +0000
commitb6370de0fc4be6bb801206e39437ad1f2f5c0be7 (patch)
treec2b535b408a890a885fef52c19d747c2fb61d28f
parent3b51f382626c645f1d929213393251f85dfe4d1f (diff)
downloadbubblewrap-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.c27
-rw-r--r--bwrap.xml9
-rw-r--r--completions/bash/bwrap1
-rwxr-xr-xtests/test-run.sh45
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)");
diff --git a/bwrap.xml b/bwrap.xml
index b7a5c41..24d67b9 100644
--- a/bwrap.xml
+++ b/bwrap.xml
@@ -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