Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ name: CI checks
on:
push:
branches:
- main
- main
pull_request:
branches:
- main
- main

jobs:
meson:
name: Build with Meson and gcc, and test
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install build-dependencies
run: sudo ./ci/builddeps.sh
- name: Enable user namespaces
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
test ! -e DESTDIR-as-subproject/usr/local/libexec/bwrap
tests/use-as-subproject/assert-correct-rpath.py DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
- name: Upload test logs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: failure() || cancelled()
with:
name: test logs
Expand All @@ -87,11 +87,11 @@ jobs:
- cpp
steps:
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Check out
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install build-dependencies
run: sudo ./ci/builddeps.sh --clang
- run: meson build -Dselinux=enabled
Expand All @@ -102,4 +102,4 @@ jobs:
-Werror=unused-variable
- run: meson compile -C build
- name: CodeQL analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v4
135 changes: 80 additions & 55 deletions bind-mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -414,72 +414,92 @@ bind_mount (int proc_fd,
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.
* This is due to the fact that the kernel, as of the beginning of 2021,
* populates mountinfo with whatever case combination first appeared in the
* dcache; kernel developers plan to change this in future so that it
* reflects the on-disk encoding instead.
* To avoid throwing an error when this happens, we use readlink() result
* instead of the provided @root_mount, so that we can compare the mountinfo
* entries with the same case combination that the kernel is expected to
* use. */
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd);
oldroot_dest_proc = get_oldroot_path (dest_proc);
kernel_case_combination = readlink_malloc (oldroot_dest_proc);
if (kernel_case_combination == NULL)
struct mount_attr attr = {
.attr_clr = 0,
.attr_set = MOUNT_ATTR_NOSUID | (devices ? 0 : MOUNT_ATTR_NODEV) |
(readonly ? MOUNT_ATTR_RDONLY : 0),
};
if (mount_setattr_wrapper (dest_fd, "",
AT_EMPTY_PATH | (recursive ? AT_RECURSIVE : 0), &attr,
sizeof(attr)) == -1)
{
if (failing_path != NULL)
*failing_path = steal_pointer (&resolved_dest);

return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
}
if (errno != ENOSYS)
{
if (failing_path != NULL)
*failing_path = steal_pointer (&resolved_dest);

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);
return BIND_MOUNT_ERROR_MOUNT_SETATTR;
}
else
{
/* If we are in a case-insensitive filesystem, mountinfo might contain a
* different case combination of the path we requested to mount.
* This is due to the fact that the kernel, as of the beginning of 2021,
* populates mountinfo with whatever case combination first appeared in the
* dcache; kernel developers plan to change this in future so that it
* reflects the on-disk encoding instead.
* To avoid throwing an error when this happens, we use readlink() result
* instead of the provided @root_mount, so that we can compare the mountinfo
* entries with the same case combination that the kernel is expected to
* use. */
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd);
oldroot_dest_proc = get_oldroot_path (dest_proc);
kernel_case_combination = readlink_malloc (oldroot_dest_proc);
if (kernel_case_combination == NULL)
{
if (failing_path != NULL)
*failing_path = steal_pointer (&resolved_dest);

errno = EINVAL;
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT;
}
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
}

assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
current_flags = mount_tab[0].options;
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
if (new_flags != current_flags &&
mount ("none", resolved_dest,
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
{
if (failing_path != NULL)
*failing_path = steal_pointer (&resolved_dest);
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);

return BIND_MOUNT_ERROR_REMOUNT_DEST;
}
errno = EINVAL;
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT;
}

/* 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.
* Note: This does not apply the flags to mounts which are later propagated into this namespace.
*/
if (recursive)
{
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
{
current_flags = mount_tab[i].options;
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
current_flags = mount_tab[0].options;
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
if (new_flags != current_flags &&
mount ("none", mount_tab[i].mountpoint,
mount ("none", resolved_dest,
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
{
/* 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)
{
if (failing_path != NULL)
*failing_path = xstrdup (mount_tab[i].mountpoint);
if (failing_path != NULL)
*failing_path = steal_pointer (&resolved_dest);

return BIND_MOUNT_ERROR_REMOUNT_DEST;
}

return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
/* 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.
* Note: This does not apply the flags to mounts which are later propagated into this namespace.
*/
if (recursive)
{
for (i = 1; mount_tab[i].mountpoint != NULL; i++)
{
current_flags = mount_tab[i].options;
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
if (new_flags != current_flags &&
mount ("none", mount_tab[i].mountpoint,
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
{
/* 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)
{
if (failing_path != NULL)
*failing_path = xstrdup (mount_tab[i].mountpoint);

return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
}
}
}
}
}
Expand Down Expand Up @@ -534,6 +554,10 @@ bind_mount_result_to_string (bind_mount_result res,
failing_path);
break;

case BIND_MOUNT_ERROR_MOUNT_SETATTR:
string = xasprintf("mount_setattr() failed at \"%s\"", failing_path);
break;

case BIND_MOUNT_SUCCESS:
string = xstrdup ("Success");
break;
Expand Down Expand Up @@ -587,6 +611,7 @@ die_with_bind_result (bind_mount_result res,
case BIND_MOUNT_ERROR_REOPEN_DEST:
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
case BIND_MOUNT_ERROR_MOUNT_SETATTR:
case BIND_MOUNT_SUCCESS:
default:
fprintf (stderr, ": %s", strerror (saved_errno));
Expand Down
1 change: 1 addition & 0 deletions bind-mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef enum
BIND_MOUNT_ERROR_FIND_DEST_MOUNT,
BIND_MOUNT_ERROR_REMOUNT_DEST,
BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT,
BIND_MOUNT_ERROR_MOUNT_SETATTR,
} bind_mount_result;

bind_mount_result bind_mount (int proc_fd,
Expand Down
37 changes: 37 additions & 0 deletions utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,40 @@ void strappendf (StringBuilder *dest,
...);
void strappend_escape_for_mount_options (StringBuilder *dest,
const char *src);

#ifndef __NR_mount_setattr
#define __NR_mount_setattr 442
#endif

#ifndef MOUNT_ATTR_RDONLY
struct mount_attr
{
__u64 attr_set;
__u64 attr_clr;
__u64 propagation;
__u64 userns_fd;
};

#define MOUNT_ATTR_RDONLY 0x00000001
#define MOUNT_ATTR_NOSUID 0x00000002
#define MOUNT_ATTR_NODEV 0x00000004
#define MOUNT_ATTR_NOEXEC 0x00000008
#define MOUNT_ATTR__ATIME 0x00000070
#define MOUNT_ATTR_RELATIME 0x00000000
#define MOUNT_ATTR_NOATIME 0x00000010
#define MOUNT_ATTR_STRICTATIME 0x00000020
#define MOUNT_ATTR_NODIRATIME 0x00000080
#define MOUNT_ATTR_IDMAP 0x00100000
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000
#endif

#ifndef AT_RECURSIVE
#define AT_RECURSIVE 0x8000
#endif

static inline int
mount_setattr_wrapper (int dirfd, const char *path, unsigned int flags,
struct mount_attr *attr, size_t size)
{
return syscall (__NR_mount_setattr, dirfd, path, flags, attr, size);
}