1 // SPDX-License-Identifier: GPL-2.0
16 #include <sys/syscall.h>
18 #include "../kselftest_harness.h"
21 #define CLONE_NEWNS 0x00020000
25 #define CLONE_NEWUSER 0x10000000
29 #define MS_SHARED (1 << 20)
33 #define MS_PRIVATE (1<<18)
36 #ifndef MOVE_MOUNT_SET_GROUP
37 #define MOVE_MOUNT_SET_GROUP 0x00000100
40 #ifndef MOVE_MOUNT_F_EMPTY_PATH
41 #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
44 #ifndef MOVE_MOUNT_T_EMPTY_PATH
45 #define MOVE_MOUNT_T_EMPTY_PATH 0x00000040
48 static ssize_t write_nointr(int fd, const void *buf, size_t count)
53 ret = write(fd, buf, count);
54 } while (ret < 0 && errno == EINTR);
59 static int write_file(const char *path, const void *buf, size_t count)
64 fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
68 ret = write_nointr(fd, buf, count);
70 if (ret < 0 || (size_t)ret != count)
76 static int create_and_enter_userns(void)
85 if (unshare(CLONE_NEWUSER))
88 if (write_file("/proc/self/setgroups", "deny", sizeof("deny") - 1) &&
92 snprintf(map, sizeof(map), "0 %d 1", uid);
93 if (write_file("/proc/self/uid_map", map, strlen(map)))
97 snprintf(map, sizeof(map), "0 %d 1", gid);
98 if (write_file("/proc/self/gid_map", map, strlen(map)))
110 static int prepare_unpriv_mountns(void)
112 if (create_and_enter_userns())
115 if (unshare(CLONE_NEWNS))
118 if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0))
124 static char *get_field(char *src, int nfields)
129 for (i = 0; i < nfields; i++) {
130 while (*p && *p != ' ' && *p != '\t')
142 static void null_endofword(char *word)
144 while (*word && *word != ' ' && *word != '\t')
149 static bool is_shared_mount(const char *path)
155 f = fopen("/proc/self/mountinfo", "re");
159 while (getline(&line, &len, f) != -1) {
162 target = get_field(line, 4);
166 opts = get_field(target, 2);
170 null_endofword(target);
172 if (strcmp(target, path) != 0)
175 null_endofword(opts);
176 if (strstr(opts, "shared:"))
186 /* Attempt to de-conflict with the selftests tree. */
188 #define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
191 #define SET_GROUP_FROM "/tmp/move_mount_set_group_supported_from"
192 #define SET_GROUP_TO "/tmp/move_mount_set_group_supported_to"
194 static bool move_mount_set_group_supported(void)
198 if (mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,
199 "size=100000,mode=700"))
202 if (mount(NULL, "/tmp", NULL, MS_PRIVATE, 0))
205 if (mkdir(SET_GROUP_FROM, 0777))
208 if (mkdir(SET_GROUP_TO, 0777))
211 if (mount("testing", SET_GROUP_FROM, "tmpfs", MS_NOATIME | MS_NODEV,
212 "size=100000,mode=700"))
215 if (mount(SET_GROUP_FROM, SET_GROUP_TO, NULL, MS_BIND, NULL))
218 if (mount(NULL, SET_GROUP_FROM, NULL, MS_SHARED, 0))
221 ret = syscall(__NR_move_mount, AT_FDCWD, SET_GROUP_FROM,
222 AT_FDCWD, SET_GROUP_TO, MOVE_MOUNT_SET_GROUP);
223 umount2("/tmp", MNT_DETACH);
228 FIXTURE(move_mount_set_group) {
231 #define SET_GROUP_A "/tmp/A"
233 FIXTURE_SETUP(move_mount_set_group)
237 ASSERT_EQ(prepare_unpriv_mountns(), 0);
239 ret = move_mount_set_group_supported();
242 SKIP(return, "move_mount(MOVE_MOUNT_SET_GROUP) is not supported");
244 umount2("/tmp", MNT_DETACH);
246 ASSERT_EQ(mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,
247 "size=100000,mode=700"), 0);
249 ASSERT_EQ(mkdir(SET_GROUP_A, 0777), 0);
251 ASSERT_EQ(mount("testing", SET_GROUP_A, "tmpfs", MS_NOATIME | MS_NODEV,
252 "size=100000,mode=700"), 0);
255 FIXTURE_TEARDOWN(move_mount_set_group)
259 ret = move_mount_set_group_supported();
262 SKIP(return, "move_mount(MOVE_MOUNT_SET_GROUP) is not supported");
264 umount2("/tmp", MNT_DETACH);
267 #define __STACK_SIZE (8 * 1024 * 1024)
268 static pid_t do_clone(int (*fn)(void *), void *arg, int flags)
272 stack = malloc(__STACK_SIZE);
277 return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);
279 return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);
283 static int wait_for_pid(pid_t pid)
288 ret = waitpid(pid, &status, 0);
296 if (!WIFEXITED(status))
299 return WEXITSTATUS(status);
309 static int get_nestedns_mount_cb(void *data)
311 struct child_args *ca = (struct child_args *)data;
314 ret = prepare_unpriv_mountns();
319 ret = mount(NULL, SET_GROUP_A, NULL, MS_SHARED, 0);
324 ret = open("/proc/self/ns/user", O_RDONLY);
329 ret = open("/proc/self/ns/mnt", O_RDONLY);
334 ret = open(SET_GROUP_A, O_RDONLY);
342 TEST_F(move_mount_set_group, complex_sharing_copying)
344 struct child_args ca_from = {
347 struct child_args ca_to = {
353 ret = move_mount_set_group_supported();
356 SKIP(return, "move_mount(MOVE_MOUNT_SET_GROUP) is not supported");
358 pid = do_clone(get_nestedns_mount_cb, (void *)&ca_from, CLONE_VFORK |
359 CLONE_VM | CLONE_FILES); ASSERT_GT(pid, 0);
360 ASSERT_EQ(wait_for_pid(pid), 0);
362 pid = do_clone(get_nestedns_mount_cb, (void *)&ca_to, CLONE_VFORK |
363 CLONE_VM | CLONE_FILES); ASSERT_GT(pid, 0);
364 ASSERT_EQ(wait_for_pid(pid), 0);
366 ASSERT_EQ(syscall(__NR_move_mount, ca_from.mntfd, "",
367 ca_to.mntfd, "", MOVE_MOUNT_SET_GROUP
368 | MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_T_EMPTY_PATH),
371 ASSERT_EQ(setns(ca_to.mntnsfd, CLONE_NEWNS), 0);
372 ASSERT_EQ(is_shared_mount(SET_GROUP_A), 1);