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
write_file(const char * path,const char * s)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. */
enter_userns(void)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. */
detached_tmpfs(void)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
FIXTURE(overmount_reparent)89 FIXTURE(overmount_reparent) {
90 char dir[DIR_LEN];
91 };
92
FIXTURE_SETUP(overmount_reparent)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
FIXTURE_TEARDOWN(overmount_reparent)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 */
TEST_F(overmount_reparent,dead_overmount_holder_not_followed)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