1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Test: rootfs overmounted multiple times with chroot into topmost 4 * 5 * This test creates a scenario where: 6 * 1. A new mount namespace is created with a tmpfs root (via pivot_root) 7 * 2. A mountpoint is created and overmounted multiple times 8 * 3. The caller chroots into the topmost mount layer 9 * 10 * The test verifies that: 11 * - Multiple overmounts create separate mount layers 12 * - Each layer's files are isolated 13 * - chroot correctly sets the process's root to the topmost layer 14 * - After chroot, only the topmost layer's files are visible 15 * 16 * Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 17 */ 18 19 #define _GNU_SOURCE 20 #include <fcntl.h> 21 #include <linux/mount.h> 22 #include <linux/stat.h> 23 #include <sched.h> 24 #include <stdio.h> 25 #include <string.h> 26 #include <sys/mount.h> 27 #include <sys/stat.h> 28 #include <sys/syscall.h> 29 #include <sys/types.h> 30 #include <sys/wait.h> 31 #include <unistd.h> 32 33 #include "../utils.h" 34 #include "empty_mntns.h" 35 #include "kselftest_harness.h" 36 37 #define NR_OVERMOUNTS 5 38 39 /* 40 * Setup a proper root filesystem using pivot_root. 41 * This ensures we own the root directory in our user namespace. 42 */ 43 static int setup_root(void) 44 { 45 char tmpdir[] = "/tmp/overmount_test.XXXXXX"; 46 char oldroot[256]; 47 48 if (!mkdtemp(tmpdir)) 49 return -1; 50 51 /* Mount tmpfs at the temporary directory */ 52 if (mount("tmpfs", tmpdir, "tmpfs", 0, "size=10M")) 53 return -1; 54 55 /* Create directory for old root */ 56 snprintf(oldroot, sizeof(oldroot), "%s/oldroot", tmpdir); 57 if (mkdir(oldroot, 0755)) 58 return -1; 59 60 /* pivot_root to use the tmpfs as new root */ 61 if (syscall(SYS_pivot_root, tmpdir, oldroot)) 62 return -1; 63 64 if (chdir("/")) 65 return -1; 66 67 /* Unmount old root */ 68 if (umount2("/oldroot", MNT_DETACH)) 69 return -1; 70 71 /* Remove oldroot directory */ 72 if (rmdir("/oldroot")) 73 return -1; 74 75 return 0; 76 } 77 78 /* 79 * Test scenario: 80 * 1. Enter a user namespace to gain CAP_SYS_ADMIN 81 * 2. Create a new mount namespace 82 * 3. Setup a tmpfs root via pivot_root 83 * 4. Create a mountpoint /newroot and overmount it multiple times 84 * 5. Create a marker file in each layer 85 * 6. Chroot into /newroot (the topmost overmount) 86 * 7. Verify we're in the topmost layer (only topmost marker visible) 87 */ 88 TEST(overmount_chroot) 89 { 90 pid_t pid; 91 92 pid = fork(); 93 ASSERT_GE(pid, 0); 94 95 if (pid == 0) { 96 ssize_t nr_mounts; 97 uint64_t mnt_ids[NR_OVERMOUNTS + 1]; 98 uint64_t root_id_before, root_id_after; 99 struct statmount *sm; 100 char marker[64]; 101 int fd, i; 102 103 /* Step 1: Enter user namespace for privileges */ 104 if (enter_userns()) 105 _exit(1); 106 107 /* Step 2: Create a new mount namespace */ 108 if (unshare(CLONE_NEWNS)) 109 _exit(2); 110 111 /* Step 3: Make the mount tree private */ 112 if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) 113 _exit(3); 114 115 /* Step 4: Setup a proper tmpfs root via pivot_root */ 116 if (setup_root()) 117 _exit(4); 118 119 /* Create the base mount point for overmounting */ 120 if (mkdir("/newroot", 0755)) 121 _exit(5); 122 123 /* Mount base tmpfs on /newroot */ 124 if (mount("tmpfs", "/newroot", "tmpfs", 0, "size=1M")) 125 _exit(6); 126 127 /* Record base mount ID */ 128 mnt_ids[0] = get_unique_mnt_id("/newroot"); 129 if (!mnt_ids[0]) 130 _exit(7); 131 132 /* Create marker in base layer */ 133 fd = open("/newroot/layer_0", O_CREAT | O_RDWR, 0644); 134 if (fd < 0) 135 _exit(8); 136 if (write(fd, "layer_0", 7) != 7) { 137 close(fd); 138 _exit(9); 139 } 140 close(fd); 141 142 /* Step 5: Overmount /newroot multiple times with tmpfs */ 143 for (i = 0; i < NR_OVERMOUNTS; i++) { 144 if (mount("tmpfs", "/newroot", "tmpfs", 0, "size=1M")) 145 _exit(10); 146 147 /* Record mount ID for this layer */ 148 mnt_ids[i + 1] = get_unique_mnt_id("/newroot"); 149 if (!mnt_ids[i + 1]) 150 _exit(11); 151 152 /* Create a marker file in each layer */ 153 snprintf(marker, sizeof(marker), "/newroot/layer_%d", i + 1); 154 fd = open(marker, O_CREAT | O_RDWR, 0644); 155 if (fd < 0) 156 _exit(12); 157 158 if (write(fd, marker, strlen(marker)) != (ssize_t)strlen(marker)) { 159 close(fd); 160 _exit(13); 161 } 162 close(fd); 163 } 164 165 /* Verify mount count increased */ 166 nr_mounts = count_mounts(); 167 if (nr_mounts < NR_OVERMOUNTS + 2) 168 _exit(14); 169 170 /* Record root mount ID before chroot */ 171 root_id_before = get_unique_mnt_id("/newroot"); 172 173 /* Verify this is the topmost layer's mount */ 174 if (root_id_before != mnt_ids[NR_OVERMOUNTS]) 175 _exit(15); 176 177 /* Step 6: Chroot into /newroot (the topmost overmount) */ 178 if (chroot("/newroot")) 179 _exit(16); 180 181 /* Change to root directory within the chroot */ 182 if (chdir("/")) 183 _exit(17); 184 185 /* Step 7: Verify we're in the topmost layer */ 186 root_id_after = get_unique_mnt_id("/"); 187 188 /* The mount ID should be the same as the topmost layer */ 189 if (root_id_after != mnt_ids[NR_OVERMOUNTS]) 190 _exit(18); 191 192 /* Verify the topmost layer's marker file exists */ 193 snprintf(marker, sizeof(marker), "/layer_%d", NR_OVERMOUNTS); 194 if (access(marker, F_OK)) 195 _exit(19); 196 197 /* Verify we cannot see markers from lower layers (they're hidden) */ 198 for (i = 0; i < NR_OVERMOUNTS; i++) { 199 snprintf(marker, sizeof(marker), "/layer_%d", i); 200 if (access(marker, F_OK) == 0) 201 _exit(20); 202 } 203 204 /* Verify the root mount is tmpfs */ 205 sm = statmount_alloc(root_id_after, 0, 206 STATMOUNT_MNT_BASIC | STATMOUNT_MNT_ROOT | 207 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE, 0); 208 if (!sm) 209 _exit(21); 210 211 if (sm->mask & STATMOUNT_FS_TYPE) { 212 if (strcmp(sm->str + sm->fs_type, "tmpfs") != 0) { 213 free(sm); 214 _exit(22); 215 } 216 } 217 218 free(sm); 219 _exit(0); 220 } 221 222 ASSERT_EQ(wait_for_pid(pid), 0); 223 } 224 225 TEST_HARNESS_MAIN 226