1*f2b69ea2SChristian Brauner // SPDX-License-Identifier: GPL-2.0 2*f2b69ea2SChristian Brauner /* 3*f2b69ea2SChristian Brauner * A pre-opened interpreter - what 'F' gives a static entry and what a 'B' 4*f2b69ea2SChristian Brauner * entry binds - keeps a file open for as long as the entry lives, so it pins 5*f2b69ea2SChristian Brauner * the mount it came from. It costs no file descriptor, and binfmt_misc is 6*f2b69ea2SChristian Brauner * FS_USERNS_MOUNT, so an unprivileged user namespace can create them without 7*f2b69ea2SChristian Brauner * bound. Check that UCOUNT_BINFMT_MISC_INTERPRETERS bounds it, that an entry 8*f2b69ea2SChristian Brauner * that pre-opens nothing is not charged, that removing an entry gives the 9*f2b69ea2SChristian Brauner * charge back, and that nesting a user namespace does not evade it. 10*f2b69ea2SChristian Brauner * 11*f2b69ea2SChristian Brauner * Runs unprivileged in a user namespace. 12*f2b69ea2SChristian Brauner */ 13*f2b69ea2SChristian Brauner #define _GNU_SOURCE 14*f2b69ea2SChristian Brauner #include <errno.h> 15*f2b69ea2SChristian Brauner #include <fcntl.h> 16*f2b69ea2SChristian Brauner #include <limits.h> 17*f2b69ea2SChristian Brauner #include <stdio.h> 18*f2b69ea2SChristian Brauner #include <string.h> 19*f2b69ea2SChristian Brauner #include <sys/mount.h> 20*f2b69ea2SChristian Brauner #include <sys/stat.h> 21*f2b69ea2SChristian Brauner #include <unistd.h> 22*f2b69ea2SChristian Brauner 23*f2b69ea2SChristian Brauner #include "../filesystems/utils.h" 24*f2b69ea2SChristian Brauner #include "kselftest_harness.h" 25*f2b69ea2SChristian Brauner 26*f2b69ea2SChristian Brauner #define MNT "/tmp/binfmt_interplimit" 27*f2b69ea2SChristian Brauner #define NESTED_MNT "/tmp/binfmt_interplimit_nested" 28*f2b69ea2SChristian Brauner #define LIMIT_SYSCTL "/proc/sys/user/max_binfmt_misc_interpreters" 29*f2b69ea2SChristian Brauner 30*f2b69ea2SChristian Brauner #define MAGIC "\\xde\\xad" 31*f2b69ea2SChristian Brauner /* Not on the instance, and unlike /bin/true it always exists. */ 32*f2b69ea2SChristian Brauner #define INTERP "/proc/self/exe" 33*f2b69ea2SChristian Brauner 34*f2b69ea2SChristian Brauner /* Small enough to fill by hand, big enough that a refund is visible. */ 35*f2b69ea2SChristian Brauner #define LIMIT 4 36*f2b69ea2SChristian Brauner 37*f2b69ea2SChristian Brauner /* What UCOUNT_ENTRY() lets a namespace raise its own limit to. */ 38*f2b69ea2SChristian Brauner #define LIMIT_MAX "2147483647" 39*f2b69ea2SChristian Brauner 40*f2b69ea2SChristian Brauner static int ensure_dir(const char *path) 41*f2b69ea2SChristian Brauner { 42*f2b69ea2SChristian Brauner if (mkdir(path, 0755) && errno != EEXIST) 43*f2b69ea2SChristian Brauner return -1; 44*f2b69ea2SChristian Brauner return 0; 45*f2b69ea2SChristian Brauner } 46*f2b69ea2SChristian Brauner 47*f2b69ea2SChristian Brauner /* Write @val to @path, preserving write(2)'s errno for the caller. */ 48*f2b69ea2SChristian Brauner static int write_keep_errno(const char *path, const char *val) 49*f2b69ea2SChristian Brauner { 50*f2b69ea2SChristian Brauner int fd, saved; 51*f2b69ea2SChristian Brauner ssize_t n; 52*f2b69ea2SChristian Brauner 53*f2b69ea2SChristian Brauner fd = open(path, O_WRONLY | O_CLOEXEC); 54*f2b69ea2SChristian Brauner if (fd < 0) 55*f2b69ea2SChristian Brauner return -1; 56*f2b69ea2SChristian Brauner n = write(fd, val, strlen(val)); 57*f2b69ea2SChristian Brauner saved = errno; 58*f2b69ea2SChristian Brauner close(fd); 59*f2b69ea2SChristian Brauner errno = saved; 60*f2b69ea2SChristian Brauner return n < 0 ? -1 : 0; 61*f2b69ea2SChristian Brauner } 62*f2b69ea2SChristian Brauner 63*f2b69ea2SChristian Brauner static int set_limit(const char *val) 64*f2b69ea2SChristian Brauner { 65*f2b69ea2SChristian Brauner return write_keep_errno(LIMIT_SYSCTL, val); 66*f2b69ea2SChristian Brauner } 67*f2b69ea2SChristian Brauner 68*f2b69ea2SChristian Brauner static int register_at(const char *mnt, const char *rule) 69*f2b69ea2SChristian Brauner { 70*f2b69ea2SChristian Brauner char path[PATH_MAX]; 71*f2b69ea2SChristian Brauner 72*f2b69ea2SChristian Brauner snprintf(path, sizeof(path), "%s/register", mnt); 73*f2b69ea2SChristian Brauner return write_keep_errno(path, rule); 74*f2b69ea2SChristian Brauner } 75*f2b69ea2SChristian Brauner 76*f2b69ea2SChristian Brauner /* An 'F' entry: one interpreter pre-opened at registration, one charge. */ 77*f2b69ea2SChristian Brauner static int register_fixed(const char *mnt, const char *name) 78*f2b69ea2SChristian Brauner { 79*f2b69ea2SChristian Brauner char rule[PATH_MAX]; 80*f2b69ea2SChristian Brauner 81*f2b69ea2SChristian Brauner snprintf(rule, sizeof(rule), ":%s:M::" MAGIC "::" INTERP ":F", name); 82*f2b69ea2SChristian Brauner return register_at(mnt, rule); 83*f2b69ea2SChristian Brauner } 84*f2b69ea2SChristian Brauner 85*f2b69ea2SChristian Brauner /* The same entry without 'F': the interpreter is opened per exec instead. */ 86*f2b69ea2SChristian Brauner static int register_plain(const char *mnt, const char *name) 87*f2b69ea2SChristian Brauner { 88*f2b69ea2SChristian Brauner char rule[PATH_MAX]; 89*f2b69ea2SChristian Brauner 90*f2b69ea2SChristian Brauner snprintf(rule, sizeof(rule), ":%s:M::" MAGIC "::" INTERP ":", name); 91*f2b69ea2SChristian Brauner return register_at(mnt, rule); 92*f2b69ea2SChristian Brauner } 93*f2b69ea2SChristian Brauner 94*f2b69ea2SChristian Brauner static int remove_entry(const char *mnt, const char *name) 95*f2b69ea2SChristian Brauner { 96*f2b69ea2SChristian Brauner char path[PATH_MAX]; 97*f2b69ea2SChristian Brauner 98*f2b69ea2SChristian Brauner snprintf(path, sizeof(path), "%s/%s", mnt, name); 99*f2b69ea2SChristian Brauner return write_keep_errno(path, "-1\n"); 100*f2b69ea2SChristian Brauner } 101*f2b69ea2SChristian Brauner 102*f2b69ea2SChristian Brauner static bool entry_exists(const char *mnt, const char *name) 103*f2b69ea2SChristian Brauner { 104*f2b69ea2SChristian Brauner char path[PATH_MAX]; 105*f2b69ea2SChristian Brauner 106*f2b69ea2SChristian Brauner snprintf(path, sizeof(path), "%s/%s", mnt, name); 107*f2b69ea2SChristian Brauner return access(path, F_OK) == 0; 108*f2b69ea2SChristian Brauner } 109*f2b69ea2SChristian Brauner 110*f2b69ea2SChristian Brauner /* Register @n 'F' entries, each with a name of its own. */ 111*f2b69ea2SChristian Brauner static int fill_budget(const char *mnt, unsigned int n) 112*f2b69ea2SChristian Brauner { 113*f2b69ea2SChristian Brauner char name[32]; 114*f2b69ea2SChristian Brauner unsigned int i; 115*f2b69ea2SChristian Brauner 116*f2b69ea2SChristian Brauner for (i = 0; i < n; i++) { 117*f2b69ea2SChristian Brauner snprintf(name, sizeof(name), "fixed%u", i); 118*f2b69ea2SChristian Brauner if (register_fixed(mnt, name)) 119*f2b69ea2SChristian Brauner return -1; 120*f2b69ea2SChristian Brauner } 121*f2b69ea2SChristian Brauner return 0; 122*f2b69ea2SChristian Brauner } 123*f2b69ea2SChristian Brauner 124*f2b69ea2SChristian Brauner FIXTURE(interp_limit) { 125*f2b69ea2SChristian Brauner }; 126*f2b69ea2SChristian Brauner 127*f2b69ea2SChristian Brauner FIXTURE_SETUP(interp_limit) 128*f2b69ea2SChristian Brauner { 129*f2b69ea2SChristian Brauner /* setup_userns() exits rather than returns if this is not there. */ 130*f2b69ea2SChristian Brauner if (access("/proc/self/ns/user", F_OK)) 131*f2b69ea2SChristian Brauner SKIP(return, "kernel without user namespaces"); 132*f2b69ea2SChristian Brauner ASSERT_EQ(setup_userns(), 0); 133*f2b69ea2SChristian Brauner 134*f2b69ea2SChristian Brauner /* CAP_SYS_RESOURCE in this namespace is what makes it writable. */ 135*f2b69ea2SChristian Brauner if (set_limit(LIMIT_MAX)) { 136*f2b69ea2SChristian Brauner if (errno == ENOENT) 137*f2b69ea2SChristian Brauner SKIP(return, "kernel without " LIMIT_SYSCTL); 138*f2b69ea2SChristian Brauner SKIP(return, "cannot set the limit: %s", strerror(errno)); 139*f2b69ea2SChristian Brauner } 140*f2b69ea2SChristian Brauner 141*f2b69ea2SChristian Brauner ASSERT_EQ(ensure_dir(MNT), 0); 142*f2b69ea2SChristian Brauner if (mount("binfmt_misc", MNT, "binfmt_misc", 0, NULL)) { 143*f2b69ea2SChristian Brauner int saved = errno; 144*f2b69ea2SChristian Brauner 145*f2b69ea2SChristian Brauner /* Teardown doesn't run when setup skips, so clean up here. */ 146*f2b69ea2SChristian Brauner rmdir(MNT); 147*f2b69ea2SChristian Brauner SKIP(return, "no binfmt_misc: %s", strerror(saved)); 148*f2b69ea2SChristian Brauner } 149*f2b69ea2SChristian Brauner } 150*f2b69ea2SChristian Brauner 151*f2b69ea2SChristian Brauner FIXTURE_TEARDOWN(interp_limit) 152*f2b69ea2SChristian Brauner { 153*f2b69ea2SChristian Brauner /* The namespaces go with the process; just don't litter /tmp. */ 154*f2b69ea2SChristian Brauner umount2(NESTED_MNT, MNT_DETACH); 155*f2b69ea2SChristian Brauner umount2(MNT, MNT_DETACH); 156*f2b69ea2SChristian Brauner rmdir(NESTED_MNT); 157*f2b69ea2SChristian Brauner rmdir(MNT); 158*f2b69ea2SChristian Brauner } 159*f2b69ea2SChristian Brauner 160*f2b69ea2SChristian Brauner /* Every pre-opened interpreter is charged, and the budget is a hard stop. */ 161*f2b69ea2SChristian Brauner TEST_F(interp_limit, fixed_interpreters_are_charged) 162*f2b69ea2SChristian Brauner { 163*f2b69ea2SChristian Brauner char buf[32]; 164*f2b69ea2SChristian Brauner 165*f2b69ea2SChristian Brauner snprintf(buf, sizeof(buf), "%u", LIMIT); 166*f2b69ea2SChristian Brauner ASSERT_EQ(set_limit(buf), 0); 167*f2b69ea2SChristian Brauner 168*f2b69ea2SChristian Brauner ASSERT_EQ(fill_budget(MNT, LIMIT), 0); 169*f2b69ea2SChristian Brauner 170*f2b69ea2SChristian Brauner EXPECT_NE(register_fixed(MNT, "over"), 0); 171*f2b69ea2SChristian Brauner EXPECT_EQ(errno, ENOSPC); 172*f2b69ea2SChristian Brauner 173*f2b69ea2SChristian Brauner /* A refused registration leaves nothing behind. */ 174*f2b69ea2SChristian Brauner EXPECT_FALSE(entry_exists(MNT, "over")); 175*f2b69ea2SChristian Brauner } 176*f2b69ea2SChristian Brauner 177*f2b69ea2SChristian Brauner /* An entry that pre-opens nothing pins nothing, so it is not charged. */ 178*f2b69ea2SChristian Brauner TEST_F(interp_limit, plain_entries_are_not_charged) 179*f2b69ea2SChristian Brauner { 180*f2b69ea2SChristian Brauner ASSERT_EQ(set_limit("0"), 0); 181*f2b69ea2SChristian Brauner 182*f2b69ea2SChristian Brauner EXPECT_EQ(register_plain(MNT, "plain"), 0); 183*f2b69ea2SChristian Brauner EXPECT_TRUE(entry_exists(MNT, "plain")); 184*f2b69ea2SChristian Brauner 185*f2b69ea2SChristian Brauner /* ... while the same entry with 'F' has nothing to spend. */ 186*f2b69ea2SChristian Brauner EXPECT_NE(register_fixed(MNT, "fixed"), 0); 187*f2b69ea2SChristian Brauner EXPECT_EQ(errno, ENOSPC); 188*f2b69ea2SChristian Brauner } 189*f2b69ea2SChristian Brauner 190*f2b69ea2SChristian Brauner /* Removing an entry closes its interpreters and gives the charge back. */ 191*f2b69ea2SChristian Brauner TEST_F(interp_limit, removal_refunds_the_charge) 192*f2b69ea2SChristian Brauner { 193*f2b69ea2SChristian Brauner char buf[32]; 194*f2b69ea2SChristian Brauner 195*f2b69ea2SChristian Brauner snprintf(buf, sizeof(buf), "%u", LIMIT); 196*f2b69ea2SChristian Brauner ASSERT_EQ(set_limit(buf), 0); 197*f2b69ea2SChristian Brauner 198*f2b69ea2SChristian Brauner ASSERT_EQ(fill_budget(MNT, LIMIT), 0); 199*f2b69ea2SChristian Brauner ASSERT_NE(register_fixed(MNT, "over"), 0); 200*f2b69ea2SChristian Brauner 201*f2b69ea2SChristian Brauner ASSERT_EQ(remove_entry(MNT, "fixed0"), 0); 202*f2b69ea2SChristian Brauner EXPECT_EQ(register_fixed(MNT, "over"), 0); 203*f2b69ea2SChristian Brauner } 204*f2b69ea2SChristian Brauner 205*f2b69ea2SChristian Brauner /* 206*f2b69ea2SChristian Brauner * The charge walks the ancestors, so a namespace cannot buy itself budget by 207*f2b69ea2SChristian Brauner * nesting: it may raise only its own limit, and the parent it was created 208*f2b69ea2SChristian Brauner * from is charged for every binding made below it. 209*f2b69ea2SChristian Brauner */ 210*f2b69ea2SChristian Brauner TEST_F(interp_limit, nesting_does_not_evade_it) 211*f2b69ea2SChristian Brauner { 212*f2b69ea2SChristian Brauner char buf[32]; 213*f2b69ea2SChristian Brauner 214*f2b69ea2SChristian Brauner snprintf(buf, sizeof(buf), "%u", LIMIT); 215*f2b69ea2SChristian Brauner ASSERT_EQ(set_limit(buf), 0); 216*f2b69ea2SChristian Brauner ASSERT_EQ(fill_budget(MNT, LIMIT), 0); 217*f2b69ea2SChristian Brauner 218*f2b69ea2SChristian Brauner ASSERT_EQ(setup_userns(), 0); 219*f2b69ea2SChristian Brauner ASSERT_EQ(set_limit(LIMIT_MAX), 0); 220*f2b69ea2SChristian Brauner 221*f2b69ea2SChristian Brauner ASSERT_EQ(ensure_dir(NESTED_MNT), 0); 222*f2b69ea2SChristian Brauner ASSERT_EQ(mount("binfmt_misc", NESTED_MNT, "binfmt_misc", 0, NULL), 0); 223*f2b69ea2SChristian Brauner 224*f2b69ea2SChristian Brauner /* A fresh instance with an unlimited budget of its own, and yet: */ 225*f2b69ea2SChristian Brauner EXPECT_NE(register_fixed(NESTED_MNT, "nested"), 0); 226*f2b69ea2SChristian Brauner EXPECT_EQ(errno, ENOSPC); 227*f2b69ea2SChristian Brauner 228*f2b69ea2SChristian Brauner /* The nested instance works for anything that pins no file. */ 229*f2b69ea2SChristian Brauner EXPECT_EQ(register_plain(NESTED_MNT, "nested_plain"), 0); 230*f2b69ea2SChristian Brauner } 231*f2b69ea2SChristian Brauner 232*f2b69ea2SChristian Brauner TEST_HARNESS_MAIN 233