1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * An unmounted mount tree that a file descriptor keeps alive is put and
4 * vacated behind the root's back, so nothing may walk it under
5 * namespace_sem: it can't be copied recursively and its propagation can't
6 * be changed. The mount at its root alone can still be copied and that copy
7 * is an ordinary mount.
8 */
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <sched.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/mount.h>
17 #include <sys/stat.h>
18 #include <sys/syscall.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 #include <linux/mount.h>
22
23 #include "../wrappers.h"
24 #include "../../kselftest_harness.h"
25
26 #ifndef __NR_mount_setattr
27 #define __NR_mount_setattr 442
28 #endif
29
30 #ifndef __NR_pidfd_open
31 #define __NR_pidfd_open 434
32 #endif
33
34 #define PATH_LEN 64
35
sys_mount_setattr(int dfd,const char * path,unsigned int flags,struct mount_attr * attr,size_t size)36 static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flags,
37 struct mount_attr *attr, size_t size)
38 {
39 return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
40 }
41
sys_pidfd_open(pid_t pid,unsigned int flags)42 static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
43 {
44 return syscall(__NR_pidfd_open, pid, flags);
45 }
46
write_file(const char * path,const char * s)47 static int write_file(const char *path, const char *s)
48 {
49 ssize_t n = -1;
50 int fd;
51
52 fd = open(path, O_WRONLY | O_CLOEXEC);
53 if (fd >= 0) {
54 n = write(fd, s, strlen(s));
55 close(fd);
56 }
57 return n == (ssize_t)strlen(s) ? 0 : -1;
58 }
59
touch(const char * path)60 static int touch(const char *path)
61 {
62 int fd;
63
64 fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
65 if (fd < 0)
66 return -1;
67 close(fd);
68 return 0;
69 }
70
71 /* Become root in a new user namespace with a private mount namespace. */
enter_userns(void)72 static int enter_userns(void)
73 {
74 uid_t uid = getuid();
75 gid_t gid = getgid();
76 char map[32];
77
78 if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
79 return -1;
80 if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
81 return -1;
82 snprintf(map, sizeof(map), "0 %d 1", uid);
83 if (write_file("/proc/self/uid_map", map))
84 return -1;
85 snprintf(map, sizeof(map), "0 %d 1", gid);
86 if (write_file("/proc/self/gid_map", map))
87 return -1;
88 if (setgid(0) || setuid(0))
89 return -1;
90 return mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL);
91 }
92
93 /* A private mount namespace of our own, all mounts private. */
own_mntns(void)94 static int own_mntns(void)
95 {
96 if (unshare(CLONE_NEWNS))
97 return -1;
98 return mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL);
99 }
100
FIXTURE(unmounted_tree)101 FIXTURE(unmounted_tree) {
102 char dir[PATH_LEN];
103 };
104
FIXTURE_SETUP(unmounted_tree)105 FIXTURE_SETUP(unmounted_tree)
106 {
107 snprintf(self->dir, sizeof(self->dir), "/tmp/unmounted_tree.XXXXXX");
108 ASSERT_NE(mkdtemp(self->dir), NULL);
109 if (enter_userns()) {
110 rmdir(self->dir);
111 SKIP(return, "test requires user namespaces");
112 }
113 ASSERT_EQ(mount("tmpfs", self->dir, "tmpfs", 0, NULL), 0);
114 }
115
FIXTURE_TEARDOWN(unmounted_tree)116 FIXTURE_TEARDOWN(unmounted_tree)
117 {
118 umount2(self->dir, MNT_DETACH);
119 rmdir(self->dir);
120 }
121
122 /* A recursive copy of the mount @fd refers to must fail with EINVAL. */
assert_not_walked(struct __test_metadata * _metadata,int fd,const char * target)123 static void assert_not_walked(struct __test_metadata *_metadata, int fd,
124 const char *target)
125 {
126 char link[PATH_LEN];
127 int tfd;
128
129 snprintf(link, sizeof(link), "/proc/self/fd/%d", fd);
130 EXPECT_EQ(mount(link, target, NULL, MS_BIND | MS_REC, NULL), -1);
131 EXPECT_EQ(errno, EINVAL);
132
133 tfd = sys_open_tree(fd, "", AT_EMPTY_PATH | AT_RECURSIVE | OPEN_TREE_CLONE |
134 OPEN_TREE_CLOEXEC);
135 EXPECT_LT(tfd, 0);
136 EXPECT_EQ(errno, EINVAL);
137 if (tfd >= 0)
138 close(tfd);
139 }
140
141 /* The mount @fd refers to can be copied on its own and mounted on @target. */
assert_root_copied(struct __test_metadata * _metadata,int fd,const char * target)142 static void assert_root_copied(struct __test_metadata *_metadata, int fd,
143 const char *target)
144 {
145 int tfd;
146
147 tfd = sys_open_tree(fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
148 ASSERT_GE(tfd, 0);
149 ASSERT_EQ(sys_move_mount(tfd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH), 0);
150 close(tfd);
151 ASSERT_EQ(umount2(target, MNT_DETACH), 0);
152 }
153
154 /*
155 * A lazily unmounted bind mount of an nsfs file, kept alive by an fd, can't
156 * be copied recursively anymore. Copying the mount itself still works and
157 * so does copying a mounted bind mount of the same file.
158 */
TEST_F(unmounted_tree,detached_nsfs_bind_not_walked)159 TEST_F(unmounted_tree, detached_nsfs_bind_not_walked)
160 {
161 char x[PATH_LEN], y[PATH_LEN], z[PATH_LEN], link[PATH_LEN];
162 int fd, tfd;
163
164 snprintf(x, sizeof(x), "%s/x", self->dir);
165 snprintf(y, sizeof(y), "%s/y", self->dir);
166 snprintf(z, sizeof(z), "%s/z", self->dir);
167 ASSERT_EQ(touch(x), 0);
168 ASSERT_EQ(touch(y), 0);
169 ASSERT_EQ(touch(z), 0);
170
171 ASSERT_EQ(mount("/proc/self/ns/net", x, NULL, MS_BIND, NULL), 0);
172 fd = open(x, O_PATH | O_CLOEXEC);
173 ASSERT_GE(fd, 0);
174 ASSERT_EQ(umount2(x, MNT_DETACH), 0);
175
176 assert_not_walked(_metadata, fd, y);
177
178 snprintf(link, sizeof(link), "/proc/self/fd/%d", fd);
179 ASSERT_EQ(mount(link, y, NULL, MS_BIND, NULL), 0);
180 ASSERT_EQ(umount2(y, MNT_DETACH), 0);
181 assert_root_copied(_metadata, fd, y);
182 /* opening it is fine too */
183 tfd = sys_open_tree(fd, "", AT_EMPTY_PATH | OPEN_TREE_CLOEXEC);
184 ASSERT_GE(tfd, 0);
185 close(tfd);
186 close(fd);
187
188 ASSERT_EQ(mount("/proc/self/ns/net", y, NULL, MS_BIND, NULL), 0);
189 ASSERT_EQ(mount(y, z, NULL, MS_BIND | MS_REC, NULL), 0);
190 tfd = sys_open_tree(AT_FDCWD, y, AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
191 ASSERT_GE(tfd, 0);
192 close(tfd);
193 }
194
195 /* Bind-mount the pidfd @pidfd on @target the way pidfd_bind_mount does. */
bind_pidfd(int pidfd,const char * target)196 static int bind_pidfd(int pidfd, const char *target)
197 {
198 int tfd, ret;
199
200 tfd = sys_open_tree(pidfd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
201 if (tfd < 0)
202 return -1;
203 ret = sys_move_mount(tfd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
204 close(tfd);
205 return ret;
206 }
207
208 /* The same for a bind mount of a pidfd. */
TEST_F(unmounted_tree,detached_pidfs_bind_not_walked)209 TEST_F(unmounted_tree, detached_pidfs_bind_not_walked)
210 {
211 char x[PATH_LEN], y[PATH_LEN];
212 int pidfd, fd, tfd;
213
214 snprintf(x, sizeof(x), "%s/x", self->dir);
215 snprintf(y, sizeof(y), "%s/y", self->dir);
216 ASSERT_EQ(touch(x), 0);
217 ASSERT_EQ(touch(y), 0);
218
219 pidfd = sys_pidfd_open(getpid(), 0);
220 ASSERT_GE(pidfd, 0);
221 ASSERT_EQ(bind_pidfd(pidfd, x), 0);
222 fd = open(x, O_PATH | O_CLOEXEC);
223 ASSERT_GE(fd, 0);
224 ASSERT_EQ(umount2(x, MNT_DETACH), 0);
225
226 assert_not_walked(_metadata, fd, y);
227 assert_root_copied(_metadata, fd, y);
228 close(fd);
229
230 ASSERT_EQ(bind_pidfd(pidfd, y), 0);
231 tfd = sys_open_tree(AT_FDCWD, y, AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
232 ASSERT_GE(tfd, 0);
233 close(tfd);
234 close(pidfd);
235 }
236
237 /* What a child in its own mount namespace reports back. */
238 enum {
239 CHILD_OK,
240 CHILD_NS, /* could not set up the mount namespace */
241 CHILD_MOUNT, /* could not set up the mounts */
242 CHILD_PIPE, /* the parent went away */
243 CHILD_WALKED, /* the detached tree was copied recursively */
244 CHILD_ROOT_REFUSED, /* the mount at its root wasn't copied */
245 CHILD_STILL_MOUNTED, /* the copy survived the unlink of its mountpoint */
246 CHILD_ERRNO, /* refused, but not with EINVAL */
247 };
248
child_reason(int code)249 static const char *child_reason(int code)
250 {
251 static const char *const reasons[] = {
252 [CHILD_OK] = "ok",
253 [CHILD_NS] = "could not set up the mount namespace",
254 [CHILD_MOUNT] = "could not set up the mounts",
255 [CHILD_PIPE] = "the parent went away",
256 [CHILD_WALKED] = "the detached tree was copied recursively",
257 [CHILD_ROOT_REFUSED] = "the mount at its root wasn't copied",
258 [CHILD_STILL_MOUNTED] = "the copy survived the unlink of its mountpoint",
259 [CHILD_ERRNO] = "refused, but not with EINVAL",
260 };
261
262 if (code < 0 || code >= (int)(sizeof(reasons) / sizeof(reasons[0])))
263 return "child died";
264 return reasons[code];
265 }
266
267 struct child_args {
268 const char *x; /* the nsfs bind mount goes here */
269 const char *s; /* a file to bind on top of it */
270 const char *y; /* a target to copy to */
271 };
272
273 /*
274 * Run @fn in a child in a mount namespace of its own. Once the child
275 * reports that its mounts are in place, unlink @victim here, where it is a
276 * plain file, and let the child carry on. Returns what the child reported.
277 */
run_child(struct __test_metadata * _metadata,int (* fn)(const struct child_args *,int,int),const struct child_args * a,const char * victim)278 static int run_child(struct __test_metadata *_metadata,
279 int (*fn)(const struct child_args *, int, int),
280 const struct child_args *a, const char *victim)
281 {
282 int to_parent[2], to_child[2], status;
283 pid_t pid;
284 char c;
285
286 if (pipe(to_parent) || pipe(to_child))
287 return -1;
288 pid = fork();
289 if (pid < 0)
290 return -1;
291 if (pid == 0) {
292 close(to_parent[0]);
293 close(to_child[1]);
294 _exit(fn(a, to_parent[1], to_child[0]));
295 }
296 close(to_parent[1]);
297 close(to_child[0]);
298 if (read(to_parent[0], &c, 1) == 1) {
299 /* not a mountpoint in this namespace, so the file can go */
300 EXPECT_EQ(unlink(victim), 0);
301 EXPECT_EQ(write(to_child[1], "", 1), 1);
302 }
303 close(to_parent[0]);
304 close(to_child[1]);
305 if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
306 return -1;
307 return WEXITSTATUS(status);
308 }
309
310 /*
311 * An nsfs bind mount on @x, an fd on it and a bind mount of @s on top. Once
312 * the parent has unlinked @x underneath, the first mount is detached and the
313 * second one, which stayed attached to it, has been vacated. The tree may
314 * not be walked, the mount at its root may still be copied.
315 */
vacant_child(const struct child_args * a,int to_parent,int from_parent)316 static int vacant_child(const struct child_args *a, int to_parent, int from_parent)
317 {
318 char link[PATH_LEN];
319 int fd, tfd;
320 char c;
321
322 if (own_mntns())
323 return CHILD_NS;
324 if (mount("/proc/self/ns/net", a->x, NULL, MS_BIND, NULL))
325 return CHILD_MOUNT;
326 fd = open(a->x, O_PATH | O_CLOEXEC);
327 if (fd < 0 || mount(a->s, a->x, NULL, MS_BIND, NULL))
328 return CHILD_MOUNT;
329
330 if (write(to_parent, "", 1) != 1 || read(from_parent, &c, 1) != 1)
331 return CHILD_PIPE;
332
333 tfd = sys_open_tree(fd, "", AT_EMPTY_PATH | AT_RECURSIVE | OPEN_TREE_CLONE |
334 OPEN_TREE_CLOEXEC);
335 if (tfd >= 0) {
336 close(tfd);
337 return CHILD_WALKED;
338 }
339 if (errno != EINVAL)
340 return CHILD_ERRNO;
341 snprintf(link, sizeof(link), "/proc/self/fd/%d", fd);
342 if (!mount(link, a->y, NULL, MS_BIND | MS_REC, NULL))
343 return CHILD_WALKED;
344 if (errno != EINVAL)
345 return CHILD_ERRNO;
346
347 tfd = sys_open_tree(fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
348 if (tfd < 0)
349 return CHILD_ROOT_REFUSED;
350 close(tfd);
351 /* the last reference on the detached mount collects the vacant one */
352 close(fd);
353 return CHILD_OK;
354 }
355
356 /*
357 * unlink() of the child's mountpoint from here, where it is a plain file,
358 * detaches the child's nsfs bind mount with the mount on top left attached
359 * to it. The mount on top loses its last reference right there and is
360 * vacated. The child must not be able to walk that tree.
361 */
TEST_F(unmounted_tree,vacant_child_not_copied)362 TEST_F(unmounted_tree, vacant_child_not_copied)
363 {
364 char x[PATH_LEN], s[PATH_LEN], y[PATH_LEN];
365 struct child_args a = { .x = x, .s = s, .y = y };
366 int ret;
367
368 snprintf(x, sizeof(x), "%s/x", self->dir);
369 snprintf(s, sizeof(s), "%s/s", self->dir);
370 snprintf(y, sizeof(y), "%s/y", self->dir);
371 ASSERT_EQ(touch(x), 0);
372 ASSERT_EQ(touch(s), 0);
373 ASSERT_EQ(touch(y), 0);
374
375 ret = run_child(_metadata, vacant_child, &a, x);
376 ASSERT_EQ(ret, CHILD_OK)
377 TH_LOG("child: %s", child_reason(ret));
378 }
379
380 /*
381 * An nsfs bind mount on @x, lazily unmounted but kept alive by an fd, and a
382 * copy of it on @y. That copy is a mount like any other: once the parent has
383 * unlinked @y underneath, it is unmounted and its attributes can't be changed
384 * anymore. A copy that took the flag along is only unhooked, stays in the
385 * namespace and still takes the change.
386 */
ordinary_copy(const struct child_args * a,int to_parent,int from_parent)387 static int ordinary_copy(const struct child_args *a, int to_parent, int from_parent)
388 {
389 struct mount_attr attr = {
390 .attr_set = MOUNT_ATTR_NOSUID,
391 };
392 char link[PATH_LEN];
393 int fd, fdy;
394 char c;
395
396 if (own_mntns())
397 return CHILD_NS;
398 if (mount("/proc/self/ns/net", a->x, NULL, MS_BIND, NULL))
399 return CHILD_MOUNT;
400 fd = open(a->x, O_PATH | O_CLOEXEC);
401 if (fd < 0 || umount2(a->x, MNT_DETACH))
402 return CHILD_MOUNT;
403 snprintf(link, sizeof(link), "/proc/self/fd/%d", fd);
404 if (mount(link, a->y, NULL, MS_BIND, NULL))
405 return CHILD_MOUNT;
406 fdy = open(a->y, O_PATH | O_CLOEXEC);
407 if (fdy < 0)
408 return CHILD_MOUNT;
409
410 if (write(to_parent, "", 1) != 1 || read(from_parent, &c, 1) != 1)
411 return CHILD_PIPE;
412
413 if (!sys_mount_setattr(fdy, "", AT_EMPTY_PATH, &attr, sizeof(attr)))
414 return CHILD_STILL_MOUNTED;
415 if (errno != EINVAL)
416 return CHILD_ERRNO;
417 close(fdy);
418 close(fd);
419 return CHILD_OK;
420 }
421
422 /*
423 * A copy of a detached nsfs bind mount must not take after its source and
424 * read as unmounted. unlink() of its mountpoint from here unmounts it like
425 * any other mount, so the child can't change its attributes anymore.
426 */
TEST_F(unmounted_tree,copy_of_detached_bind_is_ordinary)427 TEST_F(unmounted_tree, copy_of_detached_bind_is_ordinary)
428 {
429 char x[PATH_LEN], y[PATH_LEN];
430 struct child_args a = { .x = x, .y = y };
431 int ret;
432
433 snprintf(x, sizeof(x), "%s/x", self->dir);
434 snprintf(y, sizeof(y), "%s/y", self->dir);
435 ASSERT_EQ(touch(x), 0);
436 ASSERT_EQ(touch(y), 0);
437
438 ret = run_child(_metadata, ordinary_copy, &a, y);
439 ASSERT_EQ(ret, CHILD_OK)
440 TH_LOG("child: %s", child_reason(ret));
441 }
442
443 /*
444 * mount_setattr() may change the propagation of a detached tree while that
445 * tree is still the root of an anonymous mount namespace, but not once the
446 * tree has been dissolved and only a second fd keeps its root alive.
447 */
TEST_F(unmounted_tree,detached_tree_setattr_refused)448 TEST_F(unmounted_tree, detached_tree_setattr_refused)
449 {
450 struct mount_attr attr = {
451 .propagation = MS_SHARED,
452 };
453 char a[PATH_LEN], b[PATH_LEN];
454 int tfd, fd;
455
456 snprintf(a, sizeof(a), "%s/a", self->dir);
457 snprintf(b, sizeof(b), "%s/a/b", self->dir);
458 ASSERT_EQ(mkdir(a, 0755), 0);
459 ASSERT_EQ(mount("tmpfs", a, "tmpfs", 0, NULL), 0);
460 ASSERT_EQ(mkdir(b, 0755), 0);
461 ASSERT_EQ(mount("tmpfs", b, "tmpfs", 0, NULL), 0);
462
463 tfd = sys_open_tree(AT_FDCWD, self->dir, AT_RECURSIVE | OPEN_TREE_CLONE |
464 OPEN_TREE_CLOEXEC);
465 ASSERT_GE(tfd, 0);
466 ASSERT_EQ(sys_mount_setattr(tfd, "", AT_EMPTY_PATH | AT_RECURSIVE, &attr, sizeof(attr)), 0);
467 attr.propagation = MS_PRIVATE;
468 ASSERT_EQ(sys_mount_setattr(tfd, "", AT_EMPTY_PATH | AT_RECURSIVE, &attr, sizeof(attr)), 0);
469
470 /* a second reference on the root, then dissolve the tree */
471 fd = sys_open_tree(tfd, "", AT_EMPTY_PATH | OPEN_TREE_CLOEXEC);
472 ASSERT_GE(fd, 0);
473 close(tfd);
474
475 attr.propagation = MS_SHARED;
476 ASSERT_EQ(sys_mount_setattr(fd, "", AT_EMPTY_PATH | AT_RECURSIVE, &attr, sizeof(attr)), -1);
477 ASSERT_EQ(errno, EINVAL);
478 attr.propagation = MS_PRIVATE;
479 ASSERT_EQ(sys_mount_setattr(fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), -1);
480 ASSERT_EQ(errno, EINVAL);
481 close(fd);
482 }
483
484 TEST_HARNESS_MAIN
485