From 127241d7c30ef98b01535bfdd7a8643d4b0352ab Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 28 Sep 2026 20:31:37 +0100 Subject: [PATCH 1/3] Restructure implementation of --new-session No functional change, but this will make it easier to add a --new-pgrp or better debug logging. Signed-off-by: Simon McVittie --- bubblewrap.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bubblewrap.c b/bubblewrap.c index 2ab88d2f..9e742dee 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -3468,9 +3468,11 @@ main (int argc, xsetenv ("PWD", new_cwd, 1); free (old_cwd); - if (opt_new_session && - setsid () == (pid_t) -1) - die_with_error ("setsid"); + if (opt_new_session) + { + if (setsid () == (pid_t) -1) + die_with_error ("setsid"); + } if (label_exec (opt_exec_label) == -1) die_with_error ("label_exec %s", argv[0]); From a2b4bec529d08afee7f8a3835f0ded02120ca8d0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 28 Sep 2026 20:32:02 +0100 Subject: [PATCH 2/3] Make --new-session debug-log when it starts a new session Signed-off-by: Simon McVittie --- bubblewrap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bubblewrap.c b/bubblewrap.c index 9e742dee..ae005f63 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -3472,6 +3472,10 @@ main (int argc, { if (setsid () == (pid_t) -1) die_with_error ("setsid"); + + /* The session ID, process group ID and process ID are all equal + * for a session leader */ + debug ("became session leader, pgid %d in sandbox", getpgid (0)); } if (label_exec (opt_exec_label) == -1) From 44296ca320fa9a28647314030db85bc671fd100e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 28 Sep 2026 20:53:28 +0100 Subject: [PATCH 3/3] Add a --new-pgrp option This is like --new-session, but less so: instead of creating a new terminal session (which implies creating a new process group), it only creates a new process group. This is useful because `kill (0, s)` sends signal *s* to every member of the current process's process group, even if those processes are outside the current process's process ID namespace. A malicious sandboxed process could use this to terminate processes outside the sandbox (for example see CVE-2026-97029 in Flatpak), unless prevented from doing so by some process outside the sandbox creating a new process group. This is also potentially a useful building block for changing the handling of terminal-generated signals like `SIGINT` and `SIGHUP`, which are sent to the terminal's foreground process group. Signed-off-by: Simon McVittie --- bubblewrap.c | 13 +++++++++++++ bwrap.xml | 31 +++++++++++++++++++++++++++++++ completions/bash/bwrap | 1 + completions/zsh/_bwrap | 1 + 4 files changed, 46 insertions(+) diff --git a/bubblewrap.c b/bubblewrap.c index ae005f63..8b61dba3 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -75,6 +75,7 @@ static bool opt_unshare_uts = false; static bool opt_unshare_cgroup = false; static bool opt_unshare_cgroup_try = false; static bool opt_needs_devpts = false; +static bool opt_new_pgrp = false; static bool opt_new_session = false; static bool opt_die_with_parent = false; static uid_t opt_sandbox_uid = -1; @@ -350,6 +351,7 @@ usage (int ecode, FILE *out) " --userns-block-fd FD Block on FD until the user namespace is ready\n" " --info-fd FD Write information about the running container to FD\n" " --json-status-fd FD Write container status to FD as multiple JSON documents\n" + " --new-pgrp Create a new process group\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" " --as-pid-1 Do not install a reaper process with PID=1\n" @@ -2567,6 +2569,10 @@ parse_args_recurse (int *argcp, argv += 1; argc -= 1; } + else if (strcmp (arg, "--new-pgrp") == 0) + { + opt_new_pgrp = true; + } else if (strcmp (arg, "--new-session") == 0) { opt_new_session = true; @@ -3477,6 +3483,13 @@ main (int argc, * for a session leader */ debug ("became session leader, pgid %d in sandbox", getpgid (0)); } + else if (opt_new_pgrp) + { + if (setpgid (0, 0) == -1) + die_with_error ("setpgid(0, 0)"); + + debug ("became process group leader, pgid %d in sandbox", getpgid (0)); + } if (label_exec (opt_exec_label) == -1) die_with_error ("label_exec %s", argv[0]); diff --git a/bwrap.xml b/bwrap.xml index b48cf5b4..32b30baf 100644 --- a/bwrap.xml +++ b/bwrap.xml @@ -583,6 +583,37 @@ (see CVE-2017-5226). + + + + + + + Create a new process group for the sandbox + (calls + + setpgid + 2 + ). + Ignored if combined with , + because creating a new terminal session always creates a new + process group as a side-effect. + + + Note that if neither + nor is used, + the sandboxed process is likely to be able to kill processes + outside its process ID namespace by using + kill (0, SIGTERM) + to signal its entire process group. + In this case, + the parent process invoking bwrap needs to + ensure that this will not cause denial of service, + for example by placing bwrap in a new + process group. + + + diff --git a/completions/bash/bwrap b/completions/bash/bwrap index e7a523c2..b6cced36 100644 --- a/completions/bash/bwrap +++ b/completions/bash/bwrap @@ -14,6 +14,7 @@ _bwrap() { --clearenv --disable-userns --help + --new-pgrp --new-session --unshare-all --unshare-cgroup diff --git a/completions/zsh/_bwrap b/completions/zsh/_bwrap index fbddda43..e1cf9f13 100644 --- a/completions/zsh/_bwrap +++ b/completions/zsh/_bwrap @@ -53,6 +53,7 @@ _bwrap_args=( '--json-status-fd[Write container status to FD as multiple JSON documents]: :_guard "[0-9]#" "file descriptor to write to"' '--lock-file[Take a lock on DEST while sandbox is running]:lock file:_files' '--mqueue[Mount new mqueue on DEST]:mount point for mqueue:_files -/' + '--new-pgrp[Create a new process group]' '--new-session[Create a new terminal session]' '--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms' '--pidns[Use this user namespace (as parent namespace if using --unshare-pid)]: :'