Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge patch series "tighten nstree visibility checks"

Christian Brauner <brauner@kernel.org> says:

Listing various namespaces is currently only scoped on owning namespace.
We can make this more fine-grained so that we scope visibility even
tighter. To make it possible to change behavior restrict visibility for
now. This shouldn't be a big deal as there aren't actual large users out
there and paves the way to make this even cleaner in the future.

* patches from https://patch.msgid.link/20260226-work-visibility-fixes-v1-0-d2c2853313bd@kernel.org:
selftests: fix mntns iteration selftests
nstree: tighten permission checks for listing
nsfs: tighten permission checks for handle opening
nsfs: tighten permission checks for ns iteration ioctls

Link: https://patch.msgid.link/20260226-work-visibility-fixes-v1-0-d2c2853313bd@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>

+41 -36
+14 -1
fs/nsfs.c
··· 199 199 return false; 200 200 } 201 201 202 + static bool may_use_nsfs_ioctl(unsigned int cmd) 203 + { 204 + switch (_IOC_NR(cmd)) { 205 + case _IOC_NR(NS_MNT_GET_NEXT): 206 + fallthrough; 207 + case _IOC_NR(NS_MNT_GET_PREV): 208 + return may_see_all_namespaces(); 209 + } 210 + return true; 211 + } 212 + 202 213 static long ns_ioctl(struct file *filp, unsigned int ioctl, 203 214 unsigned long arg) 204 215 { ··· 225 214 226 215 if (!nsfs_ioctl_valid(ioctl)) 227 216 return -ENOIOCTLCMD; 217 + if (!may_use_nsfs_ioctl(ioctl)) 218 + return -EPERM; 228 219 229 220 ns = get_proc_ns(file_inode(filp)); 230 221 switch (ioctl) { ··· 627 614 return ERR_PTR(-EOPNOTSUPP); 628 615 } 629 616 630 - if (owning_ns && !ns_capable(owning_ns, CAP_SYS_ADMIN)) { 617 + if (owning_ns && !may_see_all_namespaces()) { 631 618 ns->ops->put(ns); 632 619 return ERR_PTR(-EPERM); 633 620 }
+2
include/linux/ns_common.h
··· 55 55 56 56 #define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns))) 57 57 58 + bool may_see_all_namespaces(void); 59 + 58 60 static __always_inline __must_check int __ns_ref_active_read(const struct ns_common *ns) 59 61 { 60 62 return atomic_read(&ns->__ns_ref_active);
+6
kernel/nscommon.c
··· 309 309 return; 310 310 } 311 311 } 312 + 313 + bool may_see_all_namespaces(void) 314 + { 315 + return (task_active_pid_ns(current) == &init_pid_ns) && 316 + ns_capable_noaudit(init_pid_ns.user_ns, CAP_SYS_ADMIN); 317 + }
+4 -25
kernel/nstree.c
··· 515 515 static inline bool __must_check may_list_ns(const struct klistns *kls, 516 516 struct ns_common *ns) 517 517 { 518 - if (kls->user_ns) { 519 - if (kls->userns_capable) 520 - return true; 521 - } else { 522 - struct ns_common *owner; 523 - struct user_namespace *user_ns; 524 - 525 - owner = ns_owner(ns); 526 - if (owner) 527 - user_ns = to_user_ns(owner); 528 - else 529 - user_ns = &init_user_ns; 530 - if (ns_capable_noaudit(user_ns, CAP_SYS_ADMIN)) 531 - return true; 532 - } 533 - 518 + if (kls->user_ns && kls->userns_capable) 519 + return true; 534 520 if (is_current_namespace(ns)) 535 521 return true; 536 - 537 - if (ns->ns_type != CLONE_NEWUSER) 538 - return false; 539 - 540 - if (ns_capable_noaudit(to_user_ns(ns), CAP_SYS_ADMIN)) 541 - return true; 542 - 543 - return false; 522 + return may_see_all_namespaces(); 544 523 } 545 524 546 525 static inline void ns_put(struct ns_common *ns) ··· 579 600 580 601 ret = 0; 581 602 head = &to_ns_common(kls->user_ns)->ns_owner_root.ns_list_head; 582 - kls->userns_capable = ns_capable_noaudit(kls->user_ns, CAP_SYS_ADMIN); 603 + kls->userns_capable = may_see_all_namespaces(); 583 604 584 605 rcu_read_lock(); 585 606
+15 -10
tools/testing/selftests/filesystems/nsfs/iterate_mntns.c
··· 37 37 __u64 mnt_ns_id[MNT_NS_COUNT]; 38 38 }; 39 39 40 + static inline bool mntns_in_list(__u64 *mnt_ns_id, struct mnt_ns_info *info) 41 + { 42 + for (int i = 0; i < MNT_NS_COUNT; i++) { 43 + if (mnt_ns_id[i] == info->mnt_ns_id) 44 + return true; 45 + } 46 + return false; 47 + } 48 + 40 49 FIXTURE_SETUP(iterate_mount_namespaces) 41 50 { 42 51 for (int i = 0; i < MNT_NS_COUNT; i++) 43 52 self->fd_mnt_ns[i] = -EBADF; 44 - 45 - /* 46 - * Creating a new user namespace let's us guarantee that we only see 47 - * mount namespaces that we did actually create. 48 - */ 49 - ASSERT_EQ(unshare(CLONE_NEWUSER), 0); 50 53 51 54 for (int i = 0; i < MNT_NS_COUNT; i++) { 52 55 struct mnt_ns_info info = {}; ··· 78 75 fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[0], F_DUPFD_CLOEXEC); 79 76 ASSERT_GE(fd_mnt_ns_cur, 0); 80 77 81 - for (;; count++) { 78 + for (;;) { 82 79 struct mnt_ns_info info = {}; 83 80 int fd_mnt_ns_next; 84 81 85 82 fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info); 86 83 if (fd_mnt_ns_next < 0 && errno == ENOENT) 87 84 break; 85 + if (mntns_in_list(self->mnt_ns_id, &info)) 86 + count++; 88 87 ASSERT_GE(fd_mnt_ns_next, 0); 89 88 ASSERT_EQ(close(fd_mnt_ns_cur), 0); 90 89 fd_mnt_ns_cur = fd_mnt_ns_next; ··· 101 96 fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[MNT_NS_LAST_INDEX], F_DUPFD_CLOEXEC); 102 97 ASSERT_GE(fd_mnt_ns_cur, 0); 103 98 104 - for (;; count++) { 99 + for (;;) { 105 100 struct mnt_ns_info info = {}; 106 101 int fd_mnt_ns_prev; 107 102 108 103 fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info); 109 104 if (fd_mnt_ns_prev < 0 && errno == ENOENT) 110 105 break; 106 + if (mntns_in_list(self->mnt_ns_id, &info)) 107 + count++; 111 108 ASSERT_GE(fd_mnt_ns_prev, 0); 112 109 ASSERT_EQ(close(fd_mnt_ns_cur), 0); 113 110 fd_mnt_ns_cur = fd_mnt_ns_prev; ··· 132 125 ASSERT_GE(fd_mnt_ns_next, 0); 133 126 ASSERT_EQ(close(fd_mnt_ns_cur), 0); 134 127 fd_mnt_ns_cur = fd_mnt_ns_next; 135 - ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]); 136 128 } 137 129 } 138 130 ··· 150 144 ASSERT_GE(fd_mnt_ns_prev, 0); 151 145 ASSERT_EQ(close(fd_mnt_ns_cur), 0); 152 146 fd_mnt_ns_cur = fd_mnt_ns_prev; 153 - ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]); 154 147 } 155 148 } 156 149