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