Skip to content

Commit f63df61

Browse files
committed
selftests: add pidfd bind-mount tests
Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent ef4144a commit f63df61

File tree

3 files changed

+190
-1
lines changed

3 files changed

+190
-1
lines changed

tools/testing/selftests/pidfd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pidfd_fdinfo_test
77
pidfd_getfd_test
88
pidfd_setns_test
99
pidfd_file_handle_test
10+
pidfd_bind_mount

tools/testing/selftests/pidfd/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ CFLAGS += -g $(KHDR_INCLUDES) -pthread -Wall
33

44
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \
55
pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \
6-
pidfd_file_handle_test
6+
pidfd_file_handle_test pidfd_bind_mount
77

88
include ../lib.mk
99

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (c) 2024 Christian Brauner <[email protected]>
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

0 commit comments

Comments
 (0)