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

selftests: add pidfd bind-mount tests

Link: https://lore.kernel.org/r/20241219-work-pidfs-mount-v1-2-dbc56198b839@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>

+190 -1
+1
tools/testing/selftests/pidfd/.gitignore
··· 7 7 pidfd_getfd_test 8 8 pidfd_setns_test 9 9 pidfd_file_handle_test 10 + pidfd_bind_mount
+1 -1
tools/testing/selftests/pidfd/Makefile
··· 3 3 4 4 TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \ 5 5 pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \ 6 - pidfd_file_handle_test 6 + pidfd_file_handle_test pidfd_bind_mount 7 7 8 8 include ../lib.mk 9 9
+188
tools/testing/selftests/pidfd/pidfd_bind_mount.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + // Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 3 + 4 + #define _GNU_SOURCE 5 + #include <fcntl.h> 6 + #include <limits.h> 7 + #include <sched.h> 8 + #include <stdio.h> 9 + #include <string.h> 10 + #include <linux/fs.h> 11 + #include <sys/ioctl.h> 12 + #include <sys/stat.h> 13 + #include <sys/mount.h> 14 + #include <unistd.h> 15 + 16 + #include "pidfd.h" 17 + #include "../kselftest_harness.h" 18 + 19 + #ifndef __NR_open_tree 20 + #if defined __alpha__ 21 + #define __NR_open_tree 538 22 + #elif defined _MIPS_SIM 23 + #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ 24 + #define __NR_open_tree 4428 25 + #endif 26 + #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ 27 + #define __NR_open_tree 6428 28 + #endif 29 + #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ 30 + #define __NR_open_tree 5428 31 + #endif 32 + #elif defined __ia64__ 33 + #define __NR_open_tree (428 + 1024) 34 + #else 35 + #define __NR_open_tree 428 36 + #endif 37 + #endif 38 + 39 + #ifndef __NR_move_mount 40 + #if defined __alpha__ 41 + #define __NR_move_mount 539 42 + #elif defined _MIPS_SIM 43 + #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ 44 + #define __NR_move_mount 4429 45 + #endif 46 + #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ 47 + #define __NR_move_mount 6429 48 + #endif 49 + #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ 50 + #define __NR_move_mount 5429 51 + #endif 52 + #elif defined __ia64__ 53 + #define __NR_move_mount (428 + 1024) 54 + #else 55 + #define __NR_move_mount 429 56 + #endif 57 + #endif 58 + 59 + #ifndef MOVE_MOUNT_F_EMPTY_PATH 60 + #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */ 61 + #endif 62 + 63 + #ifndef MOVE_MOUNT_F_EMPTY_PATH 64 + #define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */ 65 + #endif 66 + 67 + static inline int sys_move_mount(int from_dfd, const char *from_pathname, 68 + int to_dfd, const char *to_pathname, 69 + unsigned int flags) 70 + { 71 + return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, 72 + to_pathname, flags); 73 + } 74 + 75 + #ifndef OPEN_TREE_CLONE 76 + #define OPEN_TREE_CLONE 1 77 + #endif 78 + 79 + #ifndef OPEN_TREE_CLOEXEC 80 + #define OPEN_TREE_CLOEXEC O_CLOEXEC 81 + #endif 82 + 83 + #ifndef AT_RECURSIVE 84 + #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ 85 + #endif 86 + 87 + static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags) 88 + { 89 + return syscall(__NR_open_tree, dfd, filename, flags); 90 + } 91 + 92 + FIXTURE(pidfd_bind_mount) { 93 + char template[PATH_MAX]; 94 + int fd_tmp; 95 + int pidfd; 96 + struct stat st1; 97 + struct stat st2; 98 + __u32 gen1; 99 + __u32 gen2; 100 + bool must_unmount; 101 + }; 102 + 103 + FIXTURE_SETUP(pidfd_bind_mount) 104 + { 105 + self->fd_tmp = -EBADF; 106 + self->must_unmount = false; 107 + ASSERT_EQ(unshare(CLONE_NEWNS), 0); 108 + ASSERT_LE(snprintf(self->template, PATH_MAX, "%s", P_tmpdir "/pidfd_bind_mount_XXXXXX"), PATH_MAX); 109 + self->fd_tmp = mkstemp(self->template); 110 + ASSERT_GE(self->fd_tmp, 0); 111 + self->pidfd = sys_pidfd_open(getpid(), 0); 112 + ASSERT_GE(self->pidfd, 0); 113 + ASSERT_GE(fstat(self->pidfd, &self->st1), 0); 114 + ASSERT_EQ(ioctl(self->pidfd, FS_IOC_GETVERSION, &self->gen1), 0); 115 + } 116 + 117 + FIXTURE_TEARDOWN(pidfd_bind_mount) 118 + { 119 + ASSERT_EQ(close(self->fd_tmp), 0); 120 + if (self->must_unmount) 121 + ASSERT_EQ(umount2(self->template, 0), 0); 122 + ASSERT_EQ(unlink(self->template), 0); 123 + } 124 + 125 + /* 126 + * Test that a detached mount can be created for a pidfd and then 127 + * attached to the filesystem hierarchy. 128 + */ 129 + TEST_F(pidfd_bind_mount, bind_mount) 130 + { 131 + int fd_tree; 132 + 133 + fd_tree = sys_open_tree(self->pidfd, "", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH); 134 + ASSERT_GE(fd_tree, 0); 135 + 136 + ASSERT_EQ(move_mount(fd_tree, "", self->fd_tmp, "", MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH), 0); 137 + self->must_unmount = true; 138 + 139 + ASSERT_EQ(close(fd_tree), 0); 140 + } 141 + 142 + /* Test that a pidfd can be reopened through procfs. */ 143 + TEST_F(pidfd_bind_mount, reopen) 144 + { 145 + int pidfd; 146 + char proc_path[PATH_MAX]; 147 + 148 + sprintf(proc_path, "/proc/self/fd/%d", self->pidfd); 149 + pidfd = open(proc_path, O_RDONLY | O_NOCTTY | O_CLOEXEC); 150 + ASSERT_GE(pidfd, 0); 151 + 152 + ASSERT_GE(fstat(self->pidfd, &self->st2), 0); 153 + ASSERT_EQ(ioctl(self->pidfd, FS_IOC_GETVERSION, &self->gen2), 0); 154 + 155 + ASSERT_TRUE(self->st1.st_dev == self->st2.st_dev && self->st1.st_ino == self->st2.st_ino); 156 + ASSERT_TRUE(self->gen1 == self->gen2); 157 + 158 + ASSERT_EQ(close(pidfd), 0); 159 + } 160 + 161 + /* 162 + * Test that a detached mount can be created for a pidfd and then 163 + * attached to the filesystem hierarchy and reopened. 164 + */ 165 + TEST_F(pidfd_bind_mount, bind_mount_reopen) 166 + { 167 + int fd_tree, fd_pidfd_mnt; 168 + 169 + fd_tree = sys_open_tree(self->pidfd, "", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH); 170 + ASSERT_GE(fd_tree, 0); 171 + 172 + ASSERT_EQ(move_mount(fd_tree, "", self->fd_tmp, "", MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH), 0); 173 + self->must_unmount = true; 174 + 175 + fd_pidfd_mnt = openat(-EBADF, self->template, O_RDONLY | O_NOCTTY | O_CLOEXEC); 176 + ASSERT_GE(fd_pidfd_mnt, 0); 177 + 178 + ASSERT_GE(fstat(fd_tree, &self->st2), 0); 179 + ASSERT_EQ(ioctl(fd_pidfd_mnt, FS_IOC_GETVERSION, &self->gen2), 0); 180 + 181 + ASSERT_TRUE(self->st1.st_dev == self->st2.st_dev && self->st1.st_ino == self->st2.st_ino); 182 + ASSERT_TRUE(self->gen1 == self->gen2); 183 + 184 + ASSERT_EQ(close(fd_tree), 0); 185 + ASSERT_EQ(close(fd_pidfd_mnt), 0); 186 + } 187 + 188 + TEST_HARNESS_MAIN