xref: /linux/tools/testing/selftests/filesystems/open_tree_ns/open_tree_ns_test.c (revision 157d3d6efd5a58466d90be3a134f9667486fe6f9) !
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Test for OPEN_TREE_NAMESPACE flag.
4  *
5  * Test that open_tree() with OPEN_TREE_NAMESPACE creates a new mount
6  * namespace containing the specified mount tree.
7  */
8 #define _GNU_SOURCE
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <limits.h>
13 #include <linux/nsfs.h>
14 #include <sched.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <sys/mount.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 
24 #include "../wrappers.h"
25 #include "../statmount/statmount.h"
26 #include "../utils.h"
27 #include "../../kselftest_harness.h"
28 
29 #ifndef OPEN_TREE_NAMESPACE
30 #define OPEN_TREE_NAMESPACE	(1 << 1)
31 #endif
32 
get_mnt_ns_id(int fd,uint64_t * mnt_ns_id)33 static int get_mnt_ns_id(int fd, uint64_t *mnt_ns_id)
34 {
35 	if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0)
36 		return -errno;
37 	return 0;
38 }
39 
get_mnt_ns_id_from_path(const char * path,uint64_t * mnt_ns_id)40 static int get_mnt_ns_id_from_path(const char *path, uint64_t *mnt_ns_id)
41 {
42 	int fd, ret;
43 
44 	fd = open(path, O_RDONLY);
45 	if (fd < 0)
46 		return -errno;
47 
48 	ret = get_mnt_ns_id(fd, mnt_ns_id);
49 	close(fd);
50 	return ret;
51 }
52 
53 #define STATMOUNT_BUFSIZE (1 << 15)
54 
statmount_alloc(uint64_t mnt_id,uint64_t mnt_ns_id,uint64_t mask)55 static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask)
56 {
57 	struct statmount *buf;
58 	size_t bufsize = STATMOUNT_BUFSIZE;
59 	int ret;
60 
61 	for (;;) {
62 		buf = malloc(bufsize);
63 		if (!buf)
64 			return NULL;
65 
66 		ret = statmount(mnt_id, mnt_ns_id, mask, buf, bufsize, 0);
67 		if (ret == 0)
68 			return buf;
69 
70 		free(buf);
71 		if (errno != EOVERFLOW)
72 			return NULL;
73 
74 		bufsize <<= 1;
75 	}
76 }
77 
log_mount(struct __test_metadata * _metadata,struct statmount * sm)78 static void log_mount(struct __test_metadata *_metadata, struct statmount *sm)
79 {
80 	const char *fs_type = "";
81 	const char *mnt_root = "";
82 	const char *mnt_point = "";
83 
84 	if (sm->mask & STATMOUNT_FS_TYPE)
85 		fs_type = sm->str + sm->fs_type;
86 	if (sm->mask & STATMOUNT_MNT_ROOT)
87 		mnt_root = sm->str + sm->mnt_root;
88 	if (sm->mask & STATMOUNT_MNT_POINT)
89 		mnt_point = sm->str + sm->mnt_point;
90 
91 	TH_LOG("  mnt_id: %llu, parent_id: %llu, fs_type: %s, root: %s, point: %s",
92 	       (unsigned long long)sm->mnt_id,
93 	       (unsigned long long)sm->mnt_parent_id,
94 	       fs_type, mnt_root, mnt_point);
95 }
96 
dump_mounts(struct __test_metadata * _metadata,uint64_t mnt_ns_id)97 static void dump_mounts(struct __test_metadata *_metadata, uint64_t mnt_ns_id)
98 {
99 	uint64_t list[256];
100 	ssize_t nr_mounts;
101 
102 	nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);
103 	if (nr_mounts < 0) {
104 		TH_LOG("listmount failed: %s", strerror(errno));
105 		return;
106 	}
107 
108 	TH_LOG("Mount namespace %llu contains %zd mount(s):",
109 	       (unsigned long long)mnt_ns_id, nr_mounts);
110 
111 	for (ssize_t i = 0; i < nr_mounts; i++) {
112 		struct statmount *sm;
113 
114 		sm = statmount_alloc(list[i], mnt_ns_id,
115 				     STATMOUNT_MNT_BASIC |
116 				     STATMOUNT_FS_TYPE |
117 				     STATMOUNT_MNT_ROOT |
118 				     STATMOUNT_MNT_POINT);
119 		if (!sm) {
120 			TH_LOG("  [%zd] mnt_id %llu: statmount failed: %s",
121 			       i, (unsigned long long)list[i], strerror(errno));
122 			continue;
123 		}
124 
125 		log_mount(_metadata, sm);
126 		free(sm);
127 	}
128 }
129 
FIXTURE(open_tree_ns)130 FIXTURE(open_tree_ns)
131 {
132 	int fd;
133 	uint64_t current_ns_id;
134 };
135 
FIXTURE_VARIANT(open_tree_ns)136 FIXTURE_VARIANT(open_tree_ns)
137 {
138 	const char *path;
139 	unsigned int flags;
140 	bool expect_success;
141 	bool expect_different_ns;
142 	int min_mounts;
143 };
144 
FIXTURE_VARIANT_ADD(open_tree_ns,basic_root)145 FIXTURE_VARIANT_ADD(open_tree_ns, basic_root)
146 {
147 	.path = "/",
148 	.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
149 	.expect_success = true,
150 	.expect_different_ns = true,
151 	/*
152 	 * The empty rootfs is hidden from listmount()/mountinfo,
153 	 * so we only see the bind mount on top of it.
154 	 */
155 	.min_mounts = 1,
156 };
157 
FIXTURE_VARIANT_ADD(open_tree_ns,recursive_root)158 FIXTURE_VARIANT_ADD(open_tree_ns, recursive_root)
159 {
160 	.path = "/",
161 	.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
162 	.expect_success = true,
163 	.expect_different_ns = true,
164 	.min_mounts = 1,
165 };
166 
FIXTURE_VARIANT_ADD(open_tree_ns,subdir_tmp)167 FIXTURE_VARIANT_ADD(open_tree_ns, subdir_tmp)
168 {
169 	.path = "/tmp",
170 	.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
171 	.expect_success = true,
172 	.expect_different_ns = true,
173 	.min_mounts = 1,
174 };
175 
FIXTURE_VARIANT_ADD(open_tree_ns,subdir_proc)176 FIXTURE_VARIANT_ADD(open_tree_ns, subdir_proc)
177 {
178 	.path = "/proc",
179 	.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
180 	.expect_success = true,
181 	.expect_different_ns = true,
182 	.min_mounts = 1,
183 };
184 
FIXTURE_VARIANT_ADD(open_tree_ns,recursive_tmp)185 FIXTURE_VARIANT_ADD(open_tree_ns, recursive_tmp)
186 {
187 	.path = "/tmp",
188 	.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
189 	.expect_success = true,
190 	.expect_different_ns = true,
191 	.min_mounts = 1,
192 };
193 
FIXTURE_VARIANT_ADD(open_tree_ns,recursive_run)194 FIXTURE_VARIANT_ADD(open_tree_ns, recursive_run)
195 {
196 	.path = "/run",
197 	.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
198 	.expect_success = true,
199 	.expect_different_ns = true,
200 	.min_mounts = 1,
201 };
202 
FIXTURE_VARIANT_ADD(open_tree_ns,invalid_recursive_alone)203 FIXTURE_VARIANT_ADD(open_tree_ns, invalid_recursive_alone)
204 {
205 	.path = "/",
206 	.flags = AT_RECURSIVE | OPEN_TREE_CLOEXEC,
207 	.expect_success = false,
208 	.expect_different_ns = false,
209 	.min_mounts = 0,
210 };
211 
FIXTURE_SETUP(open_tree_ns)212 FIXTURE_SETUP(open_tree_ns)
213 {
214 	int ret;
215 
216 	self->fd = -1;
217 
218 	/* Check if open_tree syscall is supported */
219 	ret = sys_open_tree(-1, NULL, 0);
220 	if (ret == -1 && errno == ENOSYS)
221 		SKIP(return, "open_tree() syscall not supported");
222 
223 	/* Check if statmount/listmount are supported */
224 	ret = statmount(0, 0, 0, NULL, 0, 0);
225 	if (ret == -1 && errno == ENOSYS)
226 		SKIP(return, "statmount() syscall not supported");
227 
228 	/* Get current mount namespace ID for comparison */
229 	ret = get_mnt_ns_id_from_path("/proc/self/ns/mnt", &self->current_ns_id);
230 	if (ret < 0)
231 		SKIP(return, "Failed to get current mount namespace ID");
232 }
233 
FIXTURE_TEARDOWN(open_tree_ns)234 FIXTURE_TEARDOWN(open_tree_ns)
235 {
236 	if (self->fd >= 0)
237 		close(self->fd);
238 }
239 
TEST_F(open_tree_ns,create_namespace)240 TEST_F(open_tree_ns, create_namespace)
241 {
242 	uint64_t new_ns_id;
243 	uint64_t list[256];
244 	ssize_t nr_mounts;
245 	int ret;
246 
247 	self->fd = sys_open_tree(AT_FDCWD, variant->path, variant->flags);
248 
249 	if (!variant->expect_success) {
250 		ASSERT_LT(self->fd, 0);
251 		ASSERT_EQ(errno, EINVAL);
252 		return;
253 	}
254 
255 	if (self->fd < 0 && errno == EINVAL)
256 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
257 
258 	ASSERT_GE(self->fd, 0);
259 
260 	/* Verify we can get the namespace ID */
261 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
262 	ASSERT_EQ(ret, 0);
263 
264 	/* Verify it's a different namespace */
265 	if (variant->expect_different_ns)
266 		ASSERT_NE(new_ns_id, self->current_ns_id);
267 
268 	/* List mounts in the new namespace */
269 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
270 	ASSERT_GE(nr_mounts, 0) {
271 		TH_LOG("%m - listmount failed");
272 	}
273 
274 	/* Verify minimum expected mounts */
275 	ASSERT_GE(nr_mounts, variant->min_mounts);
276 	TH_LOG("Namespace contains %zd mounts", nr_mounts);
277 }
278 
TEST_F(open_tree_ns,setns_into_namespace)279 TEST_F(open_tree_ns, setns_into_namespace)
280 {
281 	uint64_t new_ns_id;
282 	pid_t pid;
283 	int status;
284 	int ret;
285 
286 	/* Only test with basic flags */
287 	if (!(variant->flags & OPEN_TREE_NAMESPACE))
288 		SKIP(return, "setns test only for basic / case");
289 
290 	self->fd = sys_open_tree(AT_FDCWD, variant->path, variant->flags);
291 	if (self->fd < 0 && errno == EINVAL)
292 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
293 
294 	ASSERT_GE(self->fd, 0);
295 
296 	/* Get namespace ID and dump all mounts */
297 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
298 	ASSERT_EQ(ret, 0);
299 
300 	dump_mounts(_metadata, new_ns_id);
301 
302 	pid = fork();
303 	ASSERT_GE(pid, 0);
304 
305 	if (pid == 0) {
306 		/* Child: try to enter the namespace */
307 		if (setns(self->fd, CLONE_NEWNS) < 0)
308 			_exit(1);
309 		_exit(0);
310 	}
311 
312 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
313 	ASSERT_TRUE(WIFEXITED(status));
314 	ASSERT_EQ(WEXITSTATUS(status), 0);
315 }
316 
TEST_F(open_tree_ns,verify_mount_properties)317 TEST_F(open_tree_ns, verify_mount_properties)
318 {
319 	struct statmount sm;
320 	uint64_t new_ns_id;
321 	uint64_t list[256];
322 	ssize_t nr_mounts;
323 	int ret;
324 
325 	/* Only test with basic flags on root */
326 	if (variant->flags != (OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC) ||
327 	    strcmp(variant->path, "/") != 0)
328 		SKIP(return, "mount properties test only for basic / case");
329 
330 	self->fd = sys_open_tree(AT_FDCWD, "/", OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
331 	if (self->fd < 0 && errno == EINVAL)
332 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
333 
334 	ASSERT_GE(self->fd, 0);
335 
336 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
337 	ASSERT_EQ(ret, 0);
338 
339 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
340 	ASSERT_GE(nr_mounts, 1);
341 
342 	/* Get info about the root mount (the bind mount, rootfs is hidden) */
343 	ret = statmount(list[0], new_ns_id, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
344 	ASSERT_EQ(ret, 0);
345 
346 	ASSERT_NE(sm.mnt_id, sm.mnt_parent_id);
347 
348 	TH_LOG("Root mount id: %llu, parent: %llu",
349 	       (unsigned long long)sm.mnt_id,
350 	       (unsigned long long)sm.mnt_parent_id);
351 }
352 
FIXTURE(open_tree_ns_caps)353 FIXTURE(open_tree_ns_caps)
354 {
355 	bool has_caps;
356 };
357 
FIXTURE_SETUP(open_tree_ns_caps)358 FIXTURE_SETUP(open_tree_ns_caps)
359 {
360 	int ret;
361 
362 	/* Check if open_tree syscall is supported */
363 	ret = sys_open_tree(-1, NULL, 0);
364 	if (ret == -1 && errno == ENOSYS)
365 		SKIP(return, "open_tree() syscall not supported");
366 
367 	self->has_caps = (geteuid() == 0);
368 }
369 
FIXTURE_TEARDOWN(open_tree_ns_caps)370 FIXTURE_TEARDOWN(open_tree_ns_caps)
371 {
372 }
373 
TEST_F(open_tree_ns_caps,requires_cap_sys_admin)374 TEST_F(open_tree_ns_caps, requires_cap_sys_admin)
375 {
376 	pid_t pid;
377 	int status;
378 
379 	pid = fork();
380 	ASSERT_GE(pid, 0);
381 
382 	if (pid == 0) {
383 		int fd;
384 
385 		/* Child: drop privileges using utils.h helper */
386 		if (enter_userns() != 0)
387 			_exit(2);
388 
389 		/* Drop all caps using utils.h helper */
390 		if (caps_down() == 0)
391 			_exit(3);
392 
393 		fd = sys_open_tree(AT_FDCWD, "/",
394 				   OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
395 		if (fd >= 0) {
396 			close(fd);
397 			/* Should have failed without caps */
398 			_exit(1);
399 		}
400 
401 		if (errno == EPERM)
402 			_exit(0);
403 
404 		/* EINVAL means OPEN_TREE_NAMESPACE not supported */
405 		if (errno == EINVAL)
406 			_exit(4);
407 
408 		/* Unexpected error */
409 		_exit(5);
410 	}
411 
412 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
413 	ASSERT_TRUE(WIFEXITED(status));
414 
415 	switch (WEXITSTATUS(status)) {
416 	case 0:
417 		/* Expected: EPERM without caps */
418 		break;
419 	case 1:
420 		ASSERT_FALSE(true) TH_LOG("OPEN_TREE_NAMESPACE succeeded without caps");
421 		break;
422 	case 2:
423 		SKIP(return, "setup_userns failed");
424 		break;
425 	case 3:
426 		SKIP(return, "caps_down failed");
427 		break;
428 	case 4:
429 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
430 		break;
431 	default:
432 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
433 					  WEXITSTATUS(status));
434 		break;
435 	}
436 }
437 
FIXTURE(open_tree_ns_userns)438 FIXTURE(open_tree_ns_userns)
439 {
440 	int fd;
441 };
442 
FIXTURE_SETUP(open_tree_ns_userns)443 FIXTURE_SETUP(open_tree_ns_userns)
444 {
445 	int ret;
446 
447 	self->fd = -1;
448 
449 	/* Check if open_tree syscall is supported */
450 	ret = sys_open_tree(-1, NULL, 0);
451 	if (ret == -1 && errno == ENOSYS)
452 		SKIP(return, "open_tree() syscall not supported");
453 
454 	/* Check if statmount/listmount are supported */
455 	ret = statmount(0, 0, 0, NULL, 0, 0);
456 	if (ret == -1 && errno == ENOSYS)
457 		SKIP(return, "statmount() syscall not supported");
458 }
459 
FIXTURE_TEARDOWN(open_tree_ns_userns)460 FIXTURE_TEARDOWN(open_tree_ns_userns)
461 {
462 	if (self->fd >= 0)
463 		close(self->fd);
464 }
465 
TEST_F(open_tree_ns_userns,create_in_userns)466 TEST_F(open_tree_ns_userns, create_in_userns)
467 {
468 	pid_t pid;
469 	int status;
470 
471 	pid = fork();
472 	ASSERT_GE(pid, 0);
473 
474 	if (pid == 0) {
475 		uint64_t new_ns_id;
476 		uint64_t list[256];
477 		ssize_t nr_mounts;
478 		int fd;
479 
480 		/* Create new user namespace (also creates mount namespace) */
481 		if (enter_userns() != 0)
482 			_exit(2);
483 
484 		/* Now we have CAP_SYS_ADMIN in the user namespace */
485 		fd = sys_open_tree(AT_FDCWD, "/",
486 				   OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
487 		if (fd < 0) {
488 			if (errno == EINVAL)
489 				_exit(4); /* OPEN_TREE_NAMESPACE not supported */
490 			_exit(1);
491 		}
492 
493 		/* Verify we can get the namespace ID */
494 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
495 			_exit(5);
496 
497 		/* Verify we can list mounts in the new namespace */
498 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
499 		if (nr_mounts < 0)
500 			_exit(6);
501 
502 		/* Should have at least 1 mount */
503 		if (nr_mounts < 1)
504 			_exit(7);
505 
506 		close(fd);
507 		_exit(0);
508 	}
509 
510 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
511 	ASSERT_TRUE(WIFEXITED(status));
512 
513 	switch (WEXITSTATUS(status)) {
514 	case 0:
515 		/* Success */
516 		break;
517 	case 1:
518 		ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed in userns");
519 		break;
520 	case 2:
521 		SKIP(return, "setup_userns failed");
522 		break;
523 	case 4:
524 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
525 		break;
526 	case 5:
527 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
528 		break;
529 	case 6:
530 		ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace");
531 		break;
532 	case 7:
533 		ASSERT_FALSE(true) TH_LOG("New namespace has no mounts");
534 		break;
535 	default:
536 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
537 					  WEXITSTATUS(status));
538 		break;
539 	}
540 }
541 
TEST_F(open_tree_ns_userns,setns_in_userns)542 TEST_F(open_tree_ns_userns, setns_in_userns)
543 {
544 	pid_t pid;
545 	int status;
546 
547 	pid = fork();
548 	ASSERT_GE(pid, 0);
549 
550 	if (pid == 0) {
551 		uint64_t new_ns_id;
552 		int fd;
553 		pid_t inner_pid;
554 		int inner_status;
555 
556 		/* Create new user namespace */
557 		if (enter_userns() != 0)
558 			_exit(2);
559 
560 		fd = sys_open_tree(AT_FDCWD, "/",
561 				   OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
562 		if (fd < 0) {
563 			if (errno == EINVAL)
564 				_exit(4);
565 			_exit(1);
566 		}
567 
568 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
569 			_exit(5);
570 
571 		/* Fork again to test setns into the new namespace */
572 		inner_pid = fork();
573 		if (inner_pid < 0)
574 			_exit(8);
575 
576 		if (inner_pid == 0) {
577 			/* Inner child: enter the new namespace */
578 			if (setns(fd, CLONE_NEWNS) < 0)
579 				_exit(1);
580 			_exit(0);
581 		}
582 
583 		if (waitpid(inner_pid, &inner_status, 0) != inner_pid)
584 			_exit(9);
585 
586 		if (!WIFEXITED(inner_status) || WEXITSTATUS(inner_status) != 0)
587 			_exit(10);
588 
589 		close(fd);
590 		_exit(0);
591 	}
592 
593 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
594 	ASSERT_TRUE(WIFEXITED(status));
595 
596 	switch (WEXITSTATUS(status)) {
597 	case 0:
598 		/* Success */
599 		break;
600 	case 1:
601 		ASSERT_FALSE(true) TH_LOG("open_tree or setns failed in userns");
602 		break;
603 	case 2:
604 		SKIP(return, "setup_userns failed");
605 		break;
606 	case 4:
607 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
608 		break;
609 	case 5:
610 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
611 		break;
612 	case 8:
613 		ASSERT_FALSE(true) TH_LOG("Inner fork failed");
614 		break;
615 	case 9:
616 		ASSERT_FALSE(true) TH_LOG("Inner waitpid failed");
617 		break;
618 	case 10:
619 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
620 		break;
621 	default:
622 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
623 					  WEXITSTATUS(status));
624 		break;
625 	}
626 }
627 
TEST_F(open_tree_ns_userns,recursive_in_userns)628 TEST_F(open_tree_ns_userns, recursive_in_userns)
629 {
630 	pid_t pid;
631 	int status;
632 
633 	pid = fork();
634 	ASSERT_GE(pid, 0);
635 
636 	if (pid == 0) {
637 		uint64_t new_ns_id;
638 		uint64_t list[256];
639 		ssize_t nr_mounts;
640 		int fd;
641 
642 		/* Create new user namespace */
643 		if (enter_userns() != 0)
644 			_exit(2);
645 
646 		/* Test recursive flag in userns */
647 		fd = sys_open_tree(AT_FDCWD, "/",
648 				   OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
649 		if (fd < 0) {
650 			if (errno == EINVAL)
651 				_exit(4);
652 			_exit(1);
653 		}
654 
655 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
656 			_exit(5);
657 
658 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
659 		if (nr_mounts < 0)
660 			_exit(6);
661 
662 		/* Recursive should copy submounts too */
663 		if (nr_mounts < 1)
664 			_exit(7);
665 
666 		close(fd);
667 		_exit(0);
668 	}
669 
670 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
671 	ASSERT_TRUE(WIFEXITED(status));
672 
673 	switch (WEXITSTATUS(status)) {
674 	case 0:
675 		/* Success */
676 		break;
677 	case 1:
678 		ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE|AT_RECURSIVE) failed in userns");
679 		break;
680 	case 2:
681 		SKIP(return, "setup_userns failed");
682 		break;
683 	case 4:
684 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
685 		break;
686 	case 5:
687 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
688 		break;
689 	case 6:
690 		ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace");
691 		break;
692 	case 7:
693 		ASSERT_FALSE(true) TH_LOG("New namespace has no mounts");
694 		break;
695 	default:
696 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
697 					  WEXITSTATUS(status));
698 		break;
699 	}
700 }
701 
TEST_F(open_tree_ns_userns,umount_fails_einval)702 TEST_F(open_tree_ns_userns, umount_fails_einval)
703 {
704 	pid_t pid;
705 	int status;
706 
707 	pid = fork();
708 	ASSERT_GE(pid, 0);
709 
710 	if (pid == 0) {
711 		uint64_t new_ns_id;
712 		uint64_t list[256];
713 		ssize_t nr_mounts;
714 		int fd;
715 		ssize_t i;
716 
717 		/* Create new user namespace */
718 		if (enter_userns() != 0)
719 			_exit(2);
720 
721 		fd = sys_open_tree(AT_FDCWD, "/",
722 				   OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
723 		if (fd < 0) {
724 			if (errno == EINVAL)
725 				_exit(4);
726 			_exit(1);
727 		}
728 
729 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
730 			_exit(5);
731 
732 		/* Get all mounts in the new namespace */
733 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
734 		if (nr_mounts < 0)
735 			_exit(9);
736 
737 		if (nr_mounts < 1)
738 			_exit(10);
739 
740 		/* Enter the new namespace */
741 		if (setns(fd, CLONE_NEWNS) < 0)
742 			_exit(6);
743 
744 		for (i = 0; i < nr_mounts; i++) {
745 			struct statmount *sm;
746 			const char *mnt_point;
747 
748 			sm = statmount_alloc(list[i], new_ns_id,
749 					     STATMOUNT_MNT_POINT);
750 			if (!sm)
751 				_exit(11);
752 
753 			mnt_point = sm->str + sm->mnt_point;
754 
755 			TH_LOG("Trying to umount %s", mnt_point);
756 			if (umount2(mnt_point, MNT_DETACH) == 0) {
757 				free(sm);
758 				_exit(7);
759 			}
760 
761 			if (errno != EINVAL) {
762 				/* Wrong error */
763 				free(sm);
764 				_exit(8);
765 			}
766 
767 			free(sm);
768 		}
769 
770 		close(fd);
771 		_exit(0);
772 	}
773 
774 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
775 	ASSERT_TRUE(WIFEXITED(status));
776 
777 	switch (WEXITSTATUS(status)) {
778 	case 0:
779 		break;
780 	case 1:
781 		ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed");
782 		break;
783 	case 2:
784 		SKIP(return, "setup_userns failed");
785 		break;
786 	case 4:
787 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
788 		break;
789 	case 5:
790 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
791 		break;
792 	case 6:
793 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
794 		break;
795 	case 7:
796 		ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL");
797 		break;
798 	case 8:
799 		ASSERT_FALSE(true) TH_LOG("umount failed with wrong error (expected EINVAL)");
800 		break;
801 	case 9:
802 		ASSERT_FALSE(true) TH_LOG("listmount failed");
803 		break;
804 	case 10:
805 		ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
806 		break;
807 	case 11:
808 		ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
809 		break;
810 	default:
811 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
812 					  WEXITSTATUS(status));
813 		break;
814 	}
815 }
816 
TEST_F(open_tree_ns_userns,umount_succeeds)817 TEST_F(open_tree_ns_userns, umount_succeeds)
818 {
819 	pid_t pid;
820 	int status;
821 
822 	pid = fork();
823 	ASSERT_GE(pid, 0);
824 
825 	if (pid == 0) {
826 		uint64_t new_ns_id;
827 		uint64_t list[256];
828 		ssize_t nr_mounts;
829 		int fd;
830 		ssize_t i;
831 
832 		if (unshare(CLONE_NEWNS))
833 			_exit(1);
834 
835 		if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) != 0)
836 			_exit(1);
837 
838 		fd = sys_open_tree(AT_FDCWD, "/",
839 				   OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
840 		if (fd < 0) {
841 			if (errno == EINVAL)
842 				_exit(4);
843 			_exit(1);
844 		}
845 
846 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
847 			_exit(5);
848 
849 		/* Get all mounts in the new namespace */
850 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
851 		if (nr_mounts < 0)
852 			_exit(9);
853 
854 		if (nr_mounts < 1)
855 			_exit(10);
856 
857 		/* Enter the new namespace */
858 		if (setns(fd, CLONE_NEWNS) < 0)
859 			_exit(6);
860 
861 		for (i = 0; i < nr_mounts; i++) {
862 			struct statmount *sm;
863 			const char *mnt_point;
864 
865 			sm = statmount_alloc(list[i], new_ns_id,
866 					     STATMOUNT_MNT_POINT);
867 			if (!sm)
868 				_exit(11);
869 
870 			mnt_point = sm->str + sm->mnt_point;
871 
872 			TH_LOG("Trying to umount %s", mnt_point);
873 			if (umount2(mnt_point, MNT_DETACH) != 0) {
874 				free(sm);
875 				_exit(7);
876 			}
877 
878 			free(sm);
879 		}
880 
881 		close(fd);
882 		_exit(0);
883 	}
884 
885 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
886 	ASSERT_TRUE(WIFEXITED(status));
887 
888 	switch (WEXITSTATUS(status)) {
889 	case 0:
890 		break;
891 	case 1:
892 		ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed");
893 		break;
894 	case 2:
895 		SKIP(return, "setup_userns failed");
896 		break;
897 	case 4:
898 		SKIP(return, "OPEN_TREE_NAMESPACE not supported");
899 		break;
900 	case 5:
901 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
902 		break;
903 	case 6:
904 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
905 		break;
906 	case 7:
907 		ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL");
908 		break;
909 	case 9:
910 		ASSERT_FALSE(true) TH_LOG("listmount failed");
911 		break;
912 	case 10:
913 		ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
914 		break;
915 	case 11:
916 		ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
917 		break;
918 	default:
919 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
920 					  WEXITSTATUS(status));
921 		break;
922 	}
923 }
924 
FIXTURE(open_tree_ns_unbindable)925 FIXTURE(open_tree_ns_unbindable)
926 {
927 	char tmpdir[PATH_MAX];
928 	bool mounted;
929 };
930 
FIXTURE_SETUP(open_tree_ns_unbindable)931 FIXTURE_SETUP(open_tree_ns_unbindable)
932 {
933 	int ret;
934 
935 	self->mounted = false;
936 
937 	/* Check if open_tree syscall is supported */
938 	ret = sys_open_tree(-1, NULL, 0);
939 	if (ret == -1 && errno == ENOSYS)
940 		SKIP(return, "open_tree() syscall not supported");
941 
942 	/* Create a temporary directory for the test mount */
943 	snprintf(self->tmpdir, sizeof(self->tmpdir),
944 		 "/tmp/open_tree_ns_test.XXXXXX");
945 	ASSERT_NE(mkdtemp(self->tmpdir), NULL);
946 
947 	/* Mount tmpfs there */
948 	ret = mount("tmpfs", self->tmpdir, "tmpfs", 0, NULL);
949 	if (ret < 0) {
950 		rmdir(self->tmpdir);
951 		SKIP(return, "Failed to mount tmpfs");
952 	}
953 	self->mounted = true;
954 
955 	ret = mount(NULL, self->tmpdir, NULL, MS_UNBINDABLE, NULL);
956 	if (ret < 0) {
957 		rmdir(self->tmpdir);
958 		SKIP(return, "Failed to make tmpfs unbindable");
959 	}
960 }
961 
FIXTURE_TEARDOWN(open_tree_ns_unbindable)962 FIXTURE_TEARDOWN(open_tree_ns_unbindable)
963 {
964 	if (self->mounted)
965 		umount2(self->tmpdir, MNT_DETACH);
966 	rmdir(self->tmpdir);
967 }
968 
TEST_F(open_tree_ns_unbindable,fails_on_unbindable)969 TEST_F(open_tree_ns_unbindable, fails_on_unbindable)
970 {
971 	int fd;
972 
973 	fd = sys_open_tree(AT_FDCWD, self->tmpdir,
974 			   OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
975 	ASSERT_LT(fd, 0);
976 }
977 
TEST_F(open_tree_ns_unbindable,recursive_skips_on_unbindable)978 TEST_F(open_tree_ns_unbindable, recursive_skips_on_unbindable)
979 {
980 	uint64_t new_ns_id;
981 	uint64_t list[256];
982 	ssize_t nr_mounts;
983 	int fd;
984 	ssize_t i;
985 	bool found_unbindable = false;
986 
987 	fd = sys_open_tree(AT_FDCWD, "/",
988 			   OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
989 	ASSERT_GT(fd, 0);
990 
991 	ASSERT_EQ(get_mnt_ns_id(fd, &new_ns_id), 0);
992 
993 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
994 	ASSERT_GE(nr_mounts, 0) {
995 		TH_LOG("listmount failed: %m");
996 	}
997 
998 	/*
999 	 * Iterate through all mounts in the new namespace and verify
1000 	 * the unbindable tmpfs mount was silently dropped.
1001 	 */
1002 	for (i = 0; i < nr_mounts; i++) {
1003 		struct statmount *sm;
1004 		const char *mnt_point;
1005 
1006 		sm = statmount_alloc(list[i], new_ns_id, STATMOUNT_MNT_POINT);
1007 		ASSERT_NE(sm, NULL) {
1008 			TH_LOG("statmount_alloc failed for mnt_id %llu",
1009 			       (unsigned long long)list[i]);
1010 		}
1011 
1012 		mnt_point = sm->str + sm->mnt_point;
1013 
1014 		if (strcmp(mnt_point, self->tmpdir) == 0) {
1015 			TH_LOG("Found unbindable mount at %s (should have been dropped)",
1016 			       mnt_point);
1017 			found_unbindable = true;
1018 		}
1019 
1020 		free(sm);
1021 	}
1022 
1023 	ASSERT_FALSE(found_unbindable) {
1024 		TH_LOG("Unbindable mount at %s was not dropped", self->tmpdir);
1025 	}
1026 
1027 	close(fd);
1028 }
1029 
1030 TEST_HARNESS_MAIN
1031