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
sys_move_mount(int from_dfd,const char * from_pathname,int to_dfd,const char * to_pathname,unsigned int flags)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
sys_open_tree(int dfd,const char * filename,unsigned int flags)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
FIXTURE(pidfd_bind_mount)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
FIXTURE_SETUP(pidfd_bind_mount)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
FIXTURE_TEARDOWN(pidfd_bind_mount)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 */
TEST_F(pidfd_bind_mount,bind_mount)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. */
TEST_F(pidfd_bind_mount,reopen)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 */
TEST_F(pidfd_bind_mount,bind_mount_reopen)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
189