| 1 | |
| 2 | |
| 3 | * An 'F' entry keeps its interpreter open for as long as the entry exists,
|
| 4 | * and the entry only goes away when the binfmt_misc superblock is destroyed.
|
| 5 | * An interpreter that lives on a mount which in turn keeps that superblock
|
| 6 | * alive therefore pins the instance that owns it, and nothing can break the
|
| 7 | * cycle. Check the two ways userspace could arrange for that: an interpreter
|
| 8 | * on the binfmt_misc instance itself, and one on a filesystem stacked on it.
|
| 9 | *
|
| 10 | * Runs unprivileged in a user namespace; binfmt_misc is FS_USERNS_MOUNT.
|
| 11 |
|
| 12 | #define _GNU_SOURCE
|
| 13 | #include <fcntl.h>
|
| 14 | #include <limits.h>
|
| 15 | #include <sched.h>
|
| 16 | #include <sys/mount.h>
|
| 17 | #include <sys/stat.h>
|
| 18 |
|
| 19 | #include "../filesystems/utils.h"
|
| 20 | #include "kselftest_harness.h"
|
| 21 |
|
| 22 | #define MNT "/tmp/binfmt_selfpin"
|
| 23 | #define BACKING "/tmp/binfmt_selfpin_back"
|
| 24 | #define LOWER BACKING "/lower"
|
| 25 | #define MERGED "/tmp/binfmt_selfpin_merged"
|
| 26 |
|
| 27 | #define MAGIC "\\xde\\xad"
|
| 28 | #define RULE(interp) ":selfpin:M::" MAGIC "::" interp ":F"
|
| 29 |
|
| 30 | #define INTERP "/proc/self/exe"
|
| 31 |
|
| 32 | #define OPTS_MAX (3 * PATH_MAX + 64)
|
| 33 |
|
| 34 | static int ensure_dir(const char *path)
|
| 35 | {
|
| 36 | if (mkdir(path, 0755) && errno != EEXIST)
|
| 37 | return -1;
|
| 38 | return 0;
|
| 39 | }
|
| 40 |
|
| 41 |
|
| 42 | static int register_at(struct __test_metadata *_metadata, const char *rule)
|
| 43 | {
|
| 44 | int fd, saved;
|
| 45 | ssize_t n;
|
| 46 |
|
| 47 | fd = open(MNT "/register", O_WRONLY);
|
| 48 | ASSERT_GE(fd, 0);
|
| 49 | n = write(fd, rule, strlen(rule));
|
| 50 | saved = errno;
|
| 51 | close(fd);
|
| 52 | errno = saved;
|
| 53 | return n < 0 ? -1 : 0;
|
| 54 | }
|
| 55 |
|
| 56 | |
| 57 | * Mount an overlay over @lower using a private upper/work pair, so the two
|
| 58 | * mounts this test performs cannot interfere with each other and neither
|
| 59 | * overlaps the lower layer.
|
| 60 |
|
| 61 | static int mount_overlay(const char *lower, int nr)
|
| 62 | {
|
| 63 | char opts[OPTS_MAX], upper[PATH_MAX], work[PATH_MAX];
|
| 64 |
|
| 65 | snprintf(upper, sizeof(upper), "%s/upper%d", BACKING, nr);
|
| 66 | snprintf(work, sizeof(work), "%s/work%d", BACKING, nr);
|
| 67 | if (mkdir(upper, 0755) || mkdir(work, 0755))
|
| 68 | return -1;
|
| 69 |
|
| 70 | snprintf(opts, sizeof(opts), "lowerdir=%s,upperdir=%s,workdir=%s",
|
| 71 | lower, upper, work);
|
| 72 | return mount("ovl", MERGED, "overlay", 0, opts);
|
| 73 | }
|
| 74 |
|
| 75 | FIXTURE(selfpin) {
|
| 76 | };
|
| 77 |
|
| 78 | FIXTURE_SETUP(selfpin)
|
| 79 | {
|
| 80 |
|
| 81 | if (access("/proc/self/ns/user", F_OK))
|
| 82 | SKIP(return, "kernel without user namespaces");
|
| 83 | ASSERT_EQ(setup_userns(), 0);
|
| 84 |
|
| 85 | ASSERT_EQ(ensure_dir(MNT), 0);
|
| 86 | if (mount("binfmt_misc", MNT, "binfmt_misc", 0, NULL)) {
|
| 87 | int saved = errno;
|
| 88 |
|
| 89 |
|
| 90 | rmdir(MNT);
|
| 91 | SKIP(return, "no binfmt_misc: %s", strerror(saved));
|
| 92 | }
|
| 93 | }
|
| 94 |
|
| 95 | FIXTURE_TEARDOWN(selfpin)
|
| 96 | {
|
| 97 |
|
| 98 | umount2(MERGED, MNT_DETACH);
|
| 99 | umount2(BACKING, MNT_DETACH);
|
| 100 | umount2(MNT, MNT_DETACH);
|
| 101 | rmdir(MERGED);
|
| 102 | rmdir(BACKING);
|
| 103 | rmdir(MNT);
|
| 104 | }
|
| 105 |
|
| 106 | |
| 107 | * The instance's own files are regular files the mounter owns, so they can be
|
| 108 | * made executable. Opening one for exec still has to fail, otherwise the entry
|
| 109 | * pins the very superblock it lives in.
|
| 110 |
|
| 111 | TEST_F(selfpin, interpreter_on_the_instance)
|
| 112 | {
|
| 113 | ASSERT_EQ(chmod(MNT "/status", 0755), 0);
|
| 114 |
|
| 115 | ASSERT_NE(register_at(_metadata, RULE(MNT "/status")), 0);
|
| 116 | EXPECT_EQ(errno, EACCES);
|
| 117 | }
|
| 118 |
|
| 119 |
|
| 120 | TEST_F(selfpin, interpreter_on_an_entry)
|
| 121 | {
|
| 122 | ASSERT_EQ(register_at(_metadata, ":victim:M::" MAGIC "::" INTERP ":"), 0);
|
| 123 | ASSERT_EQ(chmod(MNT "/victim", 0755), 0);
|
| 124 |
|
| 125 | ASSERT_NE(register_at(_metadata, RULE(MNT "/victim")), 0);
|
| 126 | EXPECT_EQ(errno, EACCES);
|
| 127 | }
|
| 128 |
|
| 129 | |
| 130 | * A stacking filesystem holds a private clone of each layer for its whole
|
| 131 | * lifetime, so an instance used as a layer can be pinned by an interpreter
|
| 132 | * that does not live on it at all. Refuse to be a layer.
|
| 133 |
|
| 134 | TEST_F(selfpin, refuses_to_be_stacked_on)
|
| 135 | {
|
| 136 | ASSERT_EQ(ensure_dir(BACKING), 0);
|
| 137 | ASSERT_EQ(mount("tmpfs", BACKING, "tmpfs", 0, NULL), 0);
|
| 138 | ASSERT_EQ(mkdir(LOWER, 0755), 0);
|
| 139 | ASSERT_EQ(ensure_dir(MERGED), 0);
|
| 140 |
|
| 141 |
|
| 142 | if (mount_overlay(LOWER, 1)) {
|
| 143 | if (errno == ENODEV || errno == EPERM)
|
| 144 | SKIP(return, "no unprivileged overlayfs");
|
| 145 | SKIP(return, "overlayfs unusable here: %s", strerror(errno));
|
| 146 | }
|
| 147 | ASSERT_EQ(umount(MERGED), 0);
|
| 148 |
|
| 149 | EXPECT_NE(mount_overlay(MNT, 2), 0);
|
| 150 | }
|
| 151 |
|
| 152 |
|
| 153 | TEST_F(selfpin, ordinary_interpreter_still_works)
|
| 154 | {
|
| 155 | EXPECT_EQ(register_at(_metadata, RULE(INTERP)), 0);
|
| 156 | }
|
| 157 |
|
| 158 | TEST_HARNESS_MAIN
|