10 #include <sys/statvfs.h>
19 # define CLONE_NEWNS 0x00020000
22 # define CLONE_NEWUTS 0x04000000
25 # define CLONE_NEWIPC 0x08000000
28 # define CLONE_NEWNET 0x40000000
31 # define CLONE_NEWUSER 0x10000000
34 # define CLONE_NEWPID 0x20000000
41 # define MS_RELATIME (1 << 21)
43 #ifndef MS_STRICTATIME
44 # define MS_STRICTATIME (1 << 24)
47 static void die(char *fmt, ...)
51 vfprintf(stderr, fmt, ap);
56 static void write_file(char *filename, char *fmt, ...)
65 buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
68 die("vsnprintf failed: %s\n",
71 if (buf_len >= sizeof(buf)) {
72 die("vsnprintf output truncated\n");
75 fd = open(filename, O_WRONLY);
77 die("open of %s failed: %s\n",
78 filename, strerror(errno));
80 written = write(fd, buf, buf_len);
81 if (written != buf_len) {
83 die("short write to %s\n", filename);
85 die("write to %s failed: %s\n",
86 filename, strerror(errno));
90 die("close of %s failed: %s\n",
91 filename, strerror(errno));
95 static int read_mnt_flags(const char *path)
101 ret = statvfs(path, &stat);
103 die("statvfs of %s failed: %s\n",
104 path, strerror(errno));
106 if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \
107 ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \
108 ST_SYNCHRONOUS | ST_MANDLOCK)) {
109 die("Unrecognized mount flags\n");
112 if (stat.f_flag & ST_RDONLY)
113 mnt_flags |= MS_RDONLY;
114 if (stat.f_flag & ST_NOSUID)
115 mnt_flags |= MS_NOSUID;
116 if (stat.f_flag & ST_NODEV)
117 mnt_flags |= MS_NODEV;
118 if (stat.f_flag & ST_NOEXEC)
119 mnt_flags |= MS_NOEXEC;
120 if (stat.f_flag & ST_NOATIME)
121 mnt_flags |= MS_NOATIME;
122 if (stat.f_flag & ST_NODIRATIME)
123 mnt_flags |= MS_NODIRATIME;
124 if (stat.f_flag & ST_RELATIME)
125 mnt_flags |= MS_RELATIME;
126 if (stat.f_flag & ST_SYNCHRONOUS)
127 mnt_flags |= MS_SYNCHRONOUS;
128 if (stat.f_flag & ST_MANDLOCK)
129 mnt_flags |= ST_MANDLOCK;
134 static void create_and_enter_userns(void)
142 if (unshare(CLONE_NEWUSER) !=0) {
143 die("unshare(CLONE_NEWUSER) failed: %s\n",
147 write_file("/proc/self/uid_map", "0 %d 1", uid);
148 write_file("/proc/self/gid_map", "0 %d 1", gid);
150 if (setgroups(0, NULL) != 0) {
151 die("setgroups failed: %s\n",
154 if (setgid(0) != 0) {
155 die ("setgid(0) failed %s\n",
158 if (setuid(0) != 0) {
159 die("setuid(0) failed %s\n",
165 bool test_unpriv_remount(const char *fstype, const char *mount_options,
166 int mount_flags, int remount_flags, int invalid_flags)
172 die("fork failed: %s\n",
175 if (child != 0) { /* parent */
178 pid = waitpid(child, &status, 0);
180 die("waitpid failed: %s\n",
184 die("waited for %d got %d\n",
187 if (!WIFEXITED(status)) {
188 die("child did not terminate cleanly\n");
190 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
193 create_and_enter_userns();
194 if (unshare(CLONE_NEWNS) != 0) {
195 die("unshare(CLONE_NEWNS) failed: %s\n",
199 if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) {
200 die("mount of %s with options '%s' on /tmp failed: %s\n",
202 mount_options? mount_options : "",
206 create_and_enter_userns();
208 if (unshare(CLONE_NEWNS) != 0) {
209 die("unshare(CLONE_NEWNS) failed: %s\n",
213 if (mount("/tmp", "/tmp", "none",
214 MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) {
215 /* system("cat /proc/self/mounts"); */
216 die("remount of /tmp failed: %s\n",
220 if (mount("/tmp", "/tmp", "none",
221 MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) {
222 /* system("cat /proc/self/mounts"); */
223 die("remount of /tmp with invalid flags "
224 "succeeded unexpectedly\n");
229 static bool test_unpriv_remount_simple(int mount_flags)
231 return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
234 static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags)
236 return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags,
240 static bool test_priv_mount_unpriv_remount(void)
244 const char *orig_path = "/dev";
245 const char *dest_path = "/tmp";
246 int orig_mnt_flags, remount_mnt_flags;
250 die("fork failed: %s\n",
253 if (child != 0) { /* parent */
256 pid = waitpid(child, &status, 0);
258 die("waitpid failed: %s\n",
262 die("waited for %d got %d\n",
265 if (!WIFEXITED(status)) {
266 die("child did not terminate cleanly\n");
268 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
271 orig_mnt_flags = read_mnt_flags(orig_path);
273 create_and_enter_userns();
274 ret = unshare(CLONE_NEWNS);
276 die("unshare(CLONE_NEWNS) failed: %s\n",
280 ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL);
282 die("recursive bind mount of %s onto %s failed: %s\n",
283 orig_path, dest_path, strerror(errno));
286 ret = mount(dest_path, dest_path, "none",
287 MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL);
289 /* system("cat /proc/self/mounts"); */
290 die("remount of /tmp failed: %s\n",
294 remount_mnt_flags = read_mnt_flags(dest_path);
295 if (orig_mnt_flags != remount_mnt_flags) {
296 die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n",
297 dest_path, orig_path);
302 int main(int argc, char **argv)
304 if (!test_unpriv_remount_simple(MS_RDONLY)) {
305 die("MS_RDONLY malfunctions\n");
307 if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) {
308 die("MS_NODEV malfunctions\n");
310 if (!test_unpriv_remount_simple(MS_NOSUID)) {
311 die("MS_NOSUID malfunctions\n");
313 if (!test_unpriv_remount_simple(MS_NOEXEC)) {
314 die("MS_NOEXEC malfunctions\n");
316 if (!test_unpriv_remount_atime(MS_RELATIME,
319 die("MS_RELATIME malfunctions\n");
321 if (!test_unpriv_remount_atime(MS_STRICTATIME,
324 die("MS_STRICTATIME malfunctions\n");
326 if (!test_unpriv_remount_atime(MS_NOATIME,
329 die("MS_NOATIME malfunctions\n");
331 if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME,
334 die("MS_RELATIME|MS_NODIRATIME malfunctions\n");
336 if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME,
339 die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n");
341 if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME,
344 die("MS_NOATIME|MS_DIRATIME malfunctions\n");
346 if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME))
348 die("Default atime malfunctions\n");
350 if (!test_priv_mount_unpriv_remount()) {
351 die("Mount flags unexpectedly changed after remount\n");