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