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