xref: /linux/tools/testing/selftests/filesystems/mount_cycle/overmount_reparent_test.c (revision f99005fbed4e8071110faa350cc5fa453afa1455)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * propagate_umount() slides a surviving overmount off a mount that is being
4  * unmounted and unmounts that mount right after. If a file descriptor keeps
5  * the unmounted mount alive its ->overmount is left pointing at the moved
6  * mount, which is freed on its own schedule. move_mount(MOVE_MOUNT_BENEATH)
7  * with such a file descriptor as the target walks ->overmount in
8  * topmost_overmount() before it checks that the target is still mounted and
9  * must not step into the freed mount.
10  */
11 #define _GNU_SOURCE
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <sched.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <sys/mount.h>
20 #include <sys/stat.h>
21 #include <sys/syscall.h>
22 
23 #include "../wrappers.h"
24 #include "../../kselftest_harness.h"
25 
26 #ifndef MOVE_MOUNT_BENEATH
27 #define MOVE_MOUNT_BENEATH 0x00000200
28 #endif
29 
30 #ifndef FSCONFIG_CMD_CREATE
31 #define FSCONFIG_CMD_CREATE 6
32 #endif
33 
34 #define DIR_LEN 64
35 #define PATH_LEN 128
36 
37 static int write_file(const char *path, const char *s)
38 {
39 	ssize_t n = -1;
40 	int fd;
41 
42 	fd = open(path, O_WRONLY | O_CLOEXEC);
43 	if (fd >= 0) {
44 		n = write(fd, s, strlen(s));
45 		close(fd);
46 	}
47 	return n == (ssize_t)strlen(s) ? 0 : -1;
48 }
49 
50 /* Become root in a new user namespace with a private mount namespace. */
51 static int enter_userns(void)
52 {
53 	uid_t uid = getuid();
54 	gid_t gid = getgid();
55 	char map[32];
56 
57 	if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
58 		return -1;
59 	if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
60 		return -1;
61 	snprintf(map, sizeof(map), "0 %d 1", uid);
62 	if (write_file("/proc/self/uid_map", map))
63 		return -1;
64 	snprintf(map, sizeof(map), "0 %d 1", gid);
65 	if (write_file("/proc/self/gid_map", map))
66 		return -1;
67 	if (setgid(0) || setuid(0))
68 		return -1;
69 	return mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL);
70 }
71 
72 /* A detached tmpfs mount to serve as the source of a move. */
73 static int detached_tmpfs(void)
74 {
75 	int sfd, mfd;
76 
77 	sfd = sys_fsopen("tmpfs", 0);
78 	if (sfd < 0)
79 		return -1;
80 	if (sys_fsconfig(sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) {
81 		close(sfd);
82 		return -1;
83 	}
84 	mfd = sys_fsmount(sfd, 0, 0);
85 	close(sfd);
86 	return mfd;
87 }
88 
89 FIXTURE(overmount_reparent) {
90 	char dir[DIR_LEN];
91 };
92 
93 FIXTURE_SETUP(overmount_reparent)
94 {
95 	snprintf(self->dir, sizeof(self->dir), "/tmp/overmount_reparent.XXXXXX");
96 	ASSERT_NE(mkdtemp(self->dir), NULL);
97 	if (enter_userns()) {
98 		rmdir(self->dir);
99 		SKIP(return, "test requires user namespaces");
100 	}
101 }
102 
103 FIXTURE_TEARDOWN(overmount_reparent)
104 {
105 	char p[PATH_LEN];
106 
107 	snprintf(p, sizeof(p), "%s/b", self->dir);
108 	umount2(p, MNT_DETACH);
109 	snprintf(p, sizeof(p), "%s/a", self->dir);
110 	umount2(p, MNT_DETACH);
111 }
112 
113 /*
114  * A shared mount base_a is bind-mounted to base_b as its peer. A mount X on
115  * base_a/mp propagates a copy X_b onto base_b/mp. A file descriptor pins X_b,
116  * then X_b is made private and an overmount is stacked on its root.
117  *
118  * A lazy unmount of X propagates to X_b: X_b is committed to the unmount while
119  * its overmount survives, so propagate_umount() reparents the overmount onto
120  * base_b and unmounts X_b, which the file descriptor keeps alive. Unmounting
121  * the reparented overmount frees it. X_b->overmount now dangles unless
122  * mnt_change_mountpoint() reset it.
123  *
124  * move_mount(MOVE_MOUNT_BENEATH) through the file descriptor makes
125  * do_lock_mount() follow X_b->overmount in topmost_overmount() before it
126  * notices that X_b is no longer mounted. With the pointer reset the move fails
127  * cleanly with ENOENT; otherwise it reads the freed overmount.
128  */
129 TEST_F(overmount_reparent, dead_overmount_holder_not_followed)
130 {
131 	char a[PATH_LEN], b[PATH_LEN], amp[PATH_LEN], bmp[PATH_LEN];
132 	int fd, mfd, ret;
133 
134 	snprintf(a, sizeof(a), "%s/a", self->dir);
135 	snprintf(b, sizeof(b), "%s/b", self->dir);
136 	ASSERT_EQ(mkdir(a, 0755), 0);
137 	ASSERT_EQ(mkdir(b, 0755), 0);
138 
139 	ASSERT_EQ(mount("tmpfs", a, "tmpfs", 0, NULL), 0);
140 	ASSERT_EQ(mount(NULL, a, NULL, MS_SHARED, NULL), 0);
141 	ASSERT_EQ(mount(a, b, NULL, MS_BIND, NULL), 0);
142 
143 	snprintf(amp, sizeof(amp), "%s/a/mp", self->dir);
144 	snprintf(bmp, sizeof(bmp), "%s/b/mp", self->dir);
145 	/* base_a and base_b share one superblock, so this dir is in both. */
146 	ASSERT_EQ(mkdir(amp, 0755), 0);
147 
148 	/* X on base_a/mp propagates a copy X_b onto base_b/mp. */
149 	ASSERT_EQ(mount("tmpfs", amp, "tmpfs", 0, NULL), 0);
150 
151 	/* Pin X_b before anything is stacked on top of it. */
152 	fd = open(bmp, O_PATH | O_CLOEXEC);
153 	ASSERT_GE(fd, 0);
154 
155 	/* Keep the overmount local to X_b. */
156 	ASSERT_EQ(mount(NULL, bmp, NULL, MS_PRIVATE, NULL), 0);
157 
158 	/* The overmount on X_b's root: X_b->overmount points at it. */
159 	ASSERT_EQ(mount("tmpfs", bmp, "tmpfs", 0, NULL), 0);
160 
161 	/* Reparents the overmount onto base_b and unmounts X_b. */
162 	ASSERT_EQ(umount2(amp, MNT_DETACH), 0);
163 
164 	/* Free the reparented overmount. */
165 	ASSERT_EQ(umount2(bmp, MNT_DETACH), 0);
166 	usleep(100000);
167 
168 	mfd = detached_tmpfs();
169 	ASSERT_GE(mfd, 0);
170 
171 	ret = sys_move_mount(mfd, "", fd, "",
172 			     MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH |
173 			     MOVE_MOUNT_BENEATH);
174 	EXPECT_EQ(ret, -1);
175 	EXPECT_EQ(errno, ENOENT);
176 
177 	close(mfd);
178 	close(fd);
179 }
180 
181 TEST_HARNESS_MAIN
182