Merge pull request #509 from tomsmeding/tmpfs-size

Add --size option to control size of a --tmpfs
This commit is contained in:
Simon McVittie
2022-10-26 21:43:25 +01:00
committed by GitHub
5 changed files with 207 additions and 41 deletions

View File

@@ -23,6 +23,7 @@
#include <sched.h> #include <sched.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <ctype.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h> #include <sys/wait.h>
@@ -52,6 +53,12 @@
__result; })) __result; }))
#endif #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 */ /* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */
static uid_t real_uid; static uid_t real_uid;
static gid_t real_gid; static gid_t real_gid;
@@ -91,6 +98,7 @@ int opt_userns_fd = -1;
int opt_userns2_fd = -1; int opt_userns2_fd = -1;
int opt_pidns_fd = -1; int opt_pidns_fd = -1;
int next_perms = -1; int next_perms = -1;
size_t next_size_arg = 0;
#define CAP_TO_MASK_0(x) (1L << ((x) & 31)) #define CAP_TO_MASK_0(x) (1L << ((x) & 31))
#define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32) #define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32)
@@ -149,6 +157,7 @@ struct _SetupOp
int fd; int fd;
SetupOpFlag flags; SetupOpFlag flags;
int perms; int perms;
size_t size; /* number of bytes, zero means unset/default */
SetupOp *next; SetupOp *next;
}; };
@@ -177,6 +186,7 @@ typedef struct
uint32_t op; uint32_t op;
uint32_t flags; uint32_t flags;
uint32_t perms; uint32_t perms;
size_t size_arg;
uint32_t arg1_offset; uint32_t arg1_offset;
uint32_t arg2_offset; uint32_t arg2_offset;
} PrivSepOp; } PrivSepOp;
@@ -341,6 +351,7 @@ usage (int ecode, FILE *out)
" --cap-add CAP Add cap CAP when running as privileged user\n" " --cap-add CAP Add cap CAP when running as privileged user\n"
" --cap-drop CAP Drop 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" " --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" " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n"
); );
exit (ecode); exit (ecode);
@@ -1001,6 +1012,7 @@ privileged_op (int privileged_op_socket,
uint32_t op, uint32_t op,
uint32_t flags, uint32_t flags,
uint32_t perms, uint32_t perms,
size_t size_arg,
const char *arg1, const char *arg1,
const char *arg2) const char *arg2)
{ {
@@ -1033,6 +1045,7 @@ privileged_op (int privileged_op_socket,
op_buffer->op = op; op_buffer->op = op;
op_buffer->flags = flags; op_buffer->flags = flags;
op_buffer->perms = perms; op_buffer->perms = perms;
op_buffer->size_arg = size_arg;
op_buffer->arg1_offset = arg1_offset; op_buffer->arg1_offset = arg1_offset;
op_buffer->arg2_offset = arg2_offset; op_buffer->arg2_offset = arg2_offset;
if (arg1 != NULL) if (arg1 != NULL)
@@ -1099,7 +1112,18 @@ privileged_op (int privileged_op_socket,
case PRIV_SEP_OP_TMPFS_MOUNT: 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); cleanup_free char *opt = label_mount (mode, opt_file_label);
if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0)
die_with_error ("Can't mount tmpfs on %s", arg1); die_with_error ("Can't mount tmpfs on %s", arg1);
@@ -1200,12 +1224,12 @@ setup_newroot (bool unshare_pid,
PRIV_SEP_OP_BIND_MOUNT, PRIV_SEP_OP_BIND_MOUNT,
(op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | (op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) |
(op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), (op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0),
0, source, dest); 0, 0, source, dest);
break; break;
case SETUP_REMOUNT_RO_NO_RECURSIVE: case SETUP_REMOUNT_RO_NO_RECURSIVE:
privileged_op (privileged_op_socket, 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; break;
case SETUP_MOUNT_PROC: case SETUP_MOUNT_PROC:
@@ -1216,14 +1240,14 @@ setup_newroot (bool unshare_pid,
{ {
/* Our own procfs */ /* Our own procfs */
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_PROC_MOUNT, 0, 0, PRIV_SEP_OP_PROC_MOUNT, 0, 0, 0,
dest, NULL); dest, NULL);
} }
else else
{ {
/* Use system procfs, as we share pid namespace anyway */ /* Use system procfs, as we share pid namespace anyway */
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_BIND_MOUNT, 0, 0, PRIV_SEP_OP_BIND_MOUNT, 0, 0, 0,
"oldroot/proc", dest); "oldroot/proc", dest);
} }
@@ -1245,7 +1269,7 @@ setup_newroot (bool unshare_pid,
} }
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, 0,
subdir, subdir); subdir, subdir);
} }
@@ -1256,7 +1280,7 @@ setup_newroot (bool unshare_pid,
die_with_error ("Can't mkdir %s", op->dest); die_with_error ("Can't mkdir %s", op->dest);
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, 0,
dest, NULL); dest, NULL);
static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" };
@@ -1267,7 +1291,7 @@ setup_newroot (bool unshare_pid,
if (create_file (node_dest, 0444, NULL) != 0) if (create_file (node_dest, 0444, NULL) != 0)
die_with_error ("Can't create file %s/%s", op->dest, devnodes[i]); die_with_error ("Can't create file %s/%s", op->dest, devnodes[i]);
privileged_op (privileged_op_socket, 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); node_src, node_dest);
} }
@@ -1301,7 +1325,7 @@ setup_newroot (bool unshare_pid,
if (mkdir (pts, 0755) == -1) if (mkdir (pts, 0755) == -1)
die_with_error ("Can't create %s/devpts", op->dest); die_with_error ("Can't create %s/devpts", op->dest);
privileged_op (privileged_op_socket, 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) if (symlink ("pts/ptmx", ptmx) != 0)
die_with_error ("Can't make symlink at %s/ptmx", op->dest); die_with_error ("Can't make symlink at %s/ptmx", op->dest);
@@ -1321,7 +1345,7 @@ setup_newroot (bool unshare_pid,
die_with_error ("creating %s/console", op->dest); die_with_error ("creating %s/console", op->dest);
privileged_op (privileged_op_socket, 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); src_tty_dev, dest_console);
} }
@@ -1336,7 +1360,7 @@ setup_newroot (bool unshare_pid,
die_with_error ("Can't mkdir %s", op->dest); die_with_error ("Can't mkdir %s", op->dest);
privileged_op (privileged_op_socket, 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); dest, NULL);
break; break;
@@ -1345,7 +1369,7 @@ setup_newroot (bool unshare_pid,
die_with_error ("Can't mkdir %s", op->dest); die_with_error ("Can't mkdir %s", op->dest);
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, 0,
dest, NULL); dest, NULL);
break; break;
@@ -1426,7 +1450,7 @@ setup_newroot (bool unshare_pid,
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_BIND_MOUNT, PRIV_SEP_OP_BIND_MOUNT,
(op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), (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. /* 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 Its outside the container chroot, so it shouldn't be possible, but lets
@@ -1444,7 +1468,7 @@ setup_newroot (bool unshare_pid,
case SETUP_SET_HOSTNAME: case SETUP_SET_HOSTNAME:
assert (op->dest != NULL); /* guaranteed by the constructor */ assert (op->dest != NULL); /* guaranteed by the constructor */
privileged_op (privileged_op_socket, privileged_op (privileged_op_socket,
PRIV_SEP_OP_SET_HOSTNAME, 0, 0, PRIV_SEP_OP_SET_HOSTNAME, 0, 0, 0,
op->dest, NULL); op->dest, NULL);
break; break;
@@ -1453,7 +1477,7 @@ setup_newroot (bool unshare_pid,
} }
} }
privileged_op (privileged_op_socket, 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 () */ /* Do not leak file descriptors already used by setup_newroot () */
@@ -1540,6 +1564,7 @@ read_priv_sec_op (int read_socket,
size_t buffer_size, size_t buffer_size,
uint32_t *flags, uint32_t *flags,
uint32_t *perms, uint32_t *perms,
size_t *size_arg,
const char **arg1, const char **arg1,
const char **arg2) const char **arg2)
{ {
@@ -1564,6 +1589,7 @@ read_priv_sec_op (int read_socket,
*flags = op->flags; *flags = op->flags;
*perms = op->perms; *perms = op->perms;
*size_arg = op->size_arg;
*arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset); *arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset);
*arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset); *arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset);
@@ -1578,25 +1604,10 @@ print_version_and_exit (void)
} }
static int static int
takes_perms (const char *next_option) is_modifier_option (const char *option)
{ {
static const char *const options_that_take_perms[] = return strcmp (option, "--perms") == 0
{ || strcmp(option, "--size") == 0;
"--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;
} }
static void static void
@@ -1633,9 +1644,6 @@ parse_args_recurse (int *argcp,
{ {
const char *arg = argv[0]; 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) if (strcmp (arg, "--help") == 0)
{ {
usage (EXIT_SUCCESS, stdout); usage (EXIT_SUCCESS, stdout);
@@ -1893,6 +1901,13 @@ parse_args_recurse (int *argcp,
op->perms = 0755; op->perms = 0755;
next_perms = -1; 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; argv += 1;
argc -= 1; argc -= 1;
} }
@@ -2386,6 +2401,9 @@ parse_args_recurse (int *argcp,
if (argc < 2) if (argc < 2)
die ("--perms takes an argument"); die ("--perms takes an argument");
if (next_perms != -1)
die ("--perms given twice for the same action");
perms = strtoul (argv[1], &endptr, 8); perms = strtoul (argv[1], &endptr, 8);
if (argv[1][0] == '\0' if (argv[1][0] == '\0'
@@ -2396,6 +2414,42 @@ parse_args_recurse (int *argcp,
next_perms = (int) perms; next_perms = (int) perms;
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; argv += 1;
argc -= 1; argc -= 1;
} }
@@ -2438,6 +2492,16 @@ parse_args_recurse (int *argcp,
break; 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++; argv++;
argc--; argc--;
} }
@@ -3022,6 +3086,7 @@ main (int argc,
int status; int status;
uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */
uint32_t op, flags, perms; uint32_t op, flags, perms;
size_t size_arg;
const char *arg1, *arg2; const char *arg1, *arg2;
cleanup_fd int unpriv_socket = -1; cleanup_fd int unpriv_socket = -1;
@@ -3031,8 +3096,8 @@ main (int argc,
do do
{ {
op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer),
&flags, &perms, &arg1, &arg2); &flags, &perms, &size_arg, &arg1, &arg2);
privileged_op (-1, op, flags, perms, arg1, arg2); privileged_op (-1, op, flags, perms, size_arg, arg1, arg2);
if (write (unpriv_socket, buffer, 1) != 1) if (write (unpriv_socket, buffer, 1) != 1)
die ("Can't write to op_socket"); die ("Can't write to op_socket");
} }

View File

@@ -207,6 +207,9 @@
(rwxr-xr-x). However, if a <option>--perms</option> option is in effect, and (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 it sets the permissions for group or other to zero, then newly-created
parent directories will also have their corresponding permission set to zero. 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> </para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
@@ -217,7 +220,24 @@
Subsequent operations are not affected: for example, Subsequent operations are not affected: for example,
<literal>--perms 0700 --tmpfs /a --tmpfs /b</literal> will mount <literal>--perms 0700 --tmpfs /a --tmpfs /b</literal> will mount
<filename>/a</filename> with permissions 0700, then return to <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>
<varlistentry> <varlistentry>
<term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term> <term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
@@ -260,7 +280,9 @@
<listitem> <listitem>
<para>Mount new tmpfs on <arg choice="plain">DEST</arg>. <para>Mount new tmpfs on <arg choice="plain">DEST</arg>.
If the previous option was <option>--perms</option>, it sets the 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> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@@ -54,6 +54,7 @@ _bwrap() {
--ro-bind --ro-bind
--seccomp --seccomp
--setenv --setenv
--size
--symlink --symlink
--sync-fd --sync-fd
--uid --uid

View File

@@ -1,11 +1,23 @@
#compdef bwrap #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=( _bwrap_args_after_perms=(
# Please sort alphabetically (in LC_ALL=C order) by option name # 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' '--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 -/' '--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' '--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' '--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 -/' '--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/'
) )
@@ -47,6 +59,7 @@ _bwrap_args=(
'--ro-bind[Bind mount the host path SRC readonly on DEST]:source:_files:destination:_files' '--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"' '--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: :' '--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:' '--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"' '--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"' '--uid[Custom uid in the sandbox (requires --unshare-user or --userns)]: :_guard "[0-9]#" "numeric group ID"'
@@ -73,6 +86,14 @@ _bwrap() {
_values -S ' ' 'option' $_bwrap_args_after_perms _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) caps)
# $ grep -E '#define\sCAP_\w+\s+[0-9]+' /usr/include/linux/capability.h | awk '{print $2}' | xargs echo # $ grep -E '#define\sCAP_\w+\s+[0-9]+' /usr/include/linux/capability.h | awk '{print $2}' | xargs echo
local all_caps=( local all_caps=(

View File

@@ -8,7 +8,7 @@ srcd=$(cd $(dirname "$0") && pwd)
bn=$(basename "$0") bn=$(basename "$0")
echo "1..54" echo "1..57"
# Test help # Test help
${BWRAP} --help > help.txt ${BWRAP} --help > help.txt
@@ -405,6 +405,29 @@ $RUN \
assert_file_has_content dir-permissions '^755$' assert_file_has_content dir-permissions '^755$'
echo "ok - tmpfs has expected permissions" 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 \ $RUN \
--file 0 /tmp/file \ --file 0 /tmp/file \
stat -c '%a' /tmp/file < /dev/null > file-permissions stat -c '%a' /tmp/file < /dev/null > file-permissions
@@ -431,6 +454,40 @@ $RUN \
assert_file_has_content file-permissions '^640$' assert_file_has_content file-permissions '^640$'
echo "ok - files have expected permissions" 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 FOO= BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout
assert_file_has_content stdout barbaz assert_file_has_content stdout barbaz
FOO=wrong BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout FOO=wrong BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout