xref: /linux/tools/testing/selftests/pidfd/pidfd_bind_mount.c (revision 5f85bd6aeceaecd0ff3a5ee827bf75eb6141ad55)
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