Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
43static 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 */
88TEST(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
225TEST_HARNESS_MAIN