Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package bubblewrap for openSUSE:Factory checked in at 2022-12-09 13:16:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/bubblewrap (Old) and /work/SRC/openSUSE:Factory/.bubblewrap.new.1835 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "bubblewrap" Fri Dec 9 13:16:33 2022 rev:15 rq:1041340 version:0.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/bubblewrap/bubblewrap.changes 2022-05-20 17:50:06.703192922 +0200 +++ /work/SRC/openSUSE:Factory/.bubblewrap.new.1835/bubblewrap.changes 2022-12-09 13:16:37.398632695 +0100 @@ -1,0 +2,15 @@ +Wed Dec 7 21:50:27 UTC 2022 - Dirk Müller <dmuel...@suse.com> + +- update to v0.7.0: + * --size option controls the size of a subsequent --tmpfs (#509) + * Better error messages if a mount operation fails (#472) + * Better error message if creating the new user namespace fails with + ENOSPC (#487) + * When building as a Meson subproject, a RUNPATH can be set on the + executable to make it easier to bundle its libcap dependency + * Fix test failures when running as uid 0 but with limited capabilities + (#510) + * Use POSIX command -v in preference to non-standard which (#527) + * Fix a copy/paste error in --help (#531) + +------------------------------------------------------------------- Old: ---- bubblewrap-0.6.2.tar.xz New: ---- bubblewrap-0.7.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ bubblewrap.spec ++++++ --- /var/tmp/diff_new_pack.eJtFFP/_old 2022-12-09 13:16:37.910635415 +0100 +++ /var/tmp/diff_new_pack.eJtFFP/_new 2022-12-09 13:16:37.914635435 +0100 @@ -17,12 +17,12 @@ Name: bubblewrap -Version: 0.6.2 +Version: 0.7.0 Release: 0 Summary: Core execution tool for unprivileged containers License: LGPL-2.0-or-later Group: Productivity/Security -URL: https://github.com/projectatomic/bubblewrap +URL: https://github.com/containers/bubblewrap Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.xz BuildRequires: autoconf BuildRequires: automake ++++++ bubblewrap-0.6.2.tar.xz -> bubblewrap-0.7.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/bind-mount.c new/bubblewrap-0.7.0/bind-mount.c --- old/bubblewrap-0.6.2/bind-mount.c 2022-02-13 19:53:18.000000000 +0100 +++ new/bubblewrap-0.7.0/bind-mount.c 2022-10-27 18:35:57.000000000 +0200 @@ -378,7 +378,8 @@ bind_mount (int proc_fd, const char *src, const char *dest, - bind_option_t options) + bind_option_t options, + char **failing_path) { bool readonly = (options & BIND_READONLY) != 0; bool devices = (options & BIND_DEVICES) != 0; @@ -406,7 +407,12 @@ dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC); if (dest_fd < 0) - return BIND_MOUNT_ERROR_REOPEN_DEST; + { + if (failing_path != NULL) + *failing_path = steal_pointer (&resolved_dest); + + return BIND_MOUNT_ERROR_REOPEN_DEST; + } /* If we are in a case-insensitive filesystem, mountinfo might contain a * different case combination of the path we requested to mount. @@ -422,11 +428,19 @@ oldroot_dest_proc = get_oldroot_path (dest_proc); kernel_case_combination = readlink_malloc (oldroot_dest_proc); if (kernel_case_combination == NULL) - return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; + { + if (failing_path != NULL) + *failing_path = steal_pointer (&resolved_dest); + + return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD; + } mount_tab = parse_mountinfo (proc_fd, kernel_case_combination); if (mount_tab[0].mountpoint == NULL) { + if (failing_path != NULL) + *failing_path = steal_pointer (&kernel_case_combination); + errno = EINVAL; return BIND_MOUNT_ERROR_FIND_DEST_MOUNT; } @@ -437,7 +451,12 @@ if (new_flags != current_flags && mount ("none", resolved_dest, NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) - return BIND_MOUNT_ERROR_REMOUNT_DEST; + { + if (failing_path != NULL) + *failing_path = steal_pointer (&resolved_dest); + + return BIND_MOUNT_ERROR_REMOUNT_DEST; + } /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually * apply the flags to all submounts in the recursive case. @@ -456,7 +475,12 @@ /* If we can't read the mountpoint we can't remount it, but that should be safe to ignore because its not something the user can access. */ if (errno != EACCES) - return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT; + { + if (failing_path != NULL) + *failing_path = xstrdup (mount_tab[i].mountpoint); + + return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT; + } } } } @@ -469,50 +493,53 @@ * If want_errno_p is non-NULL, *want_errno_p is used to indicate whether * it would make sense to print strerror(saved_errno). */ -const char * +static char * bind_mount_result_to_string (bind_mount_result res, + const char *failing_path, bool *want_errno_p) { - const char *string; + char *string = NULL; bool want_errno = TRUE; switch (res) { case BIND_MOUNT_ERROR_MOUNT: - string = "Unable to mount source on destination"; + string = xstrdup ("Unable to mount source on destination"); break; case BIND_MOUNT_ERROR_REALPATH_DEST: - string = "realpath(destination)"; + string = xstrdup ("realpath(destination)"); break; case BIND_MOUNT_ERROR_REOPEN_DEST: - string = "open(destination, O_PATH)"; + string = xasprintf ("open(\"%s\", O_PATH)", failing_path); break; case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD: - string = "readlink(/proc/self/fd/<destination>)"; + string = xasprintf ("readlink(/proc/self/fd/N) for \"%s\"", failing_path); break; case BIND_MOUNT_ERROR_FIND_DEST_MOUNT: - string = "Unable to find destination in mount table"; + string = xasprintf ("Unable to find \"%s\" in mount table", failing_path); want_errno = FALSE; break; case BIND_MOUNT_ERROR_REMOUNT_DEST: - string = "Unable to remount destination with correct flags"; + string = xasprintf ("Unable to remount destination \"%s\" with correct flags", + failing_path); break; case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT: - string = "Unable to remount recursively with correct flags"; + string = xasprintf ("Unable to apply mount flags: remount \"%s\"", + failing_path); break; case BIND_MOUNT_SUCCESS: - string = "Success"; + string = xstrdup ("Success"); break; default: - string = "(unknown/invalid bind_mount_result)"; + string = xstrdup ("(unknown/invalid bind_mount_result)"); break; } @@ -525,11 +552,13 @@ void die_with_bind_result (bind_mount_result res, int saved_errno, + const char *failing_path, const char *format, ...) { va_list args; bool want_errno = TRUE; + char *message; fprintf (stderr, "bwrap: "); @@ -537,7 +566,9 @@ vfprintf (stderr, format, args); va_end (args); - fprintf (stderr, ": %s", bind_mount_result_to_string (res, &want_errno)); + message = bind_mount_result_to_string (res, failing_path, &want_errno); + fprintf (stderr, ": %s", message); + /* message is leaked, but we're exiting unsuccessfully anyway, so ignore */ if (want_errno) fprintf (stderr, ": %s", strerror (saved_errno)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/bind-mount.h new/bubblewrap-0.7.0/bind-mount.h --- old/bubblewrap-0.6.2/bind-mount.h 2022-02-13 19:53:18.000000000 +0100 +++ new/bubblewrap-0.7.0/bind-mount.h 2022-10-27 18:35:57.000000000 +0200 @@ -42,14 +42,13 @@ bind_mount_result bind_mount (int proc_fd, const char *src, const char *dest, - bind_option_t options); - -const char *bind_mount_result_to_string (bind_mount_result res, - bool *want_errno); + bind_option_t options, + char **failing_path); void die_with_bind_result (bind_mount_result res, int saved_errno, + const char *failing_path, const char *format, ...) __attribute__((__noreturn__)) - __attribute__((format (printf, 3, 4))); + __attribute__((format (printf, 4, 5))); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/bubblewrap.c new/bubblewrap-0.7.0/bubblewrap.c --- old/bubblewrap-0.6.2/bubblewrap.c 2022-05-11 15:54:47.000000000 +0200 +++ new/bubblewrap-0.7.0/bubblewrap.c 2022-10-27 19:09:27.000000000 +0200 @@ -23,6 +23,7 @@ #include <sched.h> #include <pwd.h> #include <grp.h> +#include <ctype.h> #include <sys/mount.h> #include <sys/socket.h> #include <sys/wait.h> @@ -52,6 +53,12 @@ __result; })) #endif +/* We limit the size of a tmpfs to half the architecture's address space, + * to avoid hitting arbitrary limits in the kernel. + * For example, on at least one x86_64 machine, the actual limit seems to be + * 2^64 - 2^12. */ +#define MAX_TMPFS_BYTES ((size_t) (SIZE_MAX >> 1)) + /* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */ static uid_t real_uid; static gid_t real_gid; @@ -91,6 +98,7 @@ int opt_userns2_fd = -1; int opt_pidns_fd = -1; int next_perms = -1; +size_t next_size_arg = 0; #define CAP_TO_MASK_0(x) (1L << ((x) & 31)) #define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32) @@ -149,6 +157,7 @@ int fd; SetupOpFlag flags; int perms; + size_t size; /* number of bytes, zero means unset/default */ SetupOp *next; }; @@ -177,6 +186,7 @@ uint32_t op; uint32_t flags; uint32_t perms; + size_t size_arg; uint32_t arg1_offset; uint32_t arg2_offset; } PrivSepOp; @@ -301,7 +311,7 @@ " --unshare-cgroup-try Create new cgroup namespace if possible else continue by skipping it\n" " --userns FD Use this user namespace (cannot combine with --unshare-user)\n" " --userns2 FD After setup switch to this user namespace, only useful with --userns\n" - " --pidns FD Use this user namespace (as parent namespace if using --unshare-pid)\n" + " --pidns FD Use this pid namespace (as parent namespace if using --unshare-pid)\n" " --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n" " --gid GID Custom gid in the sandbox (requires --unshare-user or --userns)\n" " --hostname NAME Custom hostname in the sandbox (requires --unshare-uts)\n" @@ -341,6 +351,7 @@ " --cap-add CAP Add cap CAP when running as privileged user\n" " --cap-drop CAP Drop cap CAP when running as privileged user\n" " --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n" + " --size BYTES Set size of next argument (only for --tmpfs)\n" " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n" ); exit (ecode); @@ -1001,10 +1012,12 @@ uint32_t op, uint32_t flags, uint32_t perms, + size_t size_arg, const char *arg1, const char *arg2) { bind_mount_result bind_result; + char *failing_path = NULL; if (privileged_op_socket != -1) { @@ -1032,6 +1045,7 @@ op_buffer->op = op; op_buffer->flags = flags; op_buffer->perms = perms; + op_buffer->size_arg = size_arg; op_buffer->arg1_offset = arg1_offset; op_buffer->arg2_offset = arg2_offset; if (arg1 != NULL) @@ -1070,23 +1084,25 @@ break; case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: - bind_result = bind_mount (proc_fd, NULL, arg2, BIND_READONLY); + bind_result = bind_mount (proc_fd, NULL, arg2, BIND_READONLY, &failing_path); if (bind_result != BIND_MOUNT_SUCCESS) - die_with_bind_result (bind_result, errno, + die_with_bind_result (bind_result, errno, failing_path, "Can't remount readonly on %s", arg2); + assert (failing_path == NULL); /* otherwise we would have died */ break; case PRIV_SEP_OP_BIND_MOUNT: /* We always bind directories recursively, otherwise this would let us access files that are otherwise covered on the host */ - bind_result = bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags); + bind_result = bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags, &failing_path); if (bind_result != BIND_MOUNT_SUCCESS) - die_with_bind_result (bind_result, errno, + die_with_bind_result (bind_result, errno, failing_path, "Can't bind mount %s on %s", arg1, arg2); + assert (failing_path == NULL); /* otherwise we would have died */ break; case PRIV_SEP_OP_PROC_MOUNT: @@ -1096,7 +1112,18 @@ case PRIV_SEP_OP_TMPFS_MOUNT: { - cleanup_free char *mode = xasprintf ("mode=%#o", perms); + cleanup_free char *mode = NULL; + + /* This check should be unnecessary since we checked this when parsing + * the --size option as well. However, better be safe than sorry. */ + if (size_arg > MAX_TMPFS_BYTES) + die_with_error ("Specified tmpfs size too large (%zu > %zu)", size_arg, MAX_TMPFS_BYTES); + + if (size_arg != 0) + mode = xasprintf ("mode=%#o,size=%zu", perms, size_arg); + else + mode = xasprintf ("mode=%#o", perms); + cleanup_free char *opt = label_mount (mode, opt_file_label); if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) die_with_error ("Can't mount tmpfs on %s", arg1); @@ -1197,12 +1224,12 @@ PRIV_SEP_OP_BIND_MOUNT, (op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | (op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), - 0, source, dest); + 0, 0, source, dest); break; case SETUP_REMOUNT_RO_NO_RECURSIVE: privileged_op (privileged_op_socket, - PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, NULL, dest); + PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, 0, NULL, dest); break; case SETUP_MOUNT_PROC: @@ -1213,14 +1240,14 @@ { /* Our own procfs */ privileged_op (privileged_op_socket, - PRIV_SEP_OP_PROC_MOUNT, 0, 0, + PRIV_SEP_OP_PROC_MOUNT, 0, 0, 0, dest, NULL); } else { /* Use system procfs, as we share pid namespace anyway */ privileged_op (privileged_op_socket, - PRIV_SEP_OP_BIND_MOUNT, 0, 0, + PRIV_SEP_OP_BIND_MOUNT, 0, 0, 0, "oldroot/proc", dest); } @@ -1242,7 +1269,7 @@ } privileged_op (privileged_op_socket, - PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, + PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, 0, subdir, subdir); } @@ -1253,7 +1280,7 @@ die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, - PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, + PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, 0, dest, NULL); static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; @@ -1264,7 +1291,7 @@ if (create_file (node_dest, 0444, NULL) != 0) die_with_error ("Can't create file %s/%s", op->dest, devnodes[i]); privileged_op (privileged_op_socket, - PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, + PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, 0, node_src, node_dest); } @@ -1298,7 +1325,7 @@ if (mkdir (pts, 0755) == -1) die_with_error ("Can't create %s/devpts", op->dest); privileged_op (privileged_op_socket, - PRIV_SEP_OP_DEVPTS_MOUNT, 0, 0, pts, NULL); + PRIV_SEP_OP_DEVPTS_MOUNT, 0, 0, 0, pts, NULL); if (symlink ("pts/ptmx", ptmx) != 0) die_with_error ("Can't make symlink at %s/ptmx", op->dest); @@ -1318,7 +1345,7 @@ die_with_error ("creating %s/console", op->dest); privileged_op (privileged_op_socket, - PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, + PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, 0, src_tty_dev, dest_console); } @@ -1333,7 +1360,7 @@ die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, - PRIV_SEP_OP_TMPFS_MOUNT, 0, op->perms, + PRIV_SEP_OP_TMPFS_MOUNT, 0, op->perms, op->size, dest, NULL); break; @@ -1342,7 +1369,7 @@ die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, - PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, + PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, 0, dest, NULL); break; @@ -1423,7 +1450,7 @@ privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, (op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), - 0, tempfile, dest); + 0, 0, tempfile, dest); /* Remove the file so we're sure the app can't get to it in any other way. Its outside the container chroot, so it shouldn't be possible, but lets @@ -1441,7 +1468,7 @@ case SETUP_SET_HOSTNAME: assert (op->dest != NULL); /* guaranteed by the constructor */ privileged_op (privileged_op_socket, - PRIV_SEP_OP_SET_HOSTNAME, 0, 0, + PRIV_SEP_OP_SET_HOSTNAME, 0, 0, 0, op->dest, NULL); break; @@ -1450,7 +1477,7 @@ } } privileged_op (privileged_op_socket, - PRIV_SEP_OP_DONE, 0, 0, NULL, NULL); + PRIV_SEP_OP_DONE, 0, 0, 0, NULL, NULL); } /* Do not leak file descriptors already used by setup_newroot () */ @@ -1537,6 +1564,7 @@ size_t buffer_size, uint32_t *flags, uint32_t *perms, + size_t *size_arg, const char **arg1, const char **arg2) { @@ -1561,6 +1589,7 @@ *flags = op->flags; *perms = op->perms; + *size_arg = op->size_arg; *arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset); *arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset); @@ -1575,25 +1604,10 @@ } static int -takes_perms (const char *next_option) +is_modifier_option (const char *option) { - static const char *const options_that_take_perms[] = - { - "--bind-data", - "--dir", - "--file", - "--ro-bind-data", - "--tmpfs", - }; - size_t i; - - for (i = 0; i < N_ELEMENTS (options_that_take_perms); i++) - { - if (strcmp (options_that_take_perms[i], next_option) == 0) - return 1; - } - - return 0; + return strcmp (option, "--perms") == 0 + || strcmp(option, "--size") == 0; } static void @@ -1630,9 +1644,6 @@ { const char *arg = argv[0]; - if (next_perms >= 0 && !takes_perms (arg)) - die ("--perms must be followed by an option that creates a file"); - if (strcmp (arg, "--help") == 0) { usage (EXIT_SUCCESS, stdout); @@ -1890,6 +1901,13 @@ op->perms = 0755; next_perms = -1; + + /* If the option is unset, next_size_arg is zero, which results in + * the default tmpfs size. This is exactly what we want. */ + op->size = next_size_arg; + + next_size_arg = 0; + argv += 1; argc -= 1; } @@ -2383,6 +2401,9 @@ if (argc < 2) die ("--perms takes an argument"); + if (next_perms != -1) + die ("--perms given twice for the same action"); + perms = strtoul (argv[1], &endptr, 8); if (argv[1][0] == '\0' @@ -2396,6 +2417,42 @@ argv += 1; argc -= 1; } + else if (strcmp (arg, "--size") == 0) + { + unsigned long long size; + char *endptr = NULL; + + if (is_privileged) + die ("The --size option is not permitted in setuid mode"); + + if (argc < 2) + die ("--size takes an argument"); + + if (next_size_arg != 0) + die ("--size given twice for the same action"); + + errno = 0; /* reset errno so we can detect ERANGE from strtoull */ + + size = strtoull (argv[1], &endptr, 0); + + /* isdigit: Not only check that the first digit is not '\0', but + * simultaneously guard against negative numbers or preceding + * spaces. */ + if (errno != 0 /* from strtoull */ + || !isdigit(argv[1][0]) + || endptr == NULL + || *endptr != '\0' + || size == 0) + die ("--size takes a non-zero number of bytes"); + + if (size > MAX_TMPFS_BYTES) + die ("--size (for tmpfs) is limited to %zu", MAX_TMPFS_BYTES); + + next_size_arg = (size_t) size; + + argv += 1; + argc -= 1; + } else if (strcmp (arg, "--chmod") == 0) { unsigned long perms; @@ -2435,6 +2492,16 @@ break; } + /* If --perms was set for the current action but the current action + * didn't consume the setting, apparently --perms wasn't suitable for + * this action. */ + if (!is_modifier_option(arg) && next_perms >= 0) + die ("--perms must be followed by an option that creates a file"); + + /* Similarly for --size. */ + if (!is_modifier_option(arg) && next_size_arg != 0) + die ("--size must be followed by --tmpfs"); + argv++; argc--; } @@ -2790,6 +2857,9 @@ die ("No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); } + if (errno == ENOSPC) + die ("Creating new namespace failed: nesting depth or /proc/sys/user/max_*_namespaces exceeded (ENOSPC)"); + die_with_error ("Creating new namespace failed"); } @@ -3016,6 +3086,7 @@ int status; uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ uint32_t op, flags, perms; + size_t size_arg; const char *arg1, *arg2; cleanup_fd int unpriv_socket = -1; @@ -3025,8 +3096,8 @@ do { op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), - &flags, &perms, &arg1, &arg2); - privileged_op (-1, op, flags, perms, arg1, arg2); + &flags, &perms, &size_arg, &arg1, &arg2); + privileged_op (-1, op, flags, perms, size_arg, arg1, arg2); if (write (unpriv_socket, buffer, 1) != 1) die ("Can't write to op_socket"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/bwrap.xml new/bubblewrap-0.7.0/bwrap.xml --- old/bubblewrap-0.6.2/bwrap.xml 2022-04-23 18:42:39.000000000 +0200 +++ new/bubblewrap-0.7.0/bwrap.xml 2022-10-27 18:35:57.000000000 +0200 @@ -207,6 +207,9 @@ (rwxr-xr-x). However, if a <option>--perms</option> option is in effect, and it sets the permissions for group or other to zero, then newly-created parent directories will also have their corresponding permission set to zero. + <option>--size</option> modifies the size of the created mount when preceding a + <option>--tmpfs</option> action; <option>--perms</option> and <option>--size</option> + can be combined. </para> <variablelist> <varlistentry> @@ -217,7 +220,24 @@ Subsequent operations are not affected: for example, <literal>--perms 0700 --tmpfs /a --tmpfs /b</literal> will mount <filename>/a</filename> with permissions 0700, then return to - the default permissions for <filename>/b</filename>.</para></listitem> + the default permissions for <filename>/b</filename>. + Note that <option>--perms</option> and <option>--size</option> can be + combined: <literal>--perms 0700 --size 10485760 --tmpfs /s</literal> will apply + permissions as well as a maximum size to the created tmpfs.</para></listitem> + </varlistentry> + <varlistentry> + <term><option>--size <arg choice="plain">BYTES</arg></option></term> + <listitem><para>This option does nothing on its own, and must be followed + by <literal>--tmpfs</literal>. It sets the size in bytes for the next tmpfs. + For example, <literal>--size 10485760 --tmpfs /tmp</literal> will create a tmpfs + at <filename>/tmp</filename> of size 10MiB. Subsequent operations are not + affected: for example, + <literal>--size 10485760 --tmpfs /a --tmpfs /b</literal> will mount + <filename>/a</filename> with size 10MiB, then return to the default size for + <filename>/b</filename>. + Note that <option>--perms</option> and <option>--size</option> can be + combined: <literal>--size 10485760 --perms 0700 --tmpfs /s</literal> will apply + permissions as well as a maximum size to the created tmpfs.</para></listitem> </varlistentry> <varlistentry> <term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term> @@ -260,7 +280,9 @@ <listitem> <para>Mount new tmpfs on <arg choice="plain">DEST</arg>. If the previous option was <option>--perms</option>, it sets the - mode of the tmpfs. Otherwise, the tmpfs has mode 0755.</para> + mode of the tmpfs. Otherwise, the tmpfs has mode 0755. + If the previous option was <option>--size</option>, it sets the + size in bytes of the tmpfs. Otherwise, the tmpfs has the default size.</para> </listitem> </varlistentry> <varlistentry> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/completions/bash/bwrap new/bubblewrap-0.7.0/completions/bash/bwrap --- old/bubblewrap-0.6.2/completions/bash/bwrap 2022-04-21 11:02:36.000000000 +0200 +++ new/bubblewrap-0.7.0/completions/bash/bwrap 2022-10-27 18:35:57.000000000 +0200 @@ -54,6 +54,7 @@ --ro-bind --seccomp --setenv + --size --symlink --sync-fd --uid diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/completions/zsh/_bwrap new/bubblewrap-0.7.0/completions/zsh/_bwrap --- old/bubblewrap-0.6.2/completions/zsh/_bwrap 2022-04-21 11:02:36.000000000 +0200 +++ new/bubblewrap-0.7.0/completions/zsh/_bwrap 2022-10-27 18:35:57.000000000 +0200 @@ -1,11 +1,23 @@ #compdef bwrap +_bwrap_args_after_perms_size=( + # Please sort alphabetically (in LC_ALL=C order) by option name + '--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/' +) + _bwrap_args_after_perms=( # Please sort alphabetically (in LC_ALL=C order) by option name '--bind-data[Copy from FD to file which is bind-mounted on DEST]: :_guard "[0-9]#" "file descriptor to read content":destination:_files' '--dir[Create dir at DEST]:directory to create:_files -/' '--file[Copy from FD to destination DEST]: :_guard "[0-9]#" "file descriptor to read content from":destination:_files' '--ro-bind-data[Copy from FD to file which is readonly bind-mounted on DEST]: :_guard "[0-9]#" "file descriptor to read content from":destination:_files' + '--size[Set size in bytes for next action argument]: :->after_perms_size' + '--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/' +) + +_bwrap_args_after_size=( + # Please sort alphabetically (in LC_ALL=C order) by option name + '--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms_size' '--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/' ) @@ -47,6 +59,7 @@ '--ro-bind[Bind mount the host path SRC readonly on DEST]:source:_files:destination:_files' '--seccomp[Load and use seccomp rules from FD]: :_guard "[0-9]#" "file descriptor to read seccomp rules from"' '--setenv[Set an environment variable]:variable to set:_parameters -g "*export*":value of variable: :' + '--size[Set size in bytes for next action argument]: :->after_size' '--symlink[Create symlink at DEST with target SRC]:symlink target:_files:symlink to create:_files:' '--sync-fd[Keep this fd open while sandbox is running]: :_guard "[0-9]#" "file descriptor to keep open"' '--uid[Custom uid in the sandbox (requires --unshare-user or --userns)]: :_guard "[0-9]#" "numeric group ID"' @@ -73,6 +86,14 @@ _values -S ' ' 'option' $_bwrap_args_after_perms ;; + after_size) + _values -S ' ' 'option' $_bwrap_args_after_size + ;; + + after_perms_size) + _values -S ' ' 'option' $_bwrap_args_after_perms_size + ;; + caps) # $ grep -E '#define\sCAP_\w+\s+[0-9]+' /usr/include/linux/capability.h | awk '{print $2}' | xargs echo local all_caps=( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/configure new/bubblewrap-0.7.0/configure --- old/bubblewrap-0.6.2/configure 2022-05-11 16:05:52.000000000 +0200 +++ new/bubblewrap-0.7.0/configure 2022-11-07 18:40:51.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for bubblewrap 0.6.2. +# Generated by GNU Autoconf 2.71 for bubblewrap 0.7.0. # # Report bugs to <atomic-de...@projectatomic.io>. # @@ -610,8 +610,8 @@ # Identity of this package. PACKAGE_NAME='bubblewrap' PACKAGE_TARNAME='bubblewrap' -PACKAGE_VERSION='0.6.2' -PACKAGE_STRING='bubblewrap 0.6.2' +PACKAGE_VERSION='0.7.0' +PACKAGE_STRING='bubblewrap 0.7.0' PACKAGE_BUGREPORT='atomic-de...@projectatomic.io' PACKAGE_URL='' @@ -1344,7 +1344,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bubblewrap 0.6.2 to adapt to many kinds of systems. +\`configure' configures bubblewrap 0.7.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1411,7 +1411,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bubblewrap 0.6.2:";; + short | recursive ) echo "Configuration of bubblewrap 0.7.0:";; esac cat <<\_ACEOF @@ -1542,7 +1542,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bubblewrap configure 0.6.2 +bubblewrap configure 0.7.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1698,7 +1698,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bubblewrap $as_me 0.6.2, which was +It was created by bubblewrap $as_me 0.7.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -4266,7 +4266,7 @@ # Define the identity of the package. PACKAGE='bubblewrap' - VERSION='0.6.2' + VERSION='0.7.0' # Some tools Automake needs. @@ -5912,7 +5912,9 @@ fi -if test "x$with_bash_completion_dir" = "xyes"; then +if test "x$with_bash_completion_dir" = "xyes" +then : + pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for bash-completion >= 2.0" >&5 @@ -5984,8 +5986,11 @@ printf "%s\n" "yes" >&6; } BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`" fi -else + +else $as_nop + BASH_COMPLETION_DIR="$with_bash_completion_dir" + fi @@ -6008,10 +6013,11 @@ fi -if test "x$with_zsh_completion_dir" = "xyes"; then - ZSH_COMPLETION_DIR="$datadir/zsh/site-functions" -else - ZSH_COMPLETION_DIR="$with_zsh_completion_dir" +if test "x$with_zsh_completion_dir" = "xyes" +then : + ZSH_COMPLETION_DIR="$datadir/zsh/site-functions" +else $as_nop + ZSH_COMPLETION_DIR="$with_zsh_completion_dir" fi @@ -6033,7 +6039,9 @@ enableval=$enable_selinux; fi -if test "x$enable_selinux" != "xno"; then +if test "x$enable_selinux" != "xno" +then : + pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libselinux >= 2.1.9" >&5 @@ -6109,9 +6117,10 @@ have_selinux=yes M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX" fi - if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then - as_fn_error $? "*** SELinux support requested but libraries not found" "$LINENO" 5 - fi + if test "x$have_selinux" = xno && test "x$enable_selinux" = xyes +then : + as_fn_error $? "*** SELinux support requested but libraries not found" "$LINENO" 5 +fi pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libselinux >= 2.3" >&5 @@ -6185,6 +6194,7 @@ printf "%s\n" "#define HAVE_SELINUX_2_3 1" >>confdefs.h fi + fi if test "$have_selinux" = "yes"; then HAVE_SELINUX_TRUE= @@ -6297,8 +6307,9 @@ fi -if test "$ac_cv_lib_cap_cap_from_text" != "yes"; then - as_fn_error $? "*** libcap requested but not found" "$LINENO" 5 +if test "$ac_cv_lib_cap_cap_from_text" != "yes" +then : + as_fn_error $? "*** libcap requested but not found" "$LINENO" 5 fi @@ -6947,7 +6958,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bubblewrap $as_me 0.6.2, which was +This file was extended by bubblewrap $as_me 0.7.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -7015,7 +7026,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -bubblewrap config.status 0.6.2 +bubblewrap config.status 0.7.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/configure.ac new/bubblewrap-0.7.0/configure.ac --- old/bubblewrap-0.6.2/configure.ac 2022-05-11 16:05:17.000000000 +0200 +++ new/bubblewrap-0.7.0/configure.ac 2022-11-07 18:40:20.000000000 +0100 @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([bubblewrap], [0.6.2], [atomic-de...@projectatomic.io]) +AC_INIT([bubblewrap], [0.7.0], [atomic-de...@projectatomic.io]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) @@ -42,13 +42,15 @@ [], [with_bash_completion_dir=yes]) -if test "x$with_bash_completion_dir" = "xyes"; then +AS_IF([test "x$with_bash_completion_dir" = "xyes"], + [ PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"]) -else + ], + [ BASH_COMPLETION_DIR="$with_bash_completion_dir" -fi + ]) AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) @@ -59,11 +61,9 @@ [], [with_zsh_completion_dir=yes]) -if test "x$with_zsh_completion_dir" = "xyes"; then - [ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"] -else - ZSH_COMPLETION_DIR="$with_zsh_completion_dir" -fi +AS_IF([test "x$with_zsh_completion_dir" = "xyes"], + [ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"], + [ZSH_COMPLETION_DIR="$with_zsh_completion_dir"]) AC_SUBST([ZSH_COMPLETION_DIR]) @@ -72,19 +72,18 @@ # ------------------------------------------------------------------------------ have_selinux=no AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) -if test "x$enable_selinux" != "xno"; then +AS_IF([test "x$enable_selinux" != "xno"], [ PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9], [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available]) have_selinux=yes M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"], [have_selinux=no]) - if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then - AC_MSG_ERROR([*** SELinux support requested but libraries not found]) - fi + AS_IF([test "x$have_selinux" = xno && test "x$enable_selinux" = xyes], + [AC_MSG_ERROR([*** SELinux support requested but libraries not found])]) PKG_CHECK_MODULES([SELINUX_2_3], [libselinux >= 2.3], [AC_DEFINE(HAVE_SELINUX_2_3, 1, [Define if SELinux is version >= 2.3])], [:]) -fi +]) AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement @@ -113,9 +112,8 @@ AC_CHECK_LIB(cap, cap_from_text) -if test "$ac_cv_lib_cap_cap_from_text" != "yes"; then - AC_MSG_ERROR([*** libcap requested but not found]) -fi +AS_IF([test "$ac_cv_lib_cap_cap_from_text" != "yes"], + [AC_MSG_ERROR([*** libcap requested but not found])]) AC_ARG_WITH(priv-mode, AS_HELP_STRING([--with-priv-mode=setuid/none], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/meson.build new/bubblewrap-0.7.0/meson.build --- old/bubblewrap-0.6.2/meson.build 2022-05-11 16:05:25.000000000 +0200 +++ new/bubblewrap-0.7.0/meson.build 2022-11-07 18:40:16.000000000 +0100 @@ -1,7 +1,7 @@ project( 'bubblewrap', 'c', - version : '0.6.2', + version : '0.7.0', meson_version : '>=0.49.0', default_options : [ 'warning_level=2', @@ -121,8 +121,10 @@ 'network.c', 'utils.c', ], + build_rpath : get_option('build_rpath'), install : true, install_dir : bwrapdir, + install_rpath : get_option('install_rpath'), dependencies : [selinux_dep, libcap_dep], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/meson_options.txt new/bubblewrap-0.7.0/meson_options.txt --- old/bubblewrap-0.6.2/meson_options.txt 2022-05-11 12:04:42.000000000 +0200 +++ new/bubblewrap-0.7.0/meson_options.txt 2022-08-09 12:28:37.000000000 +0200 @@ -16,6 +16,16 @@ description : 'install bwrap in this directory [default: bindir, or libexecdir in subprojects]', ) option( + 'build_rpath', + type : 'string', + description : 'set a RUNPATH or RPATH on the bwrap executable', +) +option( + 'install_rpath', + type : 'string', + description : 'set a RUNPATH or RPATH on the bwrap executable', +) +option( 'man', type : 'feature', description : 'generate man pages', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/tests/test-run.sh new/bubblewrap-0.7.0/tests/test-run.sh --- old/bubblewrap-0.6.2/tests/test-run.sh 2022-01-31 18:19:17.000000000 +0100 +++ new/bubblewrap-0.7.0/tests/test-run.sh 2022-10-27 18:35:57.000000000 +0200 @@ -8,7 +8,7 @@ bn=$(basename "$0") -echo "1..54" +echo "1..57" # Test help ${BWRAP} --help > help.txt @@ -39,9 +39,16 @@ CAP="" fi - if ! ${is_uidzero} && $RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /etc/shadow; then + if ! cat /etc/shadow >/dev/null && + $RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /tmp/foo; then + assert_not_reached Could read /etc/shadow via /tmp/foo bind-mount + fi + + if ! cat /etc/shadow >/dev/null && + $RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /etc/shadow; then assert_not_reached Could read /etc/shadow fi + echo "ok - cannot read /etc/shadow with $ALT" # Unreadable dir if [ "x$UNREADABLE" != "x" ]; then @@ -88,7 +95,7 @@ echo "ok namespace id info in info and json-status fd" -if ! which strace >/dev/null 2>/dev/null || ! strace -h | grep -v -e default | grep -e fault >/dev/null; then +if ! command -v strace >/dev/null || ! strace -h | grep -v -e default | grep -e fault >/dev/null; then echo "ok - # SKIP no strace fault injection" else ! strace -o /dev/null -f -e trace=prctl -e fault=prctl:when=39 $RUN --die-with-parent --json-status-fd 42 true 42>json-status.json @@ -398,6 +405,29 @@ assert_file_has_content dir-permissions '^755$' echo "ok - tmpfs has expected permissions" +# 1048576 = 1 MiB +$RUN \ + --size 1048576 --tmpfs "$(pwd -P)" \ + df --output=size --block-size=1K "$(pwd -P)" > dir-size +assert_file_has_content dir-size '^ *1024$' +$RUN \ + --size 1048576 --perms 01777 --tmpfs "$(pwd -P)" \ + stat -c '%a' "$(pwd -P)" > dir-permissions +assert_file_has_content dir-permissions '^1777$' +$RUN \ + --size 1048576 --perms 01777 --tmpfs "$(pwd -P)" \ + df --output=size --block-size=1K "$(pwd -P)" > dir-size +assert_file_has_content dir-size '^ *1024$' +$RUN \ + --perms 01777 --size 1048576 --tmpfs "$(pwd -P)" \ + stat -c '%a' "$(pwd -P)" > dir-permissions +assert_file_has_content dir-permissions '^1777$' +$RUN \ + --perms 01777 --size 1048576 --tmpfs "$(pwd -P)" \ + df --output=size --block-size=1K "$(pwd -P)" > dir-size +assert_file_has_content dir-size '^ *1024$' +echo "ok - tmpfs has expected size" + $RUN \ --file 0 /tmp/file \ stat -c '%a' /tmp/file < /dev/null > file-permissions @@ -424,6 +454,40 @@ assert_file_has_content file-permissions '^640$' echo "ok - files have expected permissions" +if $RUN --size 0 --tmpfs /tmp/a true; then + assert_not_reached Zero tmpfs size allowed +fi +if $RUN --size 123bogus --tmpfs /tmp/a true; then + assert_not_reached Bogus tmpfs size allowed +fi +if $RUN --size '' --tmpfs /tmp/a true; then + assert_not_reached Empty tmpfs size allowed +fi +if $RUN --size -12345678 --tmpfs /tmp/a true; then + assert_not_reached Negative tmpfs size allowed +fi +if $RUN --size ' -12345678' --tmpfs /tmp/a true; then + assert_not_reached Negative tmpfs size with space allowed +fi +# This is 2^64 +if $RUN --size 18446744073709551616 --tmpfs /tmp/a true; then + assert_not_reached Overflowing tmpfs size allowed +fi +# This is 2^63 + 1; note that the current max size is SIZE_MAX/2 +if $RUN --size 9223372036854775809 --tmpfs /tmp/a true; then + assert_not_reached Too-large tmpfs size allowed +fi +echo "ok - bogus tmpfs size not allowed" + +if $RUN --perms 0640 --perms 0640 --tmpfs /tmp/a true; then + assert_not_reached Multiple perms options allowed +fi +if $RUN --size 1048576 --size 1048576 --tmpfs /tmp/a true; then + assert_not_reached Multiple perms options allowed +fi +echo "ok - --perms and --size only allowed once" + + FOO= BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout assert_file_has_content stdout barbaz FOO=wrong BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bubblewrap-0.6.2/tests/use-as-subproject/meson.build new/bubblewrap-0.7.0/tests/use-as-subproject/meson.build --- old/bubblewrap-0.6.2/tests/use-as-subproject/meson.build 2022-02-19 15:35:00.000000000 +0100 +++ new/bubblewrap-0.7.0/tests/use-as-subproject/meson.build 2022-08-09 12:28:37.000000000 +0200 @@ -14,6 +14,7 @@ subproject( 'bubblewrap', default_options : [ + 'install_rpath=${ORIGIN}/../lib', 'program_prefix=not-flatpak-', ], )