From f3b81859c28273cec06286f8ea0d29bda575525b Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 23 Jan 2026 19:15:15 +0100 Subject: [PATCH 1/5] namespace-util: Fix logging in userns_enter_and_pin() --- src/basic/namespace-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 555138c255..c3b84ea784 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -618,7 +618,7 @@ int userns_enter_and_pin(int userns_fd, PidRef *ret) { "(sd-pinuserns)", /* stdio_fds= */ NULL, (int[]) { pfd[1], userns_fd }, 2, - FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL, + FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_DEATHSIG_SIGKILL, &pidref); if (r < 0) return r; From 4de6855171b6d33b2116ad5e48f23bcd36c9e439 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 25 Jan 2026 09:33:00 +0100 Subject: [PATCH 2/5] test-uid-range: Migrate to new assertion macros --- src/test/test-uid-range.c | 236 +++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c index f45ae6b50a..69c39b0575 100644 --- a/src/test/test-uid-range.c +++ b/src/test/test-uid-range.c @@ -15,104 +15,104 @@ TEST(uid_range) { _cleanup_(uid_range_freep) UIDRange *p = NULL; uid_t search; - assert_se(uid_range_covers(p, 0, 0)); - assert_se(!uid_range_covers(p, 0, 1)); - assert_se(!uid_range_covers(p, 100, UINT32_MAX)); - assert_se(!uid_range_covers(p, UINT32_MAX, 1)); - assert_se(!uid_range_covers(p, UINT32_MAX - 10, 11)); + ASSERT_TRUE(uid_range_covers(p, 0, 0)); + ASSERT_FALSE(uid_range_covers(p, 0, 1)); + ASSERT_FALSE(uid_range_covers(p, 100, UINT32_MAX)); + ASSERT_FALSE(uid_range_covers(p, UINT32_MAX, 1)); + ASSERT_FALSE(uid_range_covers(p, UINT32_MAX - 10, 11)); - assert_se(uid_range_entries(p) == 0); - assert_se(uid_range_size(p) == 0); - assert_se(uid_range_is_empty(p)); + ASSERT_EQ(uid_range_entries(p), 0U); + ASSERT_EQ(uid_range_size(p), 0U); + ASSERT_TRUE(uid_range_is_empty(p)); - assert_se(uid_range_add_str(&p, "500-999") >= 0); - assert_se(p); - assert_se(uid_range_entries(p) == 1); - assert_se(uid_range_size(p) == 500); - assert_se(!uid_range_is_empty(p)); - assert_se(p->entries[0].start == 500); - assert_se(p->entries[0].nr == 500); + ASSERT_OK(uid_range_add_str(&p, "500-999")); + ASSERT_NOT_NULL(p); + ASSERT_EQ(uid_range_entries(p), 1U); + ASSERT_EQ(uid_range_size(p), 500U); + ASSERT_FALSE(uid_range_is_empty(p)); + ASSERT_EQ(p->entries[0].start, 500U); + ASSERT_EQ(p->entries[0].nr, 500U); - assert_se(!uid_range_contains(p, 499)); - assert_se(uid_range_contains(p, 500)); - assert_se(uid_range_contains(p, 999)); - assert_se(!uid_range_contains(p, 1000)); + ASSERT_FALSE(uid_range_contains(p, 499)); + ASSERT_TRUE(uid_range_contains(p, 500)); + ASSERT_TRUE(uid_range_contains(p, 999)); + ASSERT_FALSE(uid_range_contains(p, 1000)); - assert_se(!uid_range_covers(p, 100, 150)); - assert_se(!uid_range_covers(p, 400, 200)); - assert_se(!uid_range_covers(p, 499, 1)); - assert_se(uid_range_covers(p, 500, 1)); - assert_se(uid_range_covers(p, 501, 10)); - assert_se(uid_range_covers(p, 999, 1)); - assert_se(!uid_range_covers(p, 999, 2)); - assert_se(!uid_range_covers(p, 1000, 1)); - assert_se(!uid_range_covers(p, 1000, 100)); - assert_se(!uid_range_covers(p, 1001, 100)); + ASSERT_FALSE(uid_range_covers(p, 100, 150)); + ASSERT_FALSE(uid_range_covers(p, 400, 200)); + ASSERT_FALSE(uid_range_covers(p, 499, 1)); + ASSERT_TRUE(uid_range_covers(p, 500, 1)); + ASSERT_TRUE(uid_range_covers(p, 501, 10)); + ASSERT_TRUE(uid_range_covers(p, 999, 1)); + ASSERT_FALSE(uid_range_covers(p, 999, 2)); + ASSERT_FALSE(uid_range_covers(p, 1000, 1)); + ASSERT_FALSE(uid_range_covers(p, 1000, 100)); + ASSERT_FALSE(uid_range_covers(p, 1001, 100)); search = UID_INVALID; - assert_se(uid_range_next_lower(p, &search)); - assert_se(search == 999); - assert_se(uid_range_next_lower(p, &search)); - assert_se(search == 998); + ASSERT_OK_POSITIVE(uid_range_next_lower(p, &search)); + ASSERT_EQ(search, 999U); + ASSERT_OK_POSITIVE(uid_range_next_lower(p, &search)); + ASSERT_EQ(search, 998U); search = 501; - assert_se(uid_range_next_lower(p, &search)); - assert_se(search == 500); - assert_se(uid_range_next_lower(p, &search) == -EBUSY); + ASSERT_OK_POSITIVE(uid_range_next_lower(p, &search)); + ASSERT_EQ(search, 500U); + ASSERT_ERROR(uid_range_next_lower(p, &search), EBUSY); - assert_se(uid_range_add_str(&p, "1000") >= 0); - assert_se(uid_range_entries(p) == 1); - assert_se(p->entries[0].start == 500); - assert_se(p->entries[0].nr == 501); + ASSERT_OK(uid_range_add_str(&p, "1000")); + ASSERT_EQ(uid_range_entries(p), 1U); + ASSERT_EQ(p->entries[0].start, 500U); + ASSERT_EQ(p->entries[0].nr, 501U); - assert_se(uid_range_add_str(&p, "30-40") >= 0); - assert_se(uid_range_entries(p) == 2); - assert_se(uid_range_size(p) == 500 + 1 + 11); - assert_se(!uid_range_is_empty(p)); - assert_se(p->entries[0].start == 30); - assert_se(p->entries[0].nr == 11); - assert_se(p->entries[1].start == 500); - assert_se(p->entries[1].nr == 501); + ASSERT_OK(uid_range_add_str(&p, "30-40")); + ASSERT_EQ(uid_range_entries(p), 2U); + ASSERT_EQ(uid_range_size(p), 500U + 1U + 11U); + ASSERT_FALSE(uid_range_is_empty(p)); + ASSERT_EQ(p->entries[0].start, 30U); + ASSERT_EQ(p->entries[0].nr, 11U); + ASSERT_EQ(p->entries[1].start , 500U); + ASSERT_EQ(p->entries[1].nr , 501U); - assert_se(uid_range_add_str(&p, "60-70") >= 0); - assert_se(uid_range_entries(p) == 3); - assert_se(uid_range_size(p) == 500 + 1 + 11 + 11); - assert_se(!uid_range_is_empty(p)); - assert_se(p->entries[0].start == 30); - assert_se(p->entries[0].nr == 11); - assert_se(p->entries[1].start == 60); - assert_se(p->entries[1].nr == 11); - assert_se(p->entries[2].start == 500); - assert_se(p->entries[2].nr == 501); + ASSERT_OK(uid_range_add_str(&p, "60-70")); + ASSERT_EQ(uid_range_entries(p), 3U); + ASSERT_EQ(uid_range_size(p), 500U + 1U + 11U + 11U); + ASSERT_FALSE(uid_range_is_empty(p)); + ASSERT_EQ(p->entries[0].start, 30U); + ASSERT_EQ(p->entries[0].nr, 11U); + ASSERT_EQ(p->entries[1].start, 60U); + ASSERT_EQ(p->entries[1].nr, 11U); + ASSERT_EQ(p->entries[2].start, 500U); + ASSERT_EQ(p->entries[2].nr, 501U); - assert_se(uid_range_add_str(&p, "20-2000") >= 0); - assert_se(uid_range_entries(p) == 1); - assert_se(uid_range_size(p) == 1981); - assert_se(p->entries[0].start == 20); - assert_se(p->entries[0].nr == 1981); + ASSERT_OK(uid_range_add_str(&p, "20-2000")); + ASSERT_EQ(uid_range_entries(p), 1U); + ASSERT_EQ(uid_range_size(p), 1981U); + ASSERT_EQ(p->entries[0].start, 20U); + ASSERT_EQ(p->entries[0].nr, 1981U); - assert_se(uid_range_add_str(&p, "2002") >= 0); - assert_se(uid_range_entries(p) == 2); - assert_se(uid_range_size(p) == 1982); - assert_se(p->entries[0].start == 20); - assert_se(p->entries[0].nr == 1981); - assert_se(p->entries[1].start == 2002); - assert_se(p->entries[1].nr == 1); + ASSERT_OK(uid_range_add_str(&p, "2002")); + ASSERT_EQ(uid_range_entries(p), 2U); + ASSERT_EQ(uid_range_size(p), 1982U); + ASSERT_EQ(p->entries[0].start, 20U); + ASSERT_EQ(p->entries[0].nr, 1981U); + ASSERT_EQ(p->entries[1].start, 2002U); + ASSERT_EQ(p->entries[1].nr, 1U); _cleanup_(uid_range_freep) UIDRange *q = NULL; - assert_se(!uid_range_equal(p, q)); - assert_se(uid_range_add_str(&q, "20-2000") >= 0); - assert_se(!uid_range_equal(p, q)); - assert_se(uid_range_add_str(&q, "2002") >= 0); - assert_se(uid_range_equal(p, q)); + ASSERT_FALSE(uid_range_equal(p, q)); + ASSERT_OK(uid_range_add_str(&q, "20-2000")); + ASSERT_FALSE(uid_range_equal(p, q)); + ASSERT_OK(uid_range_add_str(&q, "2002")); + ASSERT_TRUE(uid_range_equal(p, q)); - assert_se(uid_range_add_str(&p, "2001") >= 0); - assert_se(uid_range_entries(p) == 1); - assert_se(uid_range_size(p) == 1983); - assert_se(p->entries[0].start == 20); - assert_se(p->entries[0].nr == 1983); + ASSERT_OK(uid_range_add_str(&p, "2001")); + ASSERT_EQ(uid_range_entries(p), 1U); + ASSERT_EQ(uid_range_size(p), 1983U); + ASSERT_EQ(p->entries[0].start, 20U); + ASSERT_EQ(p->entries[0].nr, 1983U); - assert_se(uid_range_add_str(&q, "2001") >= 0); - assert_se(uid_range_equal(p, q)); + ASSERT_OK(uid_range_add_str(&q, "2001")); + ASSERT_TRUE(uid_range_equal(p, q)); } TEST(load_userns) { @@ -125,74 +125,74 @@ TEST(load_userns) { if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return; - assert_se(r >= 0); - assert_se(uid_range_contains(p, getuid())); + ASSERT_OK(r); + ASSERT_TRUE(uid_range_contains(p, getuid())); r = running_in_userns(); if (r == 0) { - assert_se(p->n_entries == 1); - assert_se(p->entries[0].start == 0); - assert_se(p->entries[0].nr == UINT32_MAX); + ASSERT_EQ(p->n_entries, 1U); + ASSERT_EQ(p->entries[0].start, 0U); + ASSERT_EQ(p->entries[0].nr, UINT32_MAX); - assert_se(uid_range_covers(p, 0, UINT32_MAX)); + ASSERT_TRUE(uid_range_covers(p, 0, UINT32_MAX)); } - assert_se(fopen_temporary_child(NULL, &f, &fn) >= 0); + ASSERT_OK(fopen_temporary_child(NULL, &f, &fn)); fputs("0 0 20\n" "100 0 20\n", f); - assert_se(fflush_and_check(f) >= 0); + ASSERT_OK(fflush_and_check(f)); p = uid_range_free(p); - assert_se(uid_range_load_userns(fn, UID_RANGE_USERNS_INSIDE, &p) >= 0); + ASSERT_OK(uid_range_load_userns(fn, UID_RANGE_USERNS_INSIDE, &p)); - assert_se(uid_range_contains(p, 0)); - assert_se(uid_range_contains(p, 19)); - assert_se(!uid_range_contains(p, 20)); + ASSERT_TRUE(uid_range_contains(p, 0)); + ASSERT_TRUE(uid_range_contains(p, 19)); + ASSERT_FALSE(uid_range_contains(p, 20)); - assert_se(!uid_range_contains(p, 99)); - assert_se(uid_range_contains(p, 100)); - assert_se(uid_range_contains(p, 119)); - assert_se(!uid_range_contains(p, 120)); + ASSERT_FALSE(uid_range_contains(p, 99)); + ASSERT_TRUE(uid_range_contains(p, 100)); + ASSERT_TRUE(uid_range_contains(p, 119)); + ASSERT_FALSE(uid_range_contains(p, 120)); } TEST(uid_range_coalesce) { _cleanup_(uid_range_freep) UIDRange *p = NULL; for (size_t i = 0; i < 10; i++) { - assert_se(uid_range_add_internal(&p, i * 10, 10, /* coalesce= */ false) >= 0); - assert_se(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce= */ false) >= 0); + ASSERT_OK(uid_range_add_internal(&p, i * 10, 10, /* coalesce= */ false)); + ASSERT_OK(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce= */ false)); } - assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true) >= 0); - assert_se(p->n_entries == 1); - assert_se(p->entries[0].start == 0); - assert_se(p->entries[0].nr == 105); + ASSERT_OK(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true)); + ASSERT_EQ(p->n_entries, 1U); + ASSERT_EQ(p->entries[0].start, 0U); + ASSERT_EQ(p->entries[0].nr, 105U); p = uid_range_free(p); for (size_t i = 0; i < 10; i++) { - assert_se(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce= */ false) >= 0); - assert_se(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce= */ false) >= 0); + ASSERT_OK(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce= */ false)); + ASSERT_OK(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce= */ false)); } - assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true) >= 0); - assert_se(p->n_entries == 1); - assert_se(p->entries[0].start == 10); - assert_se(p->entries[0].nr == 105); + ASSERT_OK(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true)); + ASSERT_EQ(p->n_entries, 1U); + ASSERT_EQ(p->entries[0].start, 10U); + ASSERT_EQ(p->entries[0].nr, 105U); p = uid_range_free(p); for (size_t i = 0; i < 10; i++) { - assert_se(uid_range_add_internal(&p, i * 10, 10, /* coalesce= */ false) >= 0); - assert_se(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce= */ false) >= 0); - assert_se(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce= */ false) >= 0); - assert_se(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce= */ false) >= 0); + ASSERT_OK(uid_range_add_internal(&p, i * 10, 10, /* coalesce= */ false)); + ASSERT_OK(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce= */ false)); + ASSERT_OK(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce= */ false)); + ASSERT_OK(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce= */ false)); } - assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true) >= 0); - assert_se(p->n_entries == 1); - assert_se(p->entries[0].start == 0); - assert_se(p->entries[0].nr == 115); + ASSERT_OK(uid_range_add_internal(&p, 100, 1, /* coalesce= */ true)); + ASSERT_EQ(p->n_entries, 1U); + ASSERT_EQ(p->entries[0].start, 0U); + ASSERT_EQ(p->entries[0].nr, 115U); } DEFINE_TEST_MAIN(LOG_DEBUG); From b4bfacf4960665ddc9db00f04090615a946cb860 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sat, 24 Jan 2026 20:52:14 +0100 Subject: [PATCH 3/5] mountfsd: Always open_tree() in mount namespace of peer open_tree() will fail with EINVAL when passed a directory file descriptor that comes from another mount namespace. While this should be fixed in a future kernel, let's workaround the issue for now by entering the mount namespace of the peer if needed and calling open_tree() there and then passing the fd back to the mountfsd process. --- src/mountfsd/mountwork.c | 102 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c index 7fbcb2247e..d55edb3407 100644 --- a/src/mountfsd/mountwork.c +++ b/src/mountfsd/mountwork.c @@ -36,6 +36,8 @@ #include "os-util.h" #include "path-util.h" #include "pidref.h" +#include "process-util.h" +#include "socket-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" @@ -878,6 +880,99 @@ static DirectoryOwnership validate_directory_fd( return DIRECTORY_IS_OTHERWISE_OWNED; } +static int open_tree_try_drop_idmap_harder(sd_varlink *link, int directory_fd, const char *directory_path) { + int r; + + _cleanup_close_ int mount_fd = open_tree_try_drop_idmap( + directory_fd, + "", + OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH); + if (mount_fd >= 0) + return TAKE_FD(mount_fd); + if (mount_fd != -EINVAL) + return log_debug_errno(mount_fd, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path)); + + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + r = varlink_get_peer_pidref(link, &pidref); + if (r < 0) + return r; + + _cleanup_close_ int mntns_fd = pidref_namespace_open_by_type(&pidref, NAMESPACE_MOUNT); + if (mntns_fd < 0) + return log_debug_errno(mntns_fd, "Failed to open mount namespace of peer: %m"); + + r = is_our_namespace(mntns_fd, NAMESPACE_MOUNT); + if (r < 0) + return log_debug_errno(r, "Failed to check if peer is in same mount namespace: %m"); + if (r > 0) + return log_debug_errno(mount_fd, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path)); + + /* The peer is in a different mount namespace. open_tree() will fail with EINVAL on directory fds + * from a different mount namespace, so we need to fork off a child process that joins the peer's + * mount namespace and calls open_tree() there. */ + + _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, mount_fd_socket[2] = EBADF_PAIR; + + if (pipe2(errno_pipe_fd, O_CLOEXEC) < 0) + return log_debug_errno(errno, "Failed to create pipe: %m"); + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, mount_fd_socket) < 0) + return log_debug_errno(errno, "Failed to create socket pair: %m"); + + _cleanup_(pidref_done) PidRef child = PIDREF_NULL; + r = namespace_fork( + "(sd-opentreens)", + "(sd-opentree)", + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, + /* pidns_fd= */ -EBADF, + mntns_fd, + /* netns_fd= */ -EBADF, + /* userns_fd= */ -EBADF, + /* root_fd= */ -EBADF, + &child); + if (r < 0) + return log_debug_errno(r, "Failed to fork into peer's mount namespace: %m"); + if (r == 0) { + /* Child */ + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + mount_fd_socket[0] = safe_close(mount_fd_socket[0]); + + mount_fd = open_tree_try_drop_idmap( + directory_fd, + "", + OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH); + if (mount_fd < 0) { + log_debug_errno(mount_fd, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path)); + report_errno_and_exit(errno_pipe_fd[1], mount_fd); + } + + r = send_one_fd(mount_fd_socket[1], mount_fd, /* flags= */ 0); + if (r < 0) { + log_debug_errno(r, "Failed to send mount fd: %m"); + report_errno_and_exit(errno_pipe_fd[1], r); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + mount_fd_socket[1] = safe_close(mount_fd_socket[1]); + + r = pidref_wait_for_terminate_and_check("(sd-opentreens)", &child, /* flags= */ 0); + if (r < 0) + return log_debug_errno(r, "Failed to wait for child: %m"); + + r = read_errno(errno_pipe_fd[0]); + if (r < 0) + return r; + + mount_fd = receive_one_fd(mount_fd_socket[0], MSG_DONTWAIT); + if (mount_fd < 0) + return log_debug_errno(mount_fd, "Failed to receive mount fd from child: %m"); + + return TAKE_FD(mount_fd); +} + static int vl_method_mount_directory( sd_varlink *link, sd_json_variant *parameters, @@ -992,12 +1087,9 @@ static int vl_method_mount_directory( if (r < 0) return r; - _cleanup_close_ int mount_fd = open_tree_try_drop_idmap( - directory_fd, - "", - OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH); + _cleanup_close_ int mount_fd = open_tree_try_drop_idmap_harder(link, directory_fd, directory_path); if (mount_fd < 0) - return log_debug_errno(errno, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path)); + return mount_fd; /* MOUNT_ATTR_IDMAP has possibly been cleared. Let's verify that the underlying data matches our expectations. */ struct stat unmapped_st; From d1ae0e52056a9c0ed8b33e09de97252473a8f6cc Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Sun, 25 Jan 2026 21:21:22 +0100 Subject: [PATCH 4/5] nsresourced: Fix typo --- src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c index f327e9004b..f022f96706 100644 --- a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c +++ b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c @@ -31,7 +31,7 @@ void* bpf_rdonly_cast(const void *, __u32) __ksym; * by their inode number in nsfs) that restricts creation of inodes (which would inherit the callers UID/GID) * or changing of ownership (similar). * - * This hooks into the various path-based LSM entrypoints that control inode creation as well as chmod(), and + * This hooks into the various path-based LSM entrypoints that control inode creation as well as chown(), and * then looks up the calling process' user namespace in a global map of namespaces, which points us to * another map that is simply a list of allowed mnt_ids. */ From 14054134c6b1e6e27e2a43cbe7aaaf1acc2bddca Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 26 Jan 2026 21:39:34 +0100 Subject: [PATCH 5/5] nsresourced: Add comment about mknod() hook covering regular files --- src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c index f022f96706..eb94659f3d 100644 --- a/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c +++ b/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c @@ -140,6 +140,8 @@ int BPF_PROG(userns_restrict_path_mkdir, struct path *dir, struct dentry *dentry return validate_path(dir, ret); } +/* The mknod hook covers all file creations, including regular files, in case the reader is looking for a + * missing hook for open(). */ SEC("lsm/path_mknod") int BPF_PROG(userns_restrict_path_mknod, const struct path *dir, struct dentry *dentry, umode_t mode, unsigned dev, int ret) { return validate_path(dir, ret);