132f54f2bSChristian Brauner // SPDX-License-Identifier: GPL-2.0-or-later 232f54f2bSChristian Brauner /* 332f54f2bSChristian Brauner * Tests for empty mount namespace creation via UNSHARE_EMPTY_MNTNS 432f54f2bSChristian Brauner * 532f54f2bSChristian Brauner * Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 632f54f2bSChristian Brauner */ 732f54f2bSChristian Brauner 832f54f2bSChristian Brauner #define _GNU_SOURCE 932f54f2bSChristian Brauner #include <fcntl.h> 1032f54f2bSChristian Brauner #include <linux/mount.h> 1132f54f2bSChristian Brauner #include <linux/stat.h> 1232f54f2bSChristian Brauner #include <sched.h> 1332f54f2bSChristian Brauner #include <stdio.h> 1432f54f2bSChristian Brauner #include <string.h> 1532f54f2bSChristian Brauner #include <sys/mount.h> 1632f54f2bSChristian Brauner #include <sys/stat.h> 1732f54f2bSChristian Brauner #include <sys/types.h> 1832f54f2bSChristian Brauner #include <sys/wait.h> 1932f54f2bSChristian Brauner #include <unistd.h> 2032f54f2bSChristian Brauner 2132f54f2bSChristian Brauner #include "../utils.h" 2232f54f2bSChristian Brauner #include "../wrappers.h" 2332f54f2bSChristian Brauner #include "empty_mntns.h" 2432f54f2bSChristian Brauner #include "kselftest_harness.h" 2532f54f2bSChristian Brauner 2632f54f2bSChristian Brauner static bool unshare_empty_mntns_supported(void) 2732f54f2bSChristian Brauner { 2832f54f2bSChristian Brauner pid_t pid; 2932f54f2bSChristian Brauner int status; 3032f54f2bSChristian Brauner 3132f54f2bSChristian Brauner pid = fork(); 3232f54f2bSChristian Brauner if (pid < 0) 3332f54f2bSChristian Brauner return false; 3432f54f2bSChristian Brauner 3532f54f2bSChristian Brauner if (pid == 0) { 3632f54f2bSChristian Brauner if (enter_userns()) 3732f54f2bSChristian Brauner _exit(1); 3832f54f2bSChristian Brauner 3932f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS) && errno == EINVAL) 4032f54f2bSChristian Brauner _exit(1); 4132f54f2bSChristian Brauner _exit(0); 4232f54f2bSChristian Brauner } 4332f54f2bSChristian Brauner 4432f54f2bSChristian Brauner if (waitpid(pid, &status, 0) != pid) 4532f54f2bSChristian Brauner return false; 4632f54f2bSChristian Brauner 4732f54f2bSChristian Brauner if (!WIFEXITED(status)) 4832f54f2bSChristian Brauner return false; 4932f54f2bSChristian Brauner 5032f54f2bSChristian Brauner return WEXITSTATUS(status) == 0; 5132f54f2bSChristian Brauner } 5232f54f2bSChristian Brauner 5332f54f2bSChristian Brauner 5432f54f2bSChristian Brauner FIXTURE(empty_mntns) {}; 5532f54f2bSChristian Brauner 5632f54f2bSChristian Brauner FIXTURE_SETUP(empty_mntns) 5732f54f2bSChristian Brauner { 5832f54f2bSChristian Brauner if (!unshare_empty_mntns_supported()) 5932f54f2bSChristian Brauner SKIP(return, "UNSHARE_EMPTY_MNTNS not supported"); 6032f54f2bSChristian Brauner } 6132f54f2bSChristian Brauner 6232f54f2bSChristian Brauner FIXTURE_TEARDOWN(empty_mntns) {} 6332f54f2bSChristian Brauner 6432f54f2bSChristian Brauner /* Verify unshare succeeds, produces exactly 1 mount, and root == cwd */ 6532f54f2bSChristian Brauner TEST_F(empty_mntns, basic) 6632f54f2bSChristian Brauner { 6732f54f2bSChristian Brauner pid_t pid; 6832f54f2bSChristian Brauner 6932f54f2bSChristian Brauner pid = fork(); 7032f54f2bSChristian Brauner ASSERT_GE(pid, 0); 7132f54f2bSChristian Brauner 7232f54f2bSChristian Brauner if (pid == 0) { 7332f54f2bSChristian Brauner uint64_t root_id, cwd_id; 7432f54f2bSChristian Brauner 7532f54f2bSChristian Brauner if (enter_userns()) 7632f54f2bSChristian Brauner _exit(1); 7732f54f2bSChristian Brauner 7832f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 7932f54f2bSChristian Brauner _exit(2); 8032f54f2bSChristian Brauner 8132f54f2bSChristian Brauner if (count_mounts() != 1) 8232f54f2bSChristian Brauner _exit(3); 8332f54f2bSChristian Brauner 8432f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 8532f54f2bSChristian Brauner cwd_id = get_unique_mnt_id("."); 8632f54f2bSChristian Brauner if (root_id == 0 || cwd_id == 0) 8732f54f2bSChristian Brauner _exit(4); 8832f54f2bSChristian Brauner 8932f54f2bSChristian Brauner if (root_id != cwd_id) 9032f54f2bSChristian Brauner _exit(5); 9132f54f2bSChristian Brauner 9232f54f2bSChristian Brauner _exit(0); 9332f54f2bSChristian Brauner } 9432f54f2bSChristian Brauner 9532f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 9632f54f2bSChristian Brauner } 9732f54f2bSChristian Brauner 9832f54f2bSChristian Brauner /* 9932f54f2bSChristian Brauner * UNSHARE_EMPTY_MNTNS combined with CLONE_NEWUSER. 10032f54f2bSChristian Brauner * 10132f54f2bSChristian Brauner * The user namespace must be created first so /proc is still accessible 10232f54f2bSChristian Brauner * for writing uid_map/gid_map. The empty mount namespace is created 10332f54f2bSChristian Brauner * afterwards. 10432f54f2bSChristian Brauner */ 10532f54f2bSChristian Brauner TEST_F(empty_mntns, with_clone_newuser) 10632f54f2bSChristian Brauner { 10732f54f2bSChristian Brauner pid_t pid; 10832f54f2bSChristian Brauner 10932f54f2bSChristian Brauner pid = fork(); 11032f54f2bSChristian Brauner ASSERT_GE(pid, 0); 11132f54f2bSChristian Brauner 11232f54f2bSChristian Brauner if (pid == 0) { 11332f54f2bSChristian Brauner uid_t uid = getuid(); 11432f54f2bSChristian Brauner gid_t gid = getgid(); 11532f54f2bSChristian Brauner char map[100]; 11632f54f2bSChristian Brauner 11732f54f2bSChristian Brauner if (unshare(CLONE_NEWUSER)) 11832f54f2bSChristian Brauner _exit(1); 11932f54f2bSChristian Brauner 12032f54f2bSChristian Brauner snprintf(map, sizeof(map), "0 %d 1", uid); 12132f54f2bSChristian Brauner if (write_file("/proc/self/uid_map", map)) 12232f54f2bSChristian Brauner _exit(2); 12332f54f2bSChristian Brauner 12432f54f2bSChristian Brauner if (write_file("/proc/self/setgroups", "deny")) 12532f54f2bSChristian Brauner _exit(3); 12632f54f2bSChristian Brauner 12732f54f2bSChristian Brauner snprintf(map, sizeof(map), "0 %d 1", gid); 12832f54f2bSChristian Brauner if (write_file("/proc/self/gid_map", map)) 12932f54f2bSChristian Brauner _exit(4); 13032f54f2bSChristian Brauner 13132f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 13232f54f2bSChristian Brauner _exit(5); 13332f54f2bSChristian Brauner 13432f54f2bSChristian Brauner if (count_mounts() != 1) 13532f54f2bSChristian Brauner _exit(6); 13632f54f2bSChristian Brauner 13732f54f2bSChristian Brauner _exit(0); 13832f54f2bSChristian Brauner } 13932f54f2bSChristian Brauner 14032f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 14132f54f2bSChristian Brauner } 14232f54f2bSChristian Brauner 14332f54f2bSChristian Brauner /* UNSHARE_EMPTY_MNTNS combined with other namespace flags */ 14432f54f2bSChristian Brauner TEST_F(empty_mntns, with_other_ns_flags) 14532f54f2bSChristian Brauner { 14632f54f2bSChristian Brauner pid_t pid; 14732f54f2bSChristian Brauner 14832f54f2bSChristian Brauner pid = fork(); 14932f54f2bSChristian Brauner ASSERT_GE(pid, 0); 15032f54f2bSChristian Brauner 15132f54f2bSChristian Brauner if (pid == 0) { 15232f54f2bSChristian Brauner if (enter_userns()) 15332f54f2bSChristian Brauner _exit(1); 15432f54f2bSChristian Brauner 15532f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS | CLONE_NEWUTS | CLONE_NEWIPC)) 15632f54f2bSChristian Brauner _exit(2); 15732f54f2bSChristian Brauner 15832f54f2bSChristian Brauner if (count_mounts() != 1) 15932f54f2bSChristian Brauner _exit(3); 16032f54f2bSChristian Brauner 16132f54f2bSChristian Brauner _exit(0); 16232f54f2bSChristian Brauner } 16332f54f2bSChristian Brauner 16432f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 16532f54f2bSChristian Brauner } 16632f54f2bSChristian Brauner 16732f54f2bSChristian Brauner /* EPERM without proper capabilities */ 16832f54f2bSChristian Brauner TEST_F(empty_mntns, eperm_without_caps) 16932f54f2bSChristian Brauner { 17032f54f2bSChristian Brauner pid_t pid; 17132f54f2bSChristian Brauner 17232f54f2bSChristian Brauner pid = fork(); 17332f54f2bSChristian Brauner ASSERT_GE(pid, 0); 17432f54f2bSChristian Brauner 17532f54f2bSChristian Brauner if (pid == 0) { 17632f54f2bSChristian Brauner /* Skip if already root */ 17732f54f2bSChristian Brauner if (getuid() == 0) 17832f54f2bSChristian Brauner _exit(0); 17932f54f2bSChristian Brauner 18032f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS) == 0) 18132f54f2bSChristian Brauner _exit(1); 18232f54f2bSChristian Brauner 18332f54f2bSChristian Brauner if (errno != EPERM) 18432f54f2bSChristian Brauner _exit(2); 18532f54f2bSChristian Brauner 18632f54f2bSChristian Brauner _exit(0); 18732f54f2bSChristian Brauner } 18832f54f2bSChristian Brauner 18932f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 19032f54f2bSChristian Brauner } 19132f54f2bSChristian Brauner 19232f54f2bSChristian Brauner /* Many source mounts still result in exactly 1 mount */ 19332f54f2bSChristian Brauner TEST_F(empty_mntns, many_source_mounts) 19432f54f2bSChristian Brauner { 19532f54f2bSChristian Brauner pid_t pid; 19632f54f2bSChristian Brauner 19732f54f2bSChristian Brauner pid = fork(); 19832f54f2bSChristian Brauner ASSERT_GE(pid, 0); 19932f54f2bSChristian Brauner 20032f54f2bSChristian Brauner if (pid == 0) { 20132f54f2bSChristian Brauner char tmpdir[] = "/tmp/empty_mntns_test.XXXXXX"; 20232f54f2bSChristian Brauner int i; 20332f54f2bSChristian Brauner 20432f54f2bSChristian Brauner if (enter_userns()) 20532f54f2bSChristian Brauner _exit(1); 20632f54f2bSChristian Brauner 20732f54f2bSChristian Brauner if (unshare(CLONE_NEWNS)) 20832f54f2bSChristian Brauner _exit(2); 20932f54f2bSChristian Brauner 21032f54f2bSChristian Brauner if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) 21132f54f2bSChristian Brauner _exit(3); 21232f54f2bSChristian Brauner 21332f54f2bSChristian Brauner if (!mkdtemp(tmpdir)) 21432f54f2bSChristian Brauner _exit(4); 21532f54f2bSChristian Brauner 21632f54f2bSChristian Brauner if (mount("tmpfs", tmpdir, "tmpfs", 0, "size=1M")) 21732f54f2bSChristian Brauner _exit(5); 21832f54f2bSChristian Brauner 21932f54f2bSChristian Brauner for (i = 0; i < 5; i++) { 22032f54f2bSChristian Brauner char subdir[256]; 22132f54f2bSChristian Brauner 22232f54f2bSChristian Brauner snprintf(subdir, sizeof(subdir), "%s/sub%d", tmpdir, i); 22332f54f2bSChristian Brauner if (mkdir(subdir, 0755) && errno != EEXIST) 22432f54f2bSChristian Brauner _exit(6); 22532f54f2bSChristian Brauner if (mount(subdir, subdir, NULL, MS_BIND, NULL)) 22632f54f2bSChristian Brauner _exit(7); 22732f54f2bSChristian Brauner } 22832f54f2bSChristian Brauner 22932f54f2bSChristian Brauner if (count_mounts() < 5) 23032f54f2bSChristian Brauner _exit(8); 23132f54f2bSChristian Brauner 23232f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 23332f54f2bSChristian Brauner _exit(9); 23432f54f2bSChristian Brauner 23532f54f2bSChristian Brauner if (count_mounts() != 1) 23632f54f2bSChristian Brauner _exit(10); 23732f54f2bSChristian Brauner 23832f54f2bSChristian Brauner _exit(0); 23932f54f2bSChristian Brauner } 24032f54f2bSChristian Brauner 24132f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 24232f54f2bSChristian Brauner } 24332f54f2bSChristian Brauner 24432f54f2bSChristian Brauner /* CWD on a different mount gets reset to root */ 24532f54f2bSChristian Brauner TEST_F(empty_mntns, cwd_reset) 24632f54f2bSChristian Brauner { 24732f54f2bSChristian Brauner pid_t pid; 24832f54f2bSChristian Brauner 24932f54f2bSChristian Brauner pid = fork(); 25032f54f2bSChristian Brauner ASSERT_GE(pid, 0); 25132f54f2bSChristian Brauner 25232f54f2bSChristian Brauner if (pid == 0) { 25332f54f2bSChristian Brauner char tmpdir[] = "/tmp/empty_mntns_cwd.XXXXXX"; 25432f54f2bSChristian Brauner uint64_t root_id, cwd_id; 25532f54f2bSChristian Brauner struct statmount *sm; 25632f54f2bSChristian Brauner 25732f54f2bSChristian Brauner if (enter_userns()) 25832f54f2bSChristian Brauner _exit(1); 25932f54f2bSChristian Brauner 26032f54f2bSChristian Brauner if (unshare(CLONE_NEWNS)) 26132f54f2bSChristian Brauner _exit(2); 26232f54f2bSChristian Brauner 26332f54f2bSChristian Brauner if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) 26432f54f2bSChristian Brauner _exit(3); 26532f54f2bSChristian Brauner 26632f54f2bSChristian Brauner if (!mkdtemp(tmpdir)) 26732f54f2bSChristian Brauner _exit(4); 26832f54f2bSChristian Brauner 26932f54f2bSChristian Brauner if (mount("tmpfs", tmpdir, "tmpfs", 0, "size=1M")) 27032f54f2bSChristian Brauner _exit(5); 27132f54f2bSChristian Brauner 27232f54f2bSChristian Brauner if (chdir(tmpdir)) 27332f54f2bSChristian Brauner _exit(6); 27432f54f2bSChristian Brauner 27532f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 27632f54f2bSChristian Brauner _exit(7); 27732f54f2bSChristian Brauner 27832f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 27932f54f2bSChristian Brauner cwd_id = get_unique_mnt_id("."); 28032f54f2bSChristian Brauner if (root_id == 0 || cwd_id == 0) 28132f54f2bSChristian Brauner _exit(8); 28232f54f2bSChristian Brauner 28332f54f2bSChristian Brauner if (root_id != cwd_id) 28432f54f2bSChristian Brauner _exit(9); 28532f54f2bSChristian Brauner 286*1a398a23SChristian Brauner sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, 0); 28732f54f2bSChristian Brauner if (!sm) 28832f54f2bSChristian Brauner _exit(10); 28932f54f2bSChristian Brauner 29032f54f2bSChristian Brauner if (strcmp(sm->str + sm->mnt_point, "/") != 0) 29132f54f2bSChristian Brauner _exit(11); 29232f54f2bSChristian Brauner 29332f54f2bSChristian Brauner free(sm); 29432f54f2bSChristian Brauner _exit(0); 29532f54f2bSChristian Brauner } 29632f54f2bSChristian Brauner 29732f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 29832f54f2bSChristian Brauner } 29932f54f2bSChristian Brauner 30032f54f2bSChristian Brauner /* Verify statmount properties of the root mount */ 30132f54f2bSChristian Brauner TEST_F(empty_mntns, mount_properties) 30232f54f2bSChristian Brauner { 30332f54f2bSChristian Brauner pid_t pid; 30432f54f2bSChristian Brauner 30532f54f2bSChristian Brauner pid = fork(); 30632f54f2bSChristian Brauner ASSERT_GE(pid, 0); 30732f54f2bSChristian Brauner 30832f54f2bSChristian Brauner if (pid == 0) { 30932f54f2bSChristian Brauner struct statmount *sm; 31032f54f2bSChristian Brauner uint64_t root_id; 31132f54f2bSChristian Brauner 31232f54f2bSChristian Brauner if (enter_userns()) 31332f54f2bSChristian Brauner _exit(1); 31432f54f2bSChristian Brauner 31532f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 31632f54f2bSChristian Brauner _exit(2); 31732f54f2bSChristian Brauner 31832f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 31932f54f2bSChristian Brauner if (!root_id) 32032f54f2bSChristian Brauner _exit(3); 32132f54f2bSChristian Brauner 32232f54f2bSChristian Brauner sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_ROOT | 323*1a398a23SChristian Brauner STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE, 0); 32432f54f2bSChristian Brauner if (!sm) 32532f54f2bSChristian Brauner _exit(4); 32632f54f2bSChristian Brauner 32732f54f2bSChristian Brauner if (!(sm->mask & STATMOUNT_MNT_POINT)) 32832f54f2bSChristian Brauner _exit(5); 32932f54f2bSChristian Brauner 33032f54f2bSChristian Brauner if (strcmp(sm->str + sm->mnt_point, "/") != 0) 33132f54f2bSChristian Brauner _exit(6); 33232f54f2bSChristian Brauner 33332f54f2bSChristian Brauner if (!(sm->mask & STATMOUNT_MNT_BASIC)) 33432f54f2bSChristian Brauner _exit(7); 33532f54f2bSChristian Brauner 33632f54f2bSChristian Brauner if (sm->mnt_id != root_id) 33732f54f2bSChristian Brauner _exit(8); 33832f54f2bSChristian Brauner 33932f54f2bSChristian Brauner free(sm); 34032f54f2bSChristian Brauner _exit(0); 34132f54f2bSChristian Brauner } 34232f54f2bSChristian Brauner 34332f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 34432f54f2bSChristian Brauner } 34532f54f2bSChristian Brauner 34632f54f2bSChristian Brauner /* Consecutive UNSHARE_EMPTY_MNTNS calls produce new namespaces */ 34732f54f2bSChristian Brauner TEST_F(empty_mntns, repeated_unshare) 34832f54f2bSChristian Brauner { 34932f54f2bSChristian Brauner pid_t pid; 35032f54f2bSChristian Brauner 35132f54f2bSChristian Brauner pid = fork(); 35232f54f2bSChristian Brauner ASSERT_GE(pid, 0); 35332f54f2bSChristian Brauner 35432f54f2bSChristian Brauner if (pid == 0) { 35532f54f2bSChristian Brauner uint64_t first_root_id, second_root_id; 35632f54f2bSChristian Brauner 35732f54f2bSChristian Brauner if (enter_userns()) 35832f54f2bSChristian Brauner _exit(1); 35932f54f2bSChristian Brauner 36032f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 36132f54f2bSChristian Brauner _exit(2); 36232f54f2bSChristian Brauner 36332f54f2bSChristian Brauner if (count_mounts() != 1) 36432f54f2bSChristian Brauner _exit(3); 36532f54f2bSChristian Brauner 36632f54f2bSChristian Brauner first_root_id = get_unique_mnt_id("/"); 36732f54f2bSChristian Brauner 36832f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 36932f54f2bSChristian Brauner _exit(4); 37032f54f2bSChristian Brauner 37132f54f2bSChristian Brauner if (count_mounts() != 1) 37232f54f2bSChristian Brauner _exit(5); 37332f54f2bSChristian Brauner 37432f54f2bSChristian Brauner second_root_id = get_unique_mnt_id("/"); 37532f54f2bSChristian Brauner 37632f54f2bSChristian Brauner if (first_root_id == second_root_id) 37732f54f2bSChristian Brauner _exit(6); 37832f54f2bSChristian Brauner 37932f54f2bSChristian Brauner _exit(0); 38032f54f2bSChristian Brauner } 38132f54f2bSChristian Brauner 38232f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 38332f54f2bSChristian Brauner } 38432f54f2bSChristian Brauner 38532f54f2bSChristian Brauner /* Root mount's parent is itself */ 38632f54f2bSChristian Brauner TEST_F(empty_mntns, root_is_own_parent) 38732f54f2bSChristian Brauner { 38832f54f2bSChristian Brauner pid_t pid; 38932f54f2bSChristian Brauner 39032f54f2bSChristian Brauner pid = fork(); 39132f54f2bSChristian Brauner ASSERT_GE(pid, 0); 39232f54f2bSChristian Brauner 39332f54f2bSChristian Brauner if (pid == 0) { 39432f54f2bSChristian Brauner struct statmount sm; 39532f54f2bSChristian Brauner uint64_t root_id; 39632f54f2bSChristian Brauner 39732f54f2bSChristian Brauner if (enter_userns()) 39832f54f2bSChristian Brauner _exit(1); 39932f54f2bSChristian Brauner 40032f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 40132f54f2bSChristian Brauner _exit(2); 40232f54f2bSChristian Brauner 40332f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 40432f54f2bSChristian Brauner if (!root_id) 40532f54f2bSChristian Brauner _exit(3); 40632f54f2bSChristian Brauner 40732f54f2bSChristian Brauner if (statmount(root_id, 0, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0) < 0) 40832f54f2bSChristian Brauner _exit(4); 40932f54f2bSChristian Brauner 41032f54f2bSChristian Brauner if (!(sm.mask & STATMOUNT_MNT_BASIC)) 41132f54f2bSChristian Brauner _exit(5); 41232f54f2bSChristian Brauner 41332f54f2bSChristian Brauner if (sm.mnt_parent_id != sm.mnt_id) 41432f54f2bSChristian Brauner _exit(6); 41532f54f2bSChristian Brauner 41632f54f2bSChristian Brauner _exit(0); 41732f54f2bSChristian Brauner } 41832f54f2bSChristian Brauner 41932f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 42032f54f2bSChristian Brauner } 42132f54f2bSChristian Brauner 42232f54f2bSChristian Brauner /* Listmount returns only the root mount */ 42332f54f2bSChristian Brauner TEST_F(empty_mntns, listmount_single_entry) 42432f54f2bSChristian Brauner { 42532f54f2bSChristian Brauner pid_t pid; 42632f54f2bSChristian Brauner 42732f54f2bSChristian Brauner pid = fork(); 42832f54f2bSChristian Brauner ASSERT_GE(pid, 0); 42932f54f2bSChristian Brauner 43032f54f2bSChristian Brauner if (pid == 0) { 43132f54f2bSChristian Brauner uint64_t list[16]; 43232f54f2bSChristian Brauner ssize_t nr_mounts; 43332f54f2bSChristian Brauner uint64_t root_id; 43432f54f2bSChristian Brauner 43532f54f2bSChristian Brauner if (enter_userns()) 43632f54f2bSChristian Brauner _exit(1); 43732f54f2bSChristian Brauner 43832f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 43932f54f2bSChristian Brauner _exit(2); 44032f54f2bSChristian Brauner 44132f54f2bSChristian Brauner nr_mounts = listmount(LSMT_ROOT, 0, 0, list, 16, 0); 44232f54f2bSChristian Brauner if (nr_mounts != 1) 44332f54f2bSChristian Brauner _exit(3); 44432f54f2bSChristian Brauner 44532f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 44632f54f2bSChristian Brauner if (!root_id) 44732f54f2bSChristian Brauner _exit(4); 44832f54f2bSChristian Brauner 44932f54f2bSChristian Brauner if (list[0] != root_id) 45032f54f2bSChristian Brauner _exit(5); 45132f54f2bSChristian Brauner 45232f54f2bSChristian Brauner _exit(0); 45332f54f2bSChristian Brauner } 45432f54f2bSChristian Brauner 45532f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 45632f54f2bSChristian Brauner } 45732f54f2bSChristian Brauner 45832f54f2bSChristian Brauner /* 45932f54f2bSChristian Brauner * Mount tmpfs over nullfs root to build a writable filesystem from scratch. 46032f54f2bSChristian Brauner * This exercises the intended usage pattern: create an empty mount namespace 46132f54f2bSChristian Brauner * (which has a nullfs root), then mount a real filesystem over it. 46232f54f2bSChristian Brauner * 46332f54f2bSChristian Brauner * Because resolving "/" returns the process root directly (via nd_jump_root) 46432f54f2bSChristian Brauner * without following overmounts, we use the new mount API (fsopen/fsmount) 46532f54f2bSChristian Brauner * to obtain a mount fd, then fchdir + chroot to enter the new filesystem. 46632f54f2bSChristian Brauner */ 46732f54f2bSChristian Brauner TEST_F(empty_mntns, overmount_tmpfs) 46832f54f2bSChristian Brauner { 46932f54f2bSChristian Brauner pid_t pid; 47032f54f2bSChristian Brauner 47132f54f2bSChristian Brauner pid = fork(); 47232f54f2bSChristian Brauner ASSERT_GE(pid, 0); 47332f54f2bSChristian Brauner 47432f54f2bSChristian Brauner if (pid == 0) { 47532f54f2bSChristian Brauner struct statmount *sm; 47632f54f2bSChristian Brauner uint64_t root_id, cwd_id; 47732f54f2bSChristian Brauner int fd, fsfd, mntfd; 47832f54f2bSChristian Brauner 47932f54f2bSChristian Brauner if (enter_userns()) 48032f54f2bSChristian Brauner _exit(1); 48132f54f2bSChristian Brauner 48232f54f2bSChristian Brauner if (unshare(UNSHARE_EMPTY_MNTNS)) 48332f54f2bSChristian Brauner _exit(2); 48432f54f2bSChristian Brauner 48532f54f2bSChristian Brauner if (count_mounts() != 1) 48632f54f2bSChristian Brauner _exit(3); 48732f54f2bSChristian Brauner 48832f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 48932f54f2bSChristian Brauner if (!root_id) 49032f54f2bSChristian Brauner _exit(4); 49132f54f2bSChristian Brauner 49232f54f2bSChristian Brauner /* Verify root is nullfs */ 493*1a398a23SChristian Brauner sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0); 49432f54f2bSChristian Brauner if (!sm) 49532f54f2bSChristian Brauner _exit(5); 49632f54f2bSChristian Brauner 49732f54f2bSChristian Brauner if (!(sm->mask & STATMOUNT_FS_TYPE)) 49832f54f2bSChristian Brauner _exit(6); 49932f54f2bSChristian Brauner 50032f54f2bSChristian Brauner if (strcmp(sm->str + sm->fs_type, "nullfs") != 0) 50132f54f2bSChristian Brauner _exit(7); 50232f54f2bSChristian Brauner 50332f54f2bSChristian Brauner free(sm); 50432f54f2bSChristian Brauner 50532f54f2bSChristian Brauner cwd_id = get_unique_mnt_id("."); 50632f54f2bSChristian Brauner if (!cwd_id || root_id != cwd_id) 50732f54f2bSChristian Brauner _exit(8); 50832f54f2bSChristian Brauner 50932f54f2bSChristian Brauner /* 51032f54f2bSChristian Brauner * nullfs root is immutable. open(O_CREAT) returns ENOENT 51132f54f2bSChristian Brauner * because empty_dir_lookup() returns -ENOENT before the 51232f54f2bSChristian Brauner * IS_IMMUTABLE permission check in may_o_create() is reached. 51332f54f2bSChristian Brauner */ 51432f54f2bSChristian Brauner fd = open("/test", O_CREAT | O_RDWR, 0644); 51532f54f2bSChristian Brauner if (fd >= 0) { 51632f54f2bSChristian Brauner close(fd); 51732f54f2bSChristian Brauner _exit(9); 51832f54f2bSChristian Brauner } 51932f54f2bSChristian Brauner if (errno != ENOENT) 52032f54f2bSChristian Brauner _exit(10); 52132f54f2bSChristian Brauner 52232f54f2bSChristian Brauner /* 52332f54f2bSChristian Brauner * Use the new mount API to create tmpfs and get a mount fd. 52432f54f2bSChristian Brauner * We need the fd because after attaching the tmpfs on top of 52532f54f2bSChristian Brauner * "/", path resolution of "/" still returns the process root 52632f54f2bSChristian Brauner * (nullfs) without following the overmount. The mount fd 52732f54f2bSChristian Brauner * lets us fchdir + chroot into the tmpfs. 52832f54f2bSChristian Brauner */ 52932f54f2bSChristian Brauner fsfd = sys_fsopen("tmpfs", 0); 53032f54f2bSChristian Brauner if (fsfd < 0) 53132f54f2bSChristian Brauner _exit(11); 53232f54f2bSChristian Brauner 53332f54f2bSChristian Brauner if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "size", "1M", 0)) { 53432f54f2bSChristian Brauner close(fsfd); 53532f54f2bSChristian Brauner _exit(12); 53632f54f2bSChristian Brauner } 53732f54f2bSChristian Brauner 53832f54f2bSChristian Brauner if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) { 53932f54f2bSChristian Brauner close(fsfd); 54032f54f2bSChristian Brauner _exit(13); 54132f54f2bSChristian Brauner } 54232f54f2bSChristian Brauner 54332f54f2bSChristian Brauner mntfd = sys_fsmount(fsfd, 0, 0); 54432f54f2bSChristian Brauner close(fsfd); 54532f54f2bSChristian Brauner if (mntfd < 0) 54632f54f2bSChristian Brauner _exit(14); 54732f54f2bSChristian Brauner 54832f54f2bSChristian Brauner if (sys_move_mount(mntfd, "", AT_FDCWD, "/", 54932f54f2bSChristian Brauner MOVE_MOUNT_F_EMPTY_PATH)) { 55032f54f2bSChristian Brauner close(mntfd); 55132f54f2bSChristian Brauner _exit(15); 55232f54f2bSChristian Brauner } 55332f54f2bSChristian Brauner 55432f54f2bSChristian Brauner if (count_mounts() != 2) { 55532f54f2bSChristian Brauner close(mntfd); 55632f54f2bSChristian Brauner _exit(16); 55732f54f2bSChristian Brauner } 55832f54f2bSChristian Brauner 55932f54f2bSChristian Brauner /* Enter the tmpfs via the mount fd */ 56032f54f2bSChristian Brauner if (fchdir(mntfd)) { 56132f54f2bSChristian Brauner close(mntfd); 56232f54f2bSChristian Brauner _exit(17); 56332f54f2bSChristian Brauner } 56432f54f2bSChristian Brauner 56532f54f2bSChristian Brauner if (chroot(".")) { 56632f54f2bSChristian Brauner close(mntfd); 56732f54f2bSChristian Brauner _exit(18); 56832f54f2bSChristian Brauner } 56932f54f2bSChristian Brauner 57032f54f2bSChristian Brauner close(mntfd); 57132f54f2bSChristian Brauner 57232f54f2bSChristian Brauner /* Verify "/" now resolves to tmpfs */ 57332f54f2bSChristian Brauner root_id = get_unique_mnt_id("/"); 57432f54f2bSChristian Brauner if (!root_id) 57532f54f2bSChristian Brauner _exit(19); 57632f54f2bSChristian Brauner 577*1a398a23SChristian Brauner sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0); 57832f54f2bSChristian Brauner if (!sm) 57932f54f2bSChristian Brauner _exit(20); 58032f54f2bSChristian Brauner 58132f54f2bSChristian Brauner if (!(sm->mask & STATMOUNT_FS_TYPE)) 58232f54f2bSChristian Brauner _exit(21); 58332f54f2bSChristian Brauner 58432f54f2bSChristian Brauner if (strcmp(sm->str + sm->fs_type, "tmpfs") != 0) 58532f54f2bSChristian Brauner _exit(22); 58632f54f2bSChristian Brauner 58732f54f2bSChristian Brauner free(sm); 58832f54f2bSChristian Brauner 58932f54f2bSChristian Brauner /* Verify tmpfs is writable */ 59032f54f2bSChristian Brauner fd = open("/testfile", O_CREAT | O_RDWR, 0644); 59132f54f2bSChristian Brauner if (fd < 0) 59232f54f2bSChristian Brauner _exit(23); 59332f54f2bSChristian Brauner 59432f54f2bSChristian Brauner if (write(fd, "test", 4) != 4) { 59532f54f2bSChristian Brauner close(fd); 59632f54f2bSChristian Brauner _exit(24); 59732f54f2bSChristian Brauner } 59832f54f2bSChristian Brauner 59932f54f2bSChristian Brauner close(fd); 60032f54f2bSChristian Brauner 60132f54f2bSChristian Brauner if (access("/testfile", F_OK)) 60232f54f2bSChristian Brauner _exit(25); 60332f54f2bSChristian Brauner 60432f54f2bSChristian Brauner _exit(0); 60532f54f2bSChristian Brauner } 60632f54f2bSChristian Brauner 60732f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 60832f54f2bSChristian Brauner } 60932f54f2bSChristian Brauner 61032f54f2bSChristian Brauner /* 61132f54f2bSChristian Brauner * Tests below do not require UNSHARE_EMPTY_MNTNS support. 61232f54f2bSChristian Brauner */ 61332f54f2bSChristian Brauner 61432f54f2bSChristian Brauner /* Invalid unshare flags return EINVAL */ 61532f54f2bSChristian Brauner TEST(invalid_flags) 61632f54f2bSChristian Brauner { 61732f54f2bSChristian Brauner pid_t pid; 61832f54f2bSChristian Brauner 61932f54f2bSChristian Brauner pid = fork(); 62032f54f2bSChristian Brauner ASSERT_GE(pid, 0); 62132f54f2bSChristian Brauner 62232f54f2bSChristian Brauner if (pid == 0) { 62332f54f2bSChristian Brauner if (enter_userns()) 62432f54f2bSChristian Brauner _exit(1); 62532f54f2bSChristian Brauner 62632f54f2bSChristian Brauner if (unshare(0x80000000) == 0) 62732f54f2bSChristian Brauner _exit(2); 62832f54f2bSChristian Brauner 62932f54f2bSChristian Brauner if (errno != EINVAL) 63032f54f2bSChristian Brauner _exit(3); 63132f54f2bSChristian Brauner 63232f54f2bSChristian Brauner _exit(0); 63332f54f2bSChristian Brauner } 63432f54f2bSChristian Brauner 63532f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 63632f54f2bSChristian Brauner } 63732f54f2bSChristian Brauner 63832f54f2bSChristian Brauner /* Regular CLONE_NEWNS still copies the full mount tree */ 63932f54f2bSChristian Brauner TEST(clone_newns_full_copy) 64032f54f2bSChristian Brauner { 64132f54f2bSChristian Brauner pid_t pid; 64232f54f2bSChristian Brauner 64332f54f2bSChristian Brauner pid = fork(); 64432f54f2bSChristian Brauner ASSERT_GE(pid, 0); 64532f54f2bSChristian Brauner 64632f54f2bSChristian Brauner if (pid == 0) { 64732f54f2bSChristian Brauner ssize_t nr_mounts_before, nr_mounts_after; 64832f54f2bSChristian Brauner char tmpdir[] = "/tmp/empty_mntns_regr.XXXXXX"; 64932f54f2bSChristian Brauner int i; 65032f54f2bSChristian Brauner 65132f54f2bSChristian Brauner if (enter_userns()) 65232f54f2bSChristian Brauner _exit(1); 65332f54f2bSChristian Brauner 65432f54f2bSChristian Brauner if (unshare(CLONE_NEWNS)) 65532f54f2bSChristian Brauner _exit(2); 65632f54f2bSChristian Brauner 65732f54f2bSChristian Brauner if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) 65832f54f2bSChristian Brauner _exit(3); 65932f54f2bSChristian Brauner 66032f54f2bSChristian Brauner if (!mkdtemp(tmpdir)) 66132f54f2bSChristian Brauner _exit(4); 66232f54f2bSChristian Brauner 66332f54f2bSChristian Brauner if (mount("tmpfs", tmpdir, "tmpfs", 0, "size=1M")) 66432f54f2bSChristian Brauner _exit(5); 66532f54f2bSChristian Brauner 66632f54f2bSChristian Brauner for (i = 0; i < 3; i++) { 66732f54f2bSChristian Brauner char subdir[256]; 66832f54f2bSChristian Brauner 66932f54f2bSChristian Brauner snprintf(subdir, sizeof(subdir), "%s/sub%d", tmpdir, i); 67032f54f2bSChristian Brauner if (mkdir(subdir, 0755) && errno != EEXIST) 67132f54f2bSChristian Brauner _exit(6); 67232f54f2bSChristian Brauner if (mount(subdir, subdir, NULL, MS_BIND, NULL)) 67332f54f2bSChristian Brauner _exit(7); 67432f54f2bSChristian Brauner } 67532f54f2bSChristian Brauner 67632f54f2bSChristian Brauner nr_mounts_before = count_mounts(); 67732f54f2bSChristian Brauner if (nr_mounts_before < 3) 67832f54f2bSChristian Brauner _exit(8); 67932f54f2bSChristian Brauner 68032f54f2bSChristian Brauner if (unshare(CLONE_NEWNS)) 68132f54f2bSChristian Brauner _exit(9); 68232f54f2bSChristian Brauner 68332f54f2bSChristian Brauner nr_mounts_after = count_mounts(); 68432f54f2bSChristian Brauner if (nr_mounts_after < nr_mounts_before) 68532f54f2bSChristian Brauner _exit(10); 68632f54f2bSChristian Brauner 68732f54f2bSChristian Brauner _exit(0); 68832f54f2bSChristian Brauner } 68932f54f2bSChristian Brauner 69032f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 69132f54f2bSChristian Brauner } 69232f54f2bSChristian Brauner 69332f54f2bSChristian Brauner /* Other namespace unshares are unaffected */ 69432f54f2bSChristian Brauner TEST(other_ns_unaffected) 69532f54f2bSChristian Brauner { 69632f54f2bSChristian Brauner pid_t pid; 69732f54f2bSChristian Brauner 69832f54f2bSChristian Brauner pid = fork(); 69932f54f2bSChristian Brauner ASSERT_GE(pid, 0); 70032f54f2bSChristian Brauner 70132f54f2bSChristian Brauner if (pid == 0) { 70232f54f2bSChristian Brauner char hostname[256]; 70332f54f2bSChristian Brauner 70432f54f2bSChristian Brauner if (enter_userns()) 70532f54f2bSChristian Brauner _exit(1); 70632f54f2bSChristian Brauner 70732f54f2bSChristian Brauner if (unshare(CLONE_NEWUTS)) 70832f54f2bSChristian Brauner _exit(2); 70932f54f2bSChristian Brauner 71032f54f2bSChristian Brauner if (sethostname("test-empty-mntns", 16)) 71132f54f2bSChristian Brauner _exit(3); 71232f54f2bSChristian Brauner 71332f54f2bSChristian Brauner if (gethostname(hostname, sizeof(hostname))) 71432f54f2bSChristian Brauner _exit(4); 71532f54f2bSChristian Brauner 71632f54f2bSChristian Brauner if (strcmp(hostname, "test-empty-mntns") != 0) 71732f54f2bSChristian Brauner _exit(5); 71832f54f2bSChristian Brauner 71932f54f2bSChristian Brauner _exit(0); 72032f54f2bSChristian Brauner } 72132f54f2bSChristian Brauner 72232f54f2bSChristian Brauner ASSERT_EQ(wait_for_pid(pid), 0); 72332f54f2bSChristian Brauner } 72432f54f2bSChristian Brauner 72532f54f2bSChristian Brauner TEST_HARNESS_MAIN 726