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>
29 #include "idmapped-mounts/missing.h"
31 static bool is_shared_mountpoint(const char *path)
39 f = fopen("/proc/self/mountinfo", "re");
43 while (getline(&line, &len, f) > 0) {
44 char *slider1, *slider2;
46 for (slider1 = line, i = 0; slider1 && i < 4; i++)
47 slider1 = strchr(slider1 + 1, ' ');
52 slider2 = strchr(slider1 + 1, ' ');
57 if (strcmp(slider1 + 1, path) == 0) {
58 /* This is the path. Is it shared? */
59 slider1 = strchr(slider2 + 1, ' ');
60 if (slider1 && strstr(slider1, "shared:")) {
72 static void usage(void)
74 const char *text = "mount-new [--recursive] <base-dir>\n";
75 fprintf(stderr, "%s", text);
79 #define exit_usage(format, ...) \
81 fprintf(stderr, format "\n", ##__VA_ARGS__); \
85 #define exit_log(format, ...) \
87 fprintf(stderr, format "\n", ##__VA_ARGS__); \
91 static const struct option longopts[] = {
92 {"help", no_argument, 0, 'a'},
93 { NULL, no_argument, 0, 0 },
96 int main(int argc, char *argv[])
98 int exit_code = EXIT_SUCCESS, index = 0;
99 int dfd, fd_tree, new_argc, ret, i;
101 char *const *new_argv;
102 char target[PATH_MAX];
104 while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
113 new_argv = &argv[optind];
114 new_argc = argc - optind;
116 exit_usage("Missing base directory\n");
117 base_dir = new_argv[0];
119 if (*base_dir != '/')
120 exit_log("Please specify an absolute path");
122 /* Ensure that target is a shared mountpoint. */
123 if (!is_shared_mountpoint(base_dir))
124 exit_log("Please ensure that \"%s\" is a shared mountpoint", base_dir);
126 ret = unshare(CLONE_NEWNS);
128 exit_log("%m - Failed to create new mount namespace");
130 ret = mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
132 exit_log("%m - Failed to make base_dir shared mountpoint");
134 dfd = open(base_dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
136 exit_log("%m - Failed to open base directory \"%s\"", base_dir);
138 ret = mkdirat(dfd, "detached-move-mount", 0755);
140 exit_log("%m - Failed to create required temporary directories");
142 ret = snprintf(target, sizeof(target), "%s/detached-move-mount", base_dir);
143 if (ret < 0 || (size_t)ret >= sizeof(target))
144 exit_log("%m - Failed to assemble target path");
147 * Having a mount table with 10000 mounts is already quite excessive
148 * and shoult account even for weird test systems.
150 for (i = 0; i < 10000; i++) {
151 fd_tree = sys_open_tree(dfd, "detached-move-mount",
156 if (errno == ENOSYS) /* New mount API not (fully) supported. */
159 fprintf(stderr, "%m - Failed to open %d(detached-move-mount)", dfd);
160 exit_code = EXIT_FAILURE;
164 ret = sys_move_mount(fd_tree, "", dfd, "detached-move-mount", MOVE_MOUNT_F_EMPTY_PATH);
167 fprintf(stderr, "%m - Buggy mount counting");
168 else if (errno == ENOSYS) /* New mount API not (fully) supported. */
171 fprintf(stderr, "%m - Failed to attach mount to %d(detached-move-mount)", dfd);
172 exit_code = EXIT_FAILURE;
177 ret = umount2(target, MNT_DETACH);
179 fprintf(stderr, "%m - Failed to unmount %s", target);
180 exit_code = EXIT_FAILURE;
185 (void)unlinkat(dfd, "detached-move-mount", AT_REMOVEDIR);