1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 * Copyright (c) 2021 Christian Brauner <christian.brauner@ubuntu.com>
6 * Regression test to verify that creating a series of detached mounts,
7 * attaching them to the filesystem, and unmounting them does not trigger an
8 * integer overflow in ns->mounts causing the kernel to block any new mounts in
9 * count_mounts() and returning ENOSPC because it falsely assumes that the
10 * maximum number of mounts in the mount namespace has been reached, i.e. it
11 * thinks it can't fit the new mounts into the mount namespace anymore.
23 #include <sys/mount.h>
25 #include <sys/syscall.h>
26 #include <sys/types.h>
30 #ifndef OPEN_TREE_CLONE
31 #define OPEN_TREE_CLONE 1
34 #ifndef OPEN_TREE_CLOEXEC
35 #define OPEN_TREE_CLOEXEC O_CLOEXEC
38 #ifndef __NR_open_tree
40 #define __NR_open_tree 538
41 #elif defined _MIPS_SIM
42 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
43 #define __NR_open_tree 4428
45 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
46 #define __NR_open_tree 6428
48 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
49 #define __NR_open_tree 5428
51 #elif defined __ia64__
52 #define __NR_open_tree (428 + 1024)
54 #define __NR_open_tree 428
59 #ifndef MOVE_MOUNT_F_EMPTY_PATH
60 #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
63 #ifndef __NR_move_mount
65 #define __NR_move_mount 539
66 #elif defined _MIPS_SIM
67 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
68 #define __NR_move_mount 4429
70 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
71 #define __NR_move_mount 6429
73 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
74 #define __NR_move_mount 5429
76 #elif defined __ia64__
77 #define __NR_move_mount (428 + 1024)
79 #define __NR_move_mount 429
83 static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags)
85 return syscall(__NR_open_tree, dfd, filename, flags);
88 static inline int sys_move_mount(int from_dfd, const char *from_pathname, int to_dfd,
89 const char *to_pathname, unsigned int flags)
91 return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
94 static bool is_shared_mountpoint(const char *path)
102 f = fopen("/proc/self/mountinfo", "re");
106 while (getline(&line, &len, f) > 0) {
107 char *slider1, *slider2;
109 for (slider1 = line, i = 0; slider1 && i < 4; i++)
110 slider1 = strchr(slider1 + 1, ' ');
115 slider2 = strchr(slider1 + 1, ' ');
120 if (strcmp(slider1 + 1, path) == 0) {
121 /* This is the path. Is it shared? */
122 slider1 = strchr(slider2 + 1, ' ');
123 if (slider1 && strstr(slider1, "shared:")) {
135 static void usage(void)
137 const char *text = "mount-new [--recursive] <base-dir>\n";
138 fprintf(stderr, "%s", text);
142 #define exit_usage(format, ...) \
144 fprintf(stderr, format "\n", ##__VA_ARGS__); \
148 #define exit_log(format, ...) \
150 fprintf(stderr, format "\n", ##__VA_ARGS__); \
151 exit(EXIT_FAILURE); \
154 static const struct option longopts[] = {
155 {"help", no_argument, 0, 'a'},
156 { NULL, no_argument, 0, 0 },
159 int main(int argc, char *argv[])
161 int exit_code = EXIT_SUCCESS, index = 0;
162 int dfd, fd_tree, new_argc, ret;
164 char *const *new_argv;
165 char target[PATH_MAX];
167 while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
176 new_argv = &argv[optind];
177 new_argc = argc - optind;
179 exit_usage("Missing base directory\n");
180 base_dir = new_argv[0];
182 if (*base_dir != '/')
183 exit_log("Please specify an absolute path");
185 /* Ensure that target is a shared mountpoint. */
186 if (!is_shared_mountpoint(base_dir))
187 exit_log("Please ensure that \"%s\" is a shared mountpoint", base_dir);
189 ret = unshare(CLONE_NEWNS);
191 exit_log("%m - Failed to create new mount namespace");
193 ret = mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
195 exit_log("%m - Failed to make base_dir shared mountpoint");
197 dfd = open(base_dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
199 exit_log("%m - Failed to open base directory \"%s\"", base_dir);
201 ret = mkdirat(dfd, "detached-move-mount", 0755);
203 exit_log("%m - Failed to create required temporary directories");
205 ret = snprintf(target, sizeof(target), "%s/detached-move-mount", base_dir);
206 if (ret < 0 || (size_t)ret >= sizeof(target))
207 exit_log("%m - Failed to assemble target path");
210 * Having a mount table with 10000 mounts is already quite excessive
211 * and shoult account even for weird test systems.
213 for (size_t i = 0; i < 10000; i++) {
214 fd_tree = sys_open_tree(dfd, "detached-move-mount",
219 if (errno == ENOSYS) /* New mount API not (fully) supported. */
222 fprintf(stderr, "%m - Failed to open %d(detached-move-mount)", dfd);
223 exit_code = EXIT_FAILURE;
227 ret = sys_move_mount(fd_tree, "", dfd, "detached-move-mount", MOVE_MOUNT_F_EMPTY_PATH);
230 fprintf(stderr, "%m - Buggy mount counting");
231 else if (errno == ENOSYS) /* New mount API not (fully) supported. */
234 fprintf(stderr, "%m - Failed to attach mount to %d(detached-move-mount)", dfd);
235 exit_code = EXIT_FAILURE;
240 ret = umount2(target, MNT_DETACH);
242 fprintf(stderr, "%m - Failed to unmount %s", target);
243 exit_code = EXIT_FAILURE;
248 (void)unlinkat(dfd, "detached-move-mount", AT_REMOVEDIR);