From 123ec8b03538c4bb32f5e26daec2c23030870c52 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 6 May 2026 11:10:30 -0700 Subject: [PATCH] vendor: update bubblewrap to 0.11.2 (#21389) ## Why `codex-rs/vendor/bubblewrap` had fallen behind upstream, and upstream `v0.11.2` is the current Bubblewrap release. The release is a security update for `CVE-2026-41163`, affecting setuid Bubblewrap builds, and deprecates setuid support in favor of the default non-setuid build mode. ## What changed - Refreshed the vendored Bubblewrap sources under `codex-rs/vendor/bubblewrap` to upstream `v0.11.2`. - Brought in the upstream `-Dsupport_setuid` build option, which defaults setuid support off. - Updated vendored release notes and documentation files included with Bubblewrap. ## Verification Not run locally; this PR only refreshes the vendored upstream Bubblewrap source snapshot. Upstream release: https://github.com/containers/bubblewrap/releases/tag/v0.11.2 --- codex-rs/vendor/bubblewrap/NEWS.md | 47 +++++++++++++++++ codex-rs/vendor/bubblewrap/README.md | 31 ++++++------ codex-rs/vendor/bubblewrap/SECURITY.md | 7 +++ codex-rs/vendor/bubblewrap/bubblewrap.c | 50 +++++++++++++------ codex-rs/vendor/bubblewrap/meson.build | 7 ++- codex-rs/vendor/bubblewrap/meson_options.txt | 6 +++ codex-rs/vendor/bubblewrap/network.c | 4 +- .../vendor/bubblewrap/release-checklist.md | 6 +-- codex-rs/vendor/bubblewrap/utils.c | 13 +++-- 9 files changed, 131 insertions(+), 40 deletions(-) diff --git a/codex-rs/vendor/bubblewrap/NEWS.md b/codex-rs/vendor/bubblewrap/NEWS.md index da232c4bd7..eb82ed98d9 100644 --- a/codex-rs/vendor/bubblewrap/NEWS.md +++ b/codex-rs/vendor/bubblewrap/NEWS.md @@ -1,3 +1,50 @@ +bubblewrap 0.11.2 +================= + +Released: 2026-04-23 + +Bug fixes: + + * In setuid mode, don't run the low-privileged parts parts of the setup + as dumpable, as that allows it to be ptraced which can lead to problems. + This is CVE-2026-41163, and was reported by François Diakhate. + +Enhancements: + + * New build option `-Dsupport_setuid`, which if set to false (which + is the default) disables the support for setuid. Binaries built + with this will refuse to run if made setuid. We recommend building + normal bubblewrap binaries like this, which allows you to safely + ignore any security issues that only affect setuid mode. + +bubblewrap 0.11.1 +================= + +Released: 2026-03-21 + +Bug fixes: + + * Reset disposition of `SIGCHLD`, restoring normal subprocess management + if bwrap was run from a process that was ignoring that signal, + such as Erlang or volumeicon (#705, Joel Pelaez Jorge) + + * Don't ignore `--userns 0`, `--userns2 0` or `--pidns 0` if used + (#731, Daniel Cazares). + Note that using a fd number ≥ 3 for these purposes is still + preferred, to avoid confusion with the stdin, stdout, stderr + that will be inherited by the command inside the container. + + * Fix grammar in an error message (#694, J. Neuschäfer) + + * Fix a broken link in the documentation (#729, Aaron Brooks) + +Internal changes: + + * Enable user namespaces in Github Actions configuration, fixing a CI + regression with newer Ubuntu (#728, Joel Pelaez Jorge) + + * Clarify comments (#737, Simon McVittie) + bubblewrap 0.11.0 ================= diff --git a/codex-rs/vendor/bubblewrap/README.md b/codex-rs/vendor/bubblewrap/README.md index c16cd7d89a..1f838ce0a1 100644 --- a/codex-rs/vendor/bubblewrap/README.md +++ b/codex-rs/vendor/bubblewrap/README.md @@ -12,23 +12,24 @@ on the host. User namespaces --------------- -There is an effort in the Linux kernel called +There is an feature in the Linux kernel called [user namespaces](https://www.google.com/search?q=user+namespaces+site%3Ahttps%3A%2F%2Flwn.net) -which attempts to allow unprivileged users to use container features. -While significant progress has been made, there are -[still concerns](https://lwn.net/Articles/673597/) about it, and -it is not available to unprivileged users in several production distributions -such as CentOS/Red Hat Enterprise Linux 7, Debian Jessie, etc. +which allows unprivileged users to use container features. Bubblewrap uses these to +build the sandbox, allowing any user to use the tool. -See for example -[CVE-2016-3135](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3135) -which is a local root vulnerability introduced by userns. -[This March 2016 post](https://lkml.org/lkml/2016/3/9/555) has some -more discussion. +Historically, not all Linux distributions supported (at least by +default) unprivileged user namespaces, so bubblewrap supports a second +mode of operation when the binary is setuid root. In that setup +bubblewrap could be viewed as setuid implementation of a *subset* of +user namespaces. However, not all features of bubblewrap work in +this mode. -Bubblewrap could be viewed as setuid implementation of a *subset* of -user namespaces. Emphasis on subset - specifically relevant to the -above CVE, bubblewrap does not allow control over iptables. +However, setuid mode is deprecated, as most recent Linux distributions +support unprivileged user namespaces, and setuid binaries carry +significant risks. By default, bubblewrap binaries refuse to work if +setuid, and you must build explicitly with ` -Dsupport_setuid=true` to +enable it to work. Later versions of bubblewrap aims to completely +remove this support. The original bubblewrap code existed before user namespaces - it inherits code from [xdg-app helper](https://cgit.freedesktop.org/xdg-app/xdg-app/tree/common/xdg-app-helper.c?id=4c3bf179e2e4a2a298cd1db1d045adaf3f564532) @@ -151,7 +152,7 @@ sandbox. You can also change what the value of uid/gid should be in the sandbox. IPC namespaces ([CLONE_NEWIPC](https://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the different forms of IPCs, like SysV shared memory and semaphores. -PID namespaces ([CLONE_NEWPID](https://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/). +PID namespaces ([CLONE_NEWPID](https://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/docker-and-the-pid-1-zombie-reaping-problem/). Network namespaces ([CLONE_NEWNET](https://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device. diff --git a/codex-rs/vendor/bubblewrap/SECURITY.md b/codex-rs/vendor/bubblewrap/SECURITY.md index 0ddfc6c873..7e0fb32746 100644 --- a/codex-rs/vendor/bubblewrap/SECURITY.md +++ b/codex-rs/vendor/bubblewrap/SECURITY.md @@ -15,6 +15,13 @@ between the user and the OS, because anything bubblewrap could do, a malicious user could equally well do by writing their own tool equivalent to bubblewrap. +Since 0.11.2, unless compiled with the `-Dsupport_setuid=true` option, +setuid root support is disabled. In this mode bubblewrap will refuse +to operate if the binary has been made setuid. For binaries built like +this it is safe to ignore any bubblewrap CVEs that are described as +affecting setuid mode only. This is the recommended way to package +bubblewrap. + ### Sandbox security bubblewrap is a toolkit for constructing sandbox environments. diff --git a/codex-rs/vendor/bubblewrap/bubblewrap.c b/codex-rs/vendor/bubblewrap/bubblewrap.c index 69d319b7a3..9039ddfa80 100644 --- a/codex-rs/vendor/bubblewrap/bubblewrap.c +++ b/codex-rs/vendor/bubblewrap/bubblewrap.c @@ -55,7 +55,11 @@ static uid_t real_uid; static gid_t real_gid; static uid_t overflow_uid; static gid_t overflow_gid; +#ifdef ENABLE_SUPPORT_SETUID static bool is_privileged; /* See acquire_privs() */ +#else +#define is_privileged 0 +#endif static const char *argv0; static const char *host_tty_dev; static int proc_fd = -1; @@ -840,13 +844,16 @@ set_ambient_capabilities (void) static void acquire_privs (void) { - uid_t euid, new_fsuid; + uid_t euid; euid = geteuid (); /* Are we setuid ? */ if (real_uid != euid) { +#ifdef ENABLE_SUPPORT_SETUID + uid_t new_fsuid; + if (euid != 0) die ("Unexpected setuid user %d, should be 0", euid); @@ -868,13 +875,16 @@ acquire_privs (void) /* setfsuid can't properly report errors, check that it worked (as per manpage) */ new_fsuid = setfsuid (-1); if (new_fsuid != real_uid) - die ("Unable to set fsuid (was %d)", (int)new_fsuid); + die_with_error ("Unable to set fsuid (was %d)", (int)new_fsuid); /* We never need capabilities after execve(), so lets drop everything from the bounding set */ drop_cap_bounding_set (true); /* Keep only the required capabilities for setup */ set_required_caps (); +#else + die ("setuid use of bubblewrap is not supported in this build"); +#endif } else if (real_uid != 0 && has_caps ()) { @@ -937,7 +947,8 @@ switch_to_user_with_privs (void) /* Call setuid() and use capset() to adjust capabilities */ static void drop_privs (bool keep_requested_caps, - bool already_changed_uid) + bool already_changed_uid, + bool set_dumpable) { assert (!keep_requested_caps || !is_privileged); /* Drop root uid */ @@ -947,9 +958,12 @@ drop_privs (bool keep_requested_caps, drop_all_caps (keep_requested_caps); - /* We don't have any privs now, so mark us dumpable which makes /proc/self be owned by the user instead of root */ - if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) != 0) - die_with_error ("can't set dumpable"); + if (set_dumpable) + { + /* We don't have any privs now, so mark us dumpable which makes /proc/self be owned by the user instead of root */ + if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) != 0) + die_with_error ("can't set dumpable"); + } } static void @@ -1154,7 +1168,9 @@ privileged_op (int privileged_op_socket, break; case PRIV_SEP_OP_OVERLAY_MOUNT: - if (mount ("overlay", arg2, "overlay", MS_MGC_VAL, arg1) != 0) + if (is_privileged) + die ("Overlay mounts are not supported in setuid mode"); + if (mount ("overlay", arg2, "overlay", MS_MGC_VAL | MS_NOSUID | MS_NODEV, arg1) != 0) { /* The standard message for ELOOP, "Too many levels of symbolic * links", is not helpful here. */ @@ -1172,6 +1188,8 @@ privileged_op (int privileged_op_socket, something manages to send hacked priv-sep operation requests. */ if (!opt_unshare_uts) die ("Refusing to set hostname in original namespace"); + if (arg1 == NULL) + die ("Hostname argument is NULL"); if (sethostname (arg1, strlen(arg1)) != 0) die_with_error ("Can't set hostname to %s", arg1); break; @@ -3112,7 +3130,7 @@ main (int argc, } /* Switch to the custom user ns before the clone, gets us privs in that ns (assuming its a child of the current and thus allowed) */ - if (opt_userns_fd > 0 && setns (opt_userns_fd, CLONE_NEWUSER) != 0) + if (opt_userns_fd != -1 && setns (opt_userns_fd, CLONE_NEWUSER) != 0) { if (errno == EINVAL) die ("Joining the specified user namespace failed, it might not be a descendant of the current user namespace."); @@ -3178,11 +3196,11 @@ main (int argc, /* Initial launched process, wait for pid 1 or exec:ed command to exit */ - if (opt_userns2_fd > 0 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) + if (opt_userns2_fd != -1 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) die_with_error ("Setting userns2 failed"); /* We don't need any privileges in the launcher, drop them immediately. */ - drop_privs (false, false); + drop_privs (false, false, true); /* Optionally bind our lifecycle to that of the parent */ handle_die_with_parent (); @@ -3219,7 +3237,7 @@ main (int argc, return monitor_child (event_fd, pid, setup_finished_pipe[0]); } - if (opt_pidns_fd > 0) + if (opt_pidns_fd != -1) { if (setns (opt_pidns_fd, CLONE_NEWPID) != 0) die_with_error ("Setting pidns failed"); @@ -3369,8 +3387,10 @@ main (int argc, if (child == 0) { - /* Unprivileged setup process */ - drop_privs (false, true); + /* Unprivileged setup process. + * Note: Don't set dumpable, because we can still perform privileged + * operations via privileged_op(). */ + drop_privs (false, true, false); close (privsep_sockets[0]); setup_newroot (opt_unshare_pid, privsep_sockets[1]); exit (0); @@ -3446,7 +3466,7 @@ main (int argc, die_with_error ("chdir /"); } - if (opt_userns2_fd > 0 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) + if (opt_userns2_fd != -1 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) die_with_error ("Setting userns2 failed"); if (opt_unshare_user && opt_userns_block_fd == -1 && @@ -3499,7 +3519,7 @@ main (int argc, } /* All privileged ops are done now, so drop caps we don't need */ - drop_privs (!is_privileged, true); + drop_privs (!is_privileged, true, true); if (opt_block_fd != -1) { diff --git a/codex-rs/vendor/bubblewrap/meson.build b/codex-rs/vendor/bubblewrap/meson.build index 78678d0973..520d0a5f47 100644 --- a/codex-rs/vendor/bubblewrap/meson.build +++ b/codex-rs/vendor/bubblewrap/meson.build @@ -1,7 +1,7 @@ project( 'bubblewrap', 'c', - version : '0.11.0', + version : '0.11.2', meson_version : '>=0.49.0', default_options : [ 'warning_level=2', @@ -91,6 +91,11 @@ if get_option('require_userns') cdata.set('ENABLE_REQUIRE_USERNS', 1) endif +if get_option('support_setuid') + cdata.set('ENABLE_SUPPORT_SETUID', 1) + warning('running bubblewrap setuid is deprecated and risky. Most recent operating systems support unprivileged user namespaces and we recommend using that. Support for this will be removed in the next version.') +endif + configure_file( output : 'config.h', configuration : cdata, diff --git a/codex-rs/vendor/bubblewrap/meson_options.txt b/codex-rs/vendor/bubblewrap/meson_options.txt index 5e25ee86f8..05b1c7873e 100644 --- a/codex-rs/vendor/bubblewrap/meson_options.txt +++ b/codex-rs/vendor/bubblewrap/meson_options.txt @@ -41,6 +41,12 @@ option( type : 'string', description : 'Path to Python 3, or empty to use python3', ) +option( + 'support_setuid', + type : 'boolean', + description : 'Support setuid mode (deprecated)', + value : false, +) option( 'require_userns', type : 'boolean', diff --git a/codex-rs/vendor/bubblewrap/network.c b/codex-rs/vendor/bubblewrap/network.c index 106e6d6e36..373d606a0a 100644 --- a/codex-rs/vendor/bubblewrap/network.c +++ b/codex-rs/vendor/bubblewrap/network.c @@ -50,7 +50,7 @@ static int rtnl_send_request (int rtnl_fd, struct nlmsghdr *header) { - struct sockaddr_nl dst_addr = { .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 0 }; + struct sockaddr_nl dst_addr = { AF_NETLINK, 0 }; ssize_t sent; sent = TEMP_FAILURE_RETRY (sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0, @@ -139,7 +139,7 @@ loopback_setup (void) int r, if_loopback; cleanup_fd int rtnl_fd = -1; char buffer[1024]; - struct sockaddr_nl src_addr = { .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 0 }; + struct sockaddr_nl src_addr = { AF_NETLINK, 0 }; struct nlmsghdr *header; struct ifaddrmsg *addmsg; struct ifinfomsg *infomsg; diff --git a/codex-rs/vendor/bubblewrap/release-checklist.md b/codex-rs/vendor/bubblewrap/release-checklist.md index 5b2119c8af..0c1479e447 100644 --- a/codex-rs/vendor/bubblewrap/release-checklist.md +++ b/codex-rs/vendor/bubblewrap/release-checklist.md @@ -1,13 +1,13 @@ bubblewrap release checklist ============================ -* Collect release notes in `NEWS` -* Update version number in `meson.build` and release date in `NEWS` +* Collect release notes in `NEWS.md` +* Update version number in `meson.build` and release date in `NEWS.md` * Commit the changes * `meson dist -C ${builddir}` * Do any final smoke-testing, e.g. update a package, install and test it * `git evtag sign v$VERSION` - * Include the release notes from `NEWS` in the tag message + * Include the release notes from `NEWS.md` in the tag message * `git push --atomic origin main v$VERSION` * https://github.com/containers/bubblewrap/releases/new * Fill in the new version's tag in the "Tag version" box diff --git a/codex-rs/vendor/bubblewrap/utils.c b/codex-rs/vendor/bubblewrap/utils.c index 51875aea9a..7b7349ab82 100644 --- a/codex-rs/vendor/bubblewrap/utils.c +++ b/codex-rs/vendor/bubblewrap/utils.c @@ -510,14 +510,18 @@ ensure_file (const char *path, the create file will fail in the read-only case with EROFS instead of EEXIST. - We're trying to set up a mount point for a non-directory, so any - non-directory, non-symlink is acceptable - it doesn't necessarily - have to be a regular file. */ + We're trying to set up a mount point for a non-directory, for which + the kernel will accept any non-directory. If it's a symlink, follow + it and look at the target: again, any non-directory is good enough. + We'll only get S_ISLNK if the path is a dangling symlink (target + doesn't exist). */ if (stat (path, &buf) == 0 && !S_ISDIR (buf.st_mode) && !S_ISLNK (buf.st_mode)) return 0; + /* If the file didn't exist, create it. If it was a dangling symlink + * (S_ISLNK above) then this will create the target of the symlink. */ if (create_file (path, mode, NULL) != 0 && errno != EEXIST) return -1; @@ -681,7 +685,8 @@ ensure_dir (const char *path, /* We check this ahead of time, otherwise the mkdir call can fail in the read-only case with EROFS instead of EEXIST on some - filesystems (such as NFS) */ + filesystems (such as NFS). + We follow symlinks: it's OK if path is a symlink to a directory. */ if (stat (path, &buf) == 0) { if (!S_ISDIR (buf.st_mode))