Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
67static 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
87static 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
92FIXTURE(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
103FIXTURE_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
117FIXTURE_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 */
129TEST_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. */
143TEST_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 */
165TEST_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
188TEST_HARNESS_MAIN