1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * MNT_LOCKED keeps the owner of a user namespace from revealing what a
4 * mount covers. The lock has to be set on the copy in that namespace and
5 * only there, it has to survive the expiry of a mount placed beneath the
6 * locked one, and it has to survive the propagated umount of such a copy.
7 */
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sched.h>
12 #include <stdbool.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/mount.h>
19 #include <sys/prctl.h>
20 #include <sys/stat.h>
21 #include <sys/syscall.h>
22 #include <sys/wait.h>
23
24 #include "../../kselftest_harness.h"
25
26 #ifndef MOVE_MOUNT_F_EMPTY_PATH
27 #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
28 #endif
29 #ifndef MOVE_MOUNT_BENEATH
30 #define MOVE_MOUNT_BENEATH 0x00000200
31 #endif
32 #ifndef OPEN_TREE_CLONE
33 #define OPEN_TREE_CLONE 1
34 #endif
35 #ifndef OPEN_TREE_CLOEXEC
36 #define OPEN_TREE_CLOEXEC O_CLOEXEC
37 #endif
38
39 #define DIR_LEN 64
40 #define PATH_LEN 192
41 #define FLAGS (MS_NOSUID | MS_NODEV | MS_NOEXEC)
42
43 /* exit codes of the children, each test says what they mean */
44 enum {
45 CHILD_OK,
46 CHILD_SETUP,
47 CHILD_STEP1,
48 CHILD_STEP2,
49 CHILD_STEP3,
50 CHILD_STEP4,
51 };
52
write_file(const char * path,const char * s)53 static int write_file(const char *path, const char *s)
54 {
55 ssize_t n = -1;
56 int fd;
57
58 fd = open(path, O_WRONLY | O_CLOEXEC);
59 if (fd >= 0) {
60 n = write(fd, s, strlen(s));
61 close(fd);
62 }
63 return n == (ssize_t)strlen(s) ? 0 : -1;
64 }
65
create_file(const char * path,const char * s)66 static int create_file(const char *path, const char *s)
67 {
68 ssize_t n = -1;
69 int fd;
70
71 fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
72 if (fd >= 0) {
73 n = write(fd, s, strlen(s));
74 close(fd);
75 }
76 return n == (ssize_t)strlen(s) ? 0 : -1;
77 }
78
79 /* Root in a new user namespace with a copy of the mount namespace. */
enter_userns(void)80 static int enter_userns(void)
81 {
82 uid_t uid = getuid();
83 gid_t gid = getgid();
84 char map[32];
85
86 prctl(PR_SET_DUMPABLE, 1);
87 if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
88 return -1;
89 if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
90 return -1;
91 snprintf(map, sizeof(map), "0 %d 1", uid);
92 if (write_file("/proc/self/uid_map", map))
93 return -1;
94 snprintf(map, sizeof(map), "0 %d 1", gid);
95 if (write_file("/proc/self/gid_map", map))
96 return -1;
97 return setgid(0) || setuid(0) ? -1 : 0;
98 }
99
wait_child(pid_t pid)100 static int wait_child(pid_t pid)
101 {
102 int status;
103
104 if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
105 return -1;
106 return WEXITSTATUS(status);
107 }
108
wait_byte(int fd)109 static int wait_byte(int fd)
110 {
111 char c;
112
113 return read(fd, &c, 1) == 1 ? 0 : -1;
114 }
115
send_byte(int fd)116 static int send_byte(int fd)
117 {
118 return write(fd, "x", 1) == 1 ? 0 : -1;
119 }
120
121 /* A read of @path fails: the file is covered. */
covered(const char * path)122 static bool covered(const char *path)
123 {
124 int fd = open(path, O_RDONLY | O_CLOEXEC);
125
126 if (fd >= 0)
127 close(fd);
128 return fd < 0;
129 }
130
131 /* What @path reads, "" if it can't be read. */
read_word(const char * path,char * buf,size_t len)132 static int read_word(const char *path, char *buf, size_t len)
133 {
134 ssize_t n = 0;
135 int fd;
136
137 fd = open(path, O_RDONLY | O_CLOEXEC);
138 if (fd >= 0) {
139 n = read(fd, buf, len - 1);
140 close(fd);
141 }
142 buf[n > 0 ? n : 0] = 0;
143 return fd < 0 ? -1 : 0;
144 }
145
146 /* Put a clone of the mount at @src beneath the top mount at @where. */
move_beneath(const char * src,const char * where)147 static int move_beneath(const char *src, const char *where)
148 {
149 int fd, ret;
150
151 fd = syscall(__NR_open_tree, AT_FDCWD, src, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
152 if (fd < 0)
153 return -1;
154 ret = syscall(__NR_move_mount, fd, "", AT_FDCWD, where,
155 MOVE_MOUNT_F_EMPTY_PATH | MOVE_MOUNT_BENEATH);
156 close(fd);
157 return ret;
158 }
159
FIXTURE(locked_mount)160 FIXTURE(locked_mount) {
161 char base[DIR_LEN];
162 bool tracing; /* debugfs with the tracefs automount is there */
163 };
164
FIXTURE_SETUP(locked_mount)165 FIXTURE_SETUP(locked_mount)
166 {
167 char p[PATH_LEN];
168 struct stat st;
169
170 if (geteuid())
171 SKIP(return, "test requires root");
172
173 snprintf(self->base, sizeof(self->base), "/tmp/locked_mount.XXXXXX");
174 ASSERT_NE(mkdtemp(self->base), NULL);
175 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
176 ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
177 ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, "mode=0755"), 0);
178
179 /* an automount that a user can name: the tracefs below debugfs */
180 snprintf(p, sizeof(p), "%s/dbg", self->base);
181 ASSERT_EQ(mkdir(p, 0755), 0);
182 self->tracing = !mount("debugfs", p, "debugfs", FLAGS, NULL);
183 if (self->tracing) {
184 /* the cases trigger it themselves */
185 snprintf(p, sizeof(p), "%s/dbg/tracing", self->base);
186 self->tracing = !fstatat(AT_FDCWD, p, &st, AT_NO_AUTOMOUNT) &&
187 S_ISDIR(st.st_mode);
188 }
189 }
190
FIXTURE_TEARDOWN(locked_mount)191 FIXTURE_TEARDOWN(locked_mount)
192 {
193 umount2(self->base, MNT_DETACH);
194 rmdir(self->base);
195 }
196
197 /*
198 * The child keeps a directory descriptor on the host's debugfs mount, moves
199 * to a user namespace of its own and triggers the automount through the
200 * descriptor. The mount goes below the host's mount and propagates into the
201 * child's copy. The child's copy has to be locked, the host's not.
202 */
automount_child(const char * base,int dfd,int to_host,int from_host)203 static int automount_child(const char *base, int dfd, int to_host, int from_host)
204 {
205 char p[PATH_LEN];
206 struct stat st;
207
208 if (enter_userns())
209 return CHILD_SETUP;
210 if (fstatat(dfd, "tracing/.", &st, 0))
211 return CHILD_STEP1;
212 if (send_byte(to_host) || wait_byte(from_host))
213 return CHILD_SETUP;
214 /* the flags of the copy are locked: EPERM */
215 snprintf(p, sizeof(p), "%s/dbg/tracing", base);
216 if (!mount(NULL, p, NULL, MS_REMOUNT | MS_BIND, NULL) || errno != EPERM)
217 return CHILD_STEP2;
218 return CHILD_OK;
219 }
220
TEST_F(locked_mount,automount_locked_in_the_triggering_namespace)221 TEST_F(locked_mount, automount_locked_in_the_triggering_namespace)
222 {
223 int to_host[2], from_host[2], dfd, ret;
224 char p[PATH_LEN];
225 pid_t pid;
226
227 if (!self->tracing)
228 SKIP(return, "test requires debugfs with the tracefs automount");
229
230 snprintf(p, sizeof(p), "%s/dbg", self->base);
231 ASSERT_EQ(mount(NULL, p, NULL, MS_SHARED, NULL), 0);
232 dfd = open(p, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
233 ASSERT_GE(dfd, 0);
234 ASSERT_EQ(pipe(to_host), 0);
235 ASSERT_EQ(pipe(from_host), 0);
236
237 pid = fork();
238 ASSERT_GE(pid, 0);
239 if (pid == 0)
240 _exit(automount_child(self->base, dfd, to_host[1], from_host[0]));
241 close(dfd);
242 ASSERT_EQ(wait_byte(to_host[0]), 0);
243
244 /* the host's own mount isn't locked: the flags can go */
245 snprintf(p, sizeof(p), "%s/dbg/tracing", self->base);
246 EXPECT_EQ(mount(NULL, p, NULL, MS_REMOUNT | MS_BIND, NULL), 0);
247
248 ASSERT_EQ(send_byte(from_host[1]), 0);
249 ret = wait_child(pid);
250 TH_LOG("child exit code %d", ret);
251 EXPECT_EQ(ret, CHILD_OK);
252 }
253
254 /*
255 * A copy of a tree with a locked cover. The child puts a shrinkable mount
256 * beneath the cover, which hands the lock down, unmounts the cover and then
257 * asks for the umount of an unlocked ancestor. That expires the shrinkable
258 * mount on a kernel that doesn't look at the lock, and the covered
259 * directory is bare.
260 */
expiry_child(const char * base)261 static int expiry_child(const char *base)
262 {
263 char srv[PATH_LEN], shr[PATH_LEN], x[PATH_LEN], c[PATH_LEN];
264 char hidden[PATH_LEN], secret[PATH_LEN];
265
266 snprintf(srv, sizeof(srv), "%s/srv", base);
267 snprintf(shr, sizeof(shr), "%s/shr", base);
268 snprintf(x, sizeof(x), "%s/x", base);
269 snprintf(c, sizeof(c), "%s/c", base);
270 snprintf(hidden, sizeof(hidden), "%s/x/hidden", base);
271 snprintf(secret, sizeof(secret), "%s/x/hidden/secret", base);
272
273 if (enter_userns())
274 return CHILD_SETUP;
275 if (mount(srv, x, NULL, MS_BIND | MS_REC, NULL) ||
276 mount(shr, c, NULL, MS_BIND, NULL))
277 return CHILD_SETUP;
278 /* the cover is locked */
279 if (!umount2(hidden, 0) || errno != EINVAL)
280 return CHILD_STEP1;
281 if (syscall(__NR_move_mount, AT_FDCWD, c, AT_FDCWD, hidden, MOVE_MOUNT_BENEATH))
282 return CHILD_STEP2;
283 /* the lock moved down, the cover may go */
284 if (umount2(hidden, 0))
285 return CHILD_STEP3;
286 /* the holder of the lock may not, in any way */
287 if (!umount2(hidden, 0) || errno != EINVAL)
288 return CHILD_STEP4;
289 if (chdir(x))
290 return CHILD_SETUP;
291 umount2(x, 0);
292 return covered(secret) ? CHILD_OK : CHILD_STEP4;
293 }
294
TEST_F(locked_mount,expiry_leaves_a_locked_mount_alone)295 TEST_F(locked_mount, expiry_leaves_a_locked_mount_alone)
296 {
297 char p[PATH_LEN], q[PATH_LEN];
298 pid_t pid;
299 int ret;
300
301 if (!self->tracing)
302 SKIP(return, "test requires debugfs with the tracefs automount");
303
304 snprintf(p, sizeof(p), "%s/dbg/tracing", self->base);
305 snprintf(q, sizeof(q), "%s/shr", self->base);
306 ASSERT_EQ(mkdir(q, 0755), 0);
307 /* a bind of an automount is shrinkable as well */
308 ASSERT_EQ(mount(p, q, NULL, MS_BIND, NULL), 0);
309 snprintf(p, sizeof(p), "%s/srv", self->base);
310 ASSERT_EQ(mkdir(p, 0755), 0);
311 ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, "mode=0755"), 0);
312 snprintf(p, sizeof(p), "%s/srv/hidden", self->base);
313 ASSERT_EQ(mkdir(p, 0755), 0);
314 snprintf(p, sizeof(p), "%s/srv/hidden/secret", self->base);
315 ASSERT_EQ(create_file(p, "covered-by-root\n"), 0);
316 snprintf(p, sizeof(p), "%s/srv/hidden", self->base);
317 ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, "mode=0755"), 0);
318 snprintf(p, sizeof(p), "%s/x", self->base);
319 ASSERT_EQ(mkdir(p, 0755), 0);
320 snprintf(p, sizeof(p), "%s/c", self->base);
321 ASSERT_EQ(mkdir(p, 0755), 0);
322
323 pid = fork();
324 ASSERT_GE(pid, 0);
325 if (pid == 0)
326 _exit(expiry_child(self->base));
327 ret = wait_child(pid);
328 TH_LOG("child exit code %d", ret);
329 EXPECT_EQ(ret, CHILD_OK);
330 }
331
332 /*
333 * The copy of the mount tree that a user namespace gets at its creation has
334 * the automounts in it locked unless they are on an expiry list. A bind of
335 * such a tree inside the namespace copies the lock to the automount but not
336 * to the root of the bind, so the child may ask for the umount of that root.
337 * That must not expire the locked automount below it: the root is busy, the
338 * umount fails and the automount has to be there afterwards.
339 */
copied_tree_child(const char * base)340 static int copied_tree_child(const char *base)
341 {
342 char dbg[PATH_LEN], x[PATH_LEN], tracing[PATH_LEN];
343 struct stat root, st;
344
345 snprintf(dbg, sizeof(dbg), "%s/dbg", base);
346 snprintf(x, sizeof(x), "%s/x", base);
347 snprintf(tracing, sizeof(tracing), "%s/x/tracing", base);
348
349 if (enter_userns())
350 return CHILD_SETUP;
351 if (mount(dbg, x, NULL, MS_BIND | MS_REC, NULL))
352 return CHILD_SETUP;
353 /* the copy of the automount carries the lock */
354 if (!umount2(tracing, 0) || errno != EINVAL)
355 return CHILD_STEP1;
356 /* the root of the bind doesn't; keep it busy */
357 if (chdir(x))
358 return CHILD_SETUP;
359 if (!umount2(x, 0) || errno != EBUSY)
360 return CHILD_STEP2;
361 /* the automount below it must not have gone */
362 if (stat(x, &root) || fstatat(AT_FDCWD, tracing, &st, AT_NO_AUTOMOUNT))
363 return CHILD_SETUP;
364 return st.st_dev != root.st_dev ? CHILD_OK : CHILD_STEP3;
365 }
366
TEST_F(locked_mount,umount_of_a_bind_leaves_a_locked_automount_alone)367 TEST_F(locked_mount, umount_of_a_bind_leaves_a_locked_automount_alone)
368 {
369 char p[PATH_LEN];
370 struct stat st;
371 pid_t pid;
372 int ret;
373
374 if (!self->tracing)
375 SKIP(return, "test requires debugfs with the tracefs automount");
376
377 /* the copy the child gets has to contain the automount */
378 snprintf(p, sizeof(p), "%s/dbg/tracing/.", self->base);
379 ASSERT_EQ(stat(p, &st), 0);
380 snprintf(p, sizeof(p), "%s/x", self->base);
381 ASSERT_EQ(mkdir(p, 0755), 0);
382
383 pid = fork();
384 ASSERT_GE(pid, 0);
385 if (pid == 0)
386 _exit(copied_tree_child(self->base));
387 ret = wait_child(pid);
388 TH_LOG("child exit code %d", ret);
389 EXPECT_EQ(ret, CHILD_OK);
390 }
391
392 /*
393 * Z is a user namespace with a copy of a tree in which P/d is covered by a
394 * locked mount. The host mounts X on P/d, which propagates beneath Z's
395 * cover, and unmounts it again. Z's cover has to be locked afterwards as
396 * it was before.
397 */
propagation_child(const char * base,int to_host,int from_host)398 static int propagation_child(const char *base, int to_host, int from_host)
399 {
400 char d[PATH_LEN], secret[PATH_LEN];
401
402 snprintf(d, sizeof(d), "%s/P/d", base);
403 snprintf(secret, sizeof(secret), "%s/P/d/secret", base);
404
405 if (enter_userns())
406 return CHILD_SETUP;
407 /* the cover is locked */
408 if (!umount2(d, 0) || errno != EINVAL)
409 return CHILD_STEP1;
410 if (send_byte(to_host) || wait_byte(from_host))
411 return CHILD_SETUP;
412 /* X came and went beneath it: still locked */
413 if (!umount2(d, 0) || errno != EINVAL)
414 return CHILD_STEP2;
415 return covered(secret) ? CHILD_OK : CHILD_STEP3;
416 }
417
TEST_F(locked_mount,propagated_copy_keeps_the_cover_locked)418 TEST_F(locked_mount, propagated_copy_keeps_the_cover_locked)
419 {
420 int to_host[2], from_host[2], ret;
421 char p[PATH_LEN];
422 pid_t pid;
423
424 snprintf(p, sizeof(p), "%s/P", self->base);
425 ASSERT_EQ(mkdir(p, 0755), 0);
426 ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, "mode=0755"), 0);
427 ASSERT_EQ(mount(NULL, p, NULL, MS_SHARED, NULL), 0);
428 snprintf(p, sizeof(p), "%s/P/d", self->base);
429 ASSERT_EQ(mkdir(p, 0755), 0);
430 snprintf(p, sizeof(p), "%s/P/d/secret", self->base);
431 ASSERT_EQ(create_file(p, "covered-by-root\n"), 0);
432 ASSERT_EQ(pipe(to_host), 0);
433 ASSERT_EQ(pipe(from_host), 0);
434
435 pid = fork();
436 ASSERT_GE(pid, 0);
437 if (pid == 0) {
438 /* M: a manager's namespace that covers P/d for Z */
439 pid_t z;
440
441 if (unshare(CLONE_NEWNS))
442 _exit(CHILD_SETUP);
443 snprintf(p, sizeof(p), "%s/P", self->base);
444 if (mount(NULL, p, NULL, MS_SLAVE, NULL))
445 _exit(CHILD_SETUP);
446 snprintf(p, sizeof(p), "%s/P/d", self->base);
447 if (mount("tmpfs", p, "tmpfs", 0, "mode=0755"))
448 _exit(CHILD_SETUP);
449 z = fork();
450 if (z < 0)
451 _exit(CHILD_SETUP);
452 if (z == 0)
453 _exit(propagation_child(self->base, to_host[1], from_host[0]));
454 _exit(wait_child(z));
455 }
456 ASSERT_EQ(wait_byte(to_host[0]), 0);
457
458 /* the host mounts on P/d and unmounts again; both propagate */
459 snprintf(p, sizeof(p), "%s/P/d", self->base);
460 ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, "mode=0755"), 0);
461 ASSERT_EQ(umount2(p, 0), 0);
462
463 ASSERT_EQ(send_byte(from_host[1]), 0);
464 ret = wait_child(pid);
465 TH_LOG("child exit code %d", ret);
466 EXPECT_EQ(ret, CHILD_OK);
467 }
468
469 /*
470 * A mount of a mount namespace file is left out of the copy that
471 * unshare(CLONE_NEWNS) makes, together with everything mounted on it, so
472 * it can't take over the lock of a cover it is moved beneath. The child
473 * tries just that with the namespace file of a grandchild and gets EINVAL.
474 * A bind of an ordinary file takes the lock over, and the file is still
475 * covered in the next copy of the mount namespace.
476 */
ns_file_child(const char * base)477 static int ns_file_child(const char *base)
478 {
479 char secret[PATH_LEN], mine[PATH_LEN], nsfile[PATH_LEN], buf[32];
480 int ret = CHILD_OK, pfd[2];
481 pid_t pid;
482
483 snprintf(secret, sizeof(secret), "%s/P/secret", base);
484 snprintf(mine, sizeof(mine), "%s/mine", base);
485
486 if (enter_userns() || pipe(pfd))
487 return CHILD_SETUP;
488 /* the cover is locked */
489 if (!umount2(secret, 0) || errno != EINVAL)
490 return CHILD_STEP1;
491
492 /* a mount namespace newer than our own, so that a bind of it is allowed */
493 pid = fork();
494 if (pid < 0)
495 return CHILD_SETUP;
496 if (pid == 0) {
497 if (unshare(CLONE_NEWNS) || send_byte(pfd[1]))
498 _exit(1);
499 pause();
500 _exit(0);
501 }
502 if (wait_byte(pfd[0]))
503 return CHILD_SETUP;
504 snprintf(nsfile, sizeof(nsfile), "/proc/%d/ns/mnt", pid);
505 if (!move_beneath(nsfile, secret) || errno != EINVAL)
506 ret = CHILD_STEP2;
507 kill(pid, SIGKILL);
508 waitpid(pid, NULL, 0);
509 if (ret)
510 return ret;
511
512 /* an ordinary file beneath the cover takes the lock over */
513 if (move_beneath(mine, secret))
514 return CHILD_STEP3;
515 if (umount2(secret, 0))
516 return CHILD_STEP3;
517 if (!umount2(secret, 0) || errno != EINVAL)
518 return CHILD_STEP3;
519 /* and it is in the next copy of the namespace */
520 if (unshare(CLONE_NEWNS))
521 return CHILD_SETUP;
522 if (read_word(secret, buf, sizeof(buf)) || strcmp(buf, "mine"))
523 return CHILD_STEP4;
524 return CHILD_OK;
525 }
526
TEST_F(locked_mount,mount_namespace_file_cant_take_the_lock)527 TEST_F(locked_mount, mount_namespace_file_cant_take_the_lock)
528 {
529 char p[PATH_LEN], q[PATH_LEN];
530 pid_t pid;
531 int ret;
532
533 snprintf(p, sizeof(p), "%s/P", self->base);
534 ASSERT_EQ(mkdir(p, 0755), 0);
535 ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, "mode=0755"), 0);
536 snprintf(p, sizeof(p), "%s/P/secret", self->base);
537 ASSERT_EQ(create_file(p, "covered-by-root\n"), 0);
538 snprintf(q, sizeof(q), "%s/P/empty", self->base);
539 ASSERT_EQ(create_file(q, ""), 0);
540 /* the cover: an empty file over the secret */
541 ASSERT_EQ(mount(q, p, NULL, MS_BIND, NULL), 0);
542 snprintf(q, sizeof(q), "%s/mine", self->base);
543 ASSERT_EQ(create_file(q, "mine"), 0);
544
545 pid = fork();
546 ASSERT_GE(pid, 0);
547 if (pid == 0)
548 _exit(ns_file_child(self->base));
549 ret = wait_child(pid);
550 TH_LOG("child exit code %d", ret);
551 EXPECT_EQ(ret, CHILD_OK);
552 }
553
554 TEST_HARNESS_MAIN
555