projects/linux·blob master · refs

232 lines · 6.1 KiB

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