]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-bpf-devices.c
Merge pull request #31648 from neighbourhoodie/review-content
[thirdparty/systemd.git] / src / test / test-bpf-devices.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/resource.h>
4 #include <sys/time.h>
5 #include <unistd.h>
6
7 #include "alloc-util.h"
8 #include "bpf-devices.h"
9 #include "bpf-program.h"
10 #include "cgroup-setup.h"
11 #include "errno-list.h"
12 #include "fd-util.h"
13 #include "fs-util.h"
14 #include "path-util.h"
15 #include "tests.h"
16
17 static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_prog) {
18 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
19 unsigned wrong = 0;
20 int r;
21
22 log_info("/* %s */", __func__);
23
24 r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true);
25 ASSERT_OK(r);
26
27 r = bpf_devices_allow_list_static(prog, cgroup_path);
28 ASSERT_OK(r);
29
30 r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
31 ASSERT_OK(r);
32
33 FOREACH_STRING(s, "/dev/null",
34 "/dev/zero",
35 "/dev/full",
36 "/dev/random",
37 "/dev/urandom",
38 "/dev/tty",
39 "/dev/ptmx") {
40 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
41
42 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
43 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
44 wrong += fd < 0 && errno == EPERM;
45 /* We ignore errors other than EPERM, e.g. ENOENT or ENXIO */
46
47 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
48 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
49 wrong += fd2 < 0 && errno == EPERM;
50 }
51 assert_se(wrong == 0);
52 }
53
54 static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_prog) {
55 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
56 unsigned wrong = 0;
57 int r;
58
59 log_info("/* %s */", __func__);
60
61 r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
62 ASSERT_OK(r);
63
64 r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
65 ASSERT_OK(r);
66
67 r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", CGROUP_DEVICE_READ);
68 ASSERT_OK(r);
69
70 r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", CGROUP_DEVICE_WRITE);
71 ASSERT_OK(r);
72
73 r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
74 ASSERT_OK(r);
75
76 {
77 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
78 const char *s = "/dev/null";
79
80 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
81 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
82 wrong += fd < 0;
83
84 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
85 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
86 wrong += fd2 < 0;
87 }
88
89 {
90 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
91 const char *s = "/dev/random";
92
93 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
94 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
95 wrong += fd < 0;
96
97 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
98 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
99 wrong += fd2 >= 0;
100 }
101
102 {
103 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
104 const char *s = "/dev/zero";
105
106 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
107 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
108 wrong += fd >= 0;
109
110 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
111 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
112 wrong += fd2 < 0;
113 }
114
115 {
116 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
117 const char *s = "/dev/full";
118
119 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
120 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
121 wrong += fd >= 0;
122
123 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
124 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
125 wrong += fd2 >= 0;
126 }
127
128 assert_se(wrong == 0);
129 }
130
131 static void test_policy_allow_list_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) {
132 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
133 unsigned wrong = 0;
134 int r;
135
136 log_info("/* %s(%s) */", __func__, pattern);
137
138 r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
139 ASSERT_OK(r);
140
141 r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
142 ASSERT_OK(r);
143
144 r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
145 ASSERT_OK(r);
146
147 /* /dev/null, /dev/full have major==1, /dev/tty has major==5 */
148 {
149 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
150 const char *s = "/dev/null";
151
152 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
153 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
154 wrong += fd < 0;
155
156 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
157 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
158 wrong += fd2 < 0;
159 }
160
161 {
162 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
163 const char *s = "/dev/full";
164
165 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
166 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
167 wrong += fd < 0;
168
169 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
170 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
171 wrong += fd2 < 0;
172 }
173
174 {
175 _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
176 const char *s = "/dev/tty";
177
178 fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY);
179 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
180 wrong += fd >= 0;
181
182 fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY);
183 log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-");
184 wrong += fd2 >= 0;
185 }
186
187 assert_se(wrong == 0);
188 }
189
190 static void test_policy_allow_list_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) {
191 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
192 unsigned wrong = 0;
193 int r;
194
195 log_info("/* %s(type=%c) */", __func__, type);
196
197 r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
198 ASSERT_OK(r);
199
200 r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
201 ASSERT_OK(r);
202
203 r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
204 ASSERT_OK(r);
205
206 {
207 _cleanup_close_ int fd = -EBADF;
208 const char *s = "/dev/null";
209
210 fd = open(s, O_CLOEXEC|O_RDWR|O_NOCTTY);
211 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
212 if (type == 'c')
213 wrong += fd < 0;
214 else
215 wrong += fd >= 0;
216 }
217
218 assert_se(wrong == 0);
219 }
220
221 static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFProgram **installed_prog) {
222 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
223 unsigned wrong = 0;
224 int r;
225
226 log_info("/* %s(add_mismatched=%s) */", __func__, yes_no(add_mismatched));
227
228 r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, add_mismatched);
229 ASSERT_OK(r);
230
231 if (add_mismatched) {
232 r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
233 assert_se(r < 0);
234 }
235
236 r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, false, cgroup_path, installed_prog);
237 ASSERT_OK(r);
238
239 {
240 _cleanup_close_ int fd = -EBADF;
241 const char *s = "/dev/null";
242
243 fd = open(s, O_CLOEXEC|O_RDWR|O_NOCTTY);
244 log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-");
245 wrong += fd >= 0;
246 }
247
248 assert_se(wrong == 0);
249 }
250
251
252 int main(int argc, char *argv[]) {
253 _cleanup_free_ char *cgroup = NULL, *parent = NULL;
254 _cleanup_(rmdir_and_freep) char *controller_path = NULL;
255 CGroupMask supported;
256 struct rlimit rl;
257 int r;
258
259 test_setup_logging(LOG_DEBUG);
260
261 ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
262 rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
263 (void) setrlimit(RLIMIT_MEMLOCK, &rl);
264
265 r = cg_all_unified();
266 if (r <= 0)
267 return log_tests_skipped("We don't seem to be running with unified cgroup hierarchy");
268
269 if (!can_memlock())
270 return log_tests_skipped("Can't use mlock()");
271
272 r = enter_cgroup_subroot(&cgroup);
273 if (r == -ENOMEDIUM)
274 return log_tests_skipped("cgroupfs not available");
275 if (r < 0)
276 return log_tests_skipped_errno(r, "Failed to prepare cgroup subtree");
277
278 r = bpf_devices_supported();
279 if (r == 0)
280 return log_tests_skipped("BPF device filter not supported");
281 ASSERT_EQ(r, 1);
282
283 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &controller_path);
284 ASSERT_OK(r);
285
286 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
287
288 test_policy_closed(cgroup, &prog);
289 test_policy_strict(cgroup, &prog);
290
291 test_policy_allow_list_major("mem", cgroup, &prog);
292 test_policy_allow_list_major("1", cgroup, &prog);
293
294 test_policy_allow_list_major_star('c', cgroup, &prog);
295 test_policy_allow_list_major_star('b', cgroup, &prog);
296
297 test_policy_empty(false, cgroup, &prog);
298 test_policy_empty(true, cgroup, &prog);
299
300 ASSERT_OK(path_extract_directory(cgroup, &parent));
301
302 ASSERT_OK(cg_mask_supported(&supported));
303 r = cg_attach_everywhere(supported, parent, 0, NULL, NULL);
304 ASSERT_OK(r);
305
306 return 0;
307 }