Skip to content

Commit

Permalink
utils: Add a specialized function to print errno for mount(2)
Browse files Browse the repository at this point in the history
mount(2) uses ENOSPC to represent an arbitrary anti-denial-of-service
limit being exceeded, which is outside the usual meaning of
"No space left on device". We can make this clearer by catching that
particular failure mode and giving users a hint.

Reference: https://bugzilla.kernel.org/show_bug.cgi?id=218336
Resolves: ValveSoftware/steam-runtime#637
Signed-off-by: Simon McVittie <smcv@collabora.com>
  • Loading branch information
smcv committed Jan 3, 2024
1 parent 4351ec2 commit 68a0330
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 9 deletions.
19 changes: 18 additions & 1 deletion bind-mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,24 @@ die_with_bind_result (bind_mount_result res,
/* message is leaked, but we're exiting unsuccessfully anyway, so ignore */

if (want_errno)
fprintf (stderr, ": %s", strerror (saved_errno));
{
switch (res)
{
case BIND_MOUNT_ERROR_MOUNT:
case BIND_MOUNT_ERROR_REMOUNT_DEST:
case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT:
fprintf (stderr, ": %s", mount_strerror (saved_errno));
break;

case BIND_MOUNT_ERROR_REALPATH_DEST:
case BIND_MOUNT_ERROR_REOPEN_DEST:
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
case BIND_MOUNT_SUCCESS:
default:
fprintf (stderr, ": %s", strerror (saved_errno));
}
}

fprintf (stderr, "\n");
exit (1);
Expand Down
16 changes: 8 additions & 8 deletions bubblewrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ privileged_op (int privileged_op_socket,

case PRIV_SEP_OP_PROC_MOUNT:
if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0)
die_with_error ("Can't mount proc on %s", arg1);
die_with_mount_error ("Can't mount proc on %s", arg1);
break;

case PRIV_SEP_OP_TMPFS_MOUNT:
Expand All @@ -1132,19 +1132,19 @@ privileged_op (int privileged_op_socket,

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);
die_with_mount_error ("Can't mount tmpfs on %s", arg1);
break;
}

case PRIV_SEP_OP_DEVPTS_MOUNT:
if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC,
"newinstance,ptmxmode=0666,mode=620") != 0)
die_with_error ("Can't mount devpts on %s", arg1);
die_with_mount_error ("Can't mount devpts on %s", arg1);
break;

case PRIV_SEP_OP_MQUEUE_MOUNT:
if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0)
die_with_error ("Can't mount mqueue on %s", arg1);
die_with_mount_error ("Can't mount mqueue on %s", arg1);
break;

case PRIV_SEP_OP_SET_HOSTNAME:
Expand Down Expand Up @@ -3060,11 +3060,11 @@ main (int argc,
* receive mounts from the real root, but don't
* propagate mounts to the real root. */
if (mount (NULL, "/", NULL, MS_SILENT | MS_SLAVE | MS_REC, NULL) < 0)
die_with_error ("Failed to make / slave");
die_with_mount_error ("Failed to make / slave");

/* Create a tmpfs which we will use as / in the namespace */
if (mount ("tmpfs", base_path, "tmpfs", MS_NODEV | MS_NOSUID, NULL) != 0)
die_with_error ("Failed to mount tmpfs");
die_with_mount_error ("Failed to mount tmpfs");

old_cwd = get_current_dir_name ();

Expand All @@ -3083,7 +3083,7 @@ main (int argc,
die_with_error ("Creating newroot failed");

if (mount ("newroot", "newroot", NULL, MS_SILENT | MS_MGC_VAL | MS_BIND | MS_REC, NULL) < 0)
die_with_error ("setting up newroot bind");
die_with_mount_error ("setting up newroot bind");

if (mkdir ("oldroot", 0755))
die_with_error ("Creating oldroot failed");
Expand Down Expand Up @@ -3149,7 +3149,7 @@ main (int argc,

/* The old root better be rprivate or we will send unmount events to the parent namespace */
if (mount ("oldroot", "oldroot", NULL, MS_SILENT | MS_REC | MS_PRIVATE, NULL) != 0)
die_with_error ("Failed to make old root rprivate");
die_with_mount_error ("Failed to make old root rprivate");

if (umount2 ("oldroot", MNT_DETACH))
die_with_error ("unmount old root");
Expand Down
36 changes: 36 additions & 0 deletions utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ die_with_error (const char *format, ...)
exit (1);
}

void
die_with_mount_error (const char *format, ...)
{
va_list args;
int errsv;

errsv = errno;

va_start (args, format);
warnv (format, args, mount_strerror (errsv));
va_end (args);

exit (1);
}

void
die (const char *format, ...)
{
Expand Down Expand Up @@ -893,3 +908,24 @@ label_exec (UNUSED const char *exec_label)
#endif
return 0;
}

/*
* Like strerror(), but specialized for a failed mount(2) call.
*/
const char *
mount_strerror (int errsv)
{
switch (errsv)
{
case ENOSPC:
/* "No space left on device" misleads users into thinking there
* is some sort of disk-space problem, but mount(2) uses that
* errno value to mean something more like "limit exceeded". */
return ("Limit exceeded (ENOSPC). "
"(Hint: Check that /proc/sys/fs/mount-max is sufficient, "
"typically 100000)");

default:
return strerror (errsv);
}
}
4 changes: 4 additions & 0 deletions utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ void warn (const char *format,
...) __attribute__((format (printf, 1, 2)));
void die_with_error (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die_with_mount_error (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die_oom (void) __attribute__((__noreturn__));
Expand Down Expand Up @@ -134,6 +136,8 @@ char *label_mount (const char *opt,
int label_exec (const char *exec_label);
int label_create_file (const char *file_label);

const char *mount_strerror (int errsv);

static inline void
cleanup_freep (void *p)
{
Expand Down

0 comments on commit 68a0330

Please sign in to comment.