From f65b3eca0743278ae7154db3c5b6f6fbe383b899 Mon Sep 17 00:00:00 2001 From: xxyzz Date: Sat, 30 May 2026 17:41:36 +0800 Subject: [PATCH] Use `mount_setattr()` in `bind_mount()` Signed-off-by: xxyzz --- .github/workflows/check.yml | 14 ++-- bind-mount.c | 135 +++++++++++++++++++++--------------- bind-mount.h | 1 + utils.h | 37 ++++++++++ 4 files changed, 125 insertions(+), 62 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 8a747d52c..d606d9820 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -3,10 +3,10 @@ name: CI checks on: push: branches: - - main + - main pull_request: branches: - - main + - main jobs: meson: @@ -14,7 +14,7 @@ jobs: 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 @@ -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 @@ -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 @@ -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 diff --git a/bind-mount.c b/bind-mount.c index a2e1ac6c4..5fc4f8eda 100644 --- a/bind-mount.c +++ b/bind-mount.c @@ -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; + } + } } } } @@ -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; @@ -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)); diff --git a/bind-mount.h b/bind-mount.h index 8a361fbd5..966812d10 100644 --- a/bind-mount.h +++ b/bind-mount.h @@ -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, diff --git a/utils.h b/utils.h index 079fe7c93..713a071fd 100644 --- a/utils.h +++ b/utils.h @@ -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); +}