1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/resource.h>
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"
14 #include "path-util.h"
17 static void test_policy_closed(const char *cgroup_path
, BPFProgram
**installed_prog
) {
18 _cleanup_(bpf_program_freep
) BPFProgram
*prog
= NULL
;
22 log_info("/* %s */", __func__
);
24 r
= bpf_devices_cgroup_init(&prog
, CGROUP_DEVICE_POLICY_CLOSED
, true);
27 r
= bpf_devices_allow_list_static(prog
, cgroup_path
);
30 r
= bpf_devices_apply_policy(&prog
, CGROUP_DEVICE_POLICY_CLOSED
, true, cgroup_path
, installed_prog
);
33 FOREACH_STRING(s
, "/dev/null",
40 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
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 */
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
;
51 assert_se(wrong
== 0);
54 static void test_policy_strict(const char *cgroup_path
, BPFProgram
**installed_prog
) {
55 _cleanup_(bpf_program_freep
) BPFProgram
*prog
= NULL
;
59 log_info("/* %s */", __func__
);
61 r
= bpf_devices_cgroup_init(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true);
64 r
= bpf_devices_allow_list_device(prog
, cgroup_path
, "/dev/null", CGROUP_DEVICE_READ
|CGROUP_DEVICE_WRITE
);
67 r
= bpf_devices_allow_list_device(prog
, cgroup_path
, "/dev/random", CGROUP_DEVICE_READ
);
70 r
= bpf_devices_allow_list_device(prog
, cgroup_path
, "/dev/zero", CGROUP_DEVICE_WRITE
);
73 r
= bpf_devices_apply_policy(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true, cgroup_path
, installed_prog
);
77 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
78 const char *s
= "/dev/null";
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
) : "-");
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
) : "-");
90 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
91 const char *s
= "/dev/random";
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
) : "-");
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
) : "-");
103 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
104 const char *s
= "/dev/zero";
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
) : "-");
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
) : "-");
116 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
117 const char *s
= "/dev/full";
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
) : "-");
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
) : "-");
128 assert_se(wrong
== 0);
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
;
136 log_info("/* %s(%s) */", __func__
, pattern
);
138 r
= bpf_devices_cgroup_init(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true);
141 r
= bpf_devices_allow_list_major(prog
, cgroup_path
, pattern
, 'c', CGROUP_DEVICE_READ
|CGROUP_DEVICE_WRITE
);
144 r
= bpf_devices_apply_policy(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true, cgroup_path
, installed_prog
);
147 /* /dev/null, /dev/full have major==1, /dev/tty has major==5 */
149 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
150 const char *s
= "/dev/null";
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
) : "-");
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
) : "-");
162 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
163 const char *s
= "/dev/full";
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
) : "-");
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
) : "-");
175 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
;
176 const char *s
= "/dev/tty";
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
) : "-");
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
) : "-");
187 assert_se(wrong
== 0);
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
;
195 log_info("/* %s(type=%c) */", __func__
, type
);
197 r
= bpf_devices_cgroup_init(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true);
200 r
= bpf_devices_allow_list_major(prog
, cgroup_path
, "*", type
, CGROUP_DEVICE_READ
|CGROUP_DEVICE_WRITE
);
203 r
= bpf_devices_apply_policy(&prog
, CGROUP_DEVICE_POLICY_STRICT
, true, cgroup_path
, installed_prog
);
207 _cleanup_close_
int fd
= -EBADF
;
208 const char *s
= "/dev/null";
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
) : "-");
218 assert_se(wrong
== 0);
221 static void test_policy_empty(bool add_mismatched
, const char *cgroup_path
, BPFProgram
**installed_prog
) {
222 _cleanup_(bpf_program_freep
) BPFProgram
*prog
= NULL
;
226 log_info("/* %s(add_mismatched=%s) */", __func__
, yes_no(add_mismatched
));
228 r
= bpf_devices_cgroup_init(&prog
, CGROUP_DEVICE_POLICY_STRICT
, add_mismatched
);
231 if (add_mismatched
) {
232 r
= bpf_devices_allow_list_major(prog
, cgroup_path
, "foobarxxx", 'c', CGROUP_DEVICE_READ
|CGROUP_DEVICE_WRITE
);
236 r
= bpf_devices_apply_policy(&prog
, CGROUP_DEVICE_POLICY_STRICT
, false, cgroup_path
, installed_prog
);
240 _cleanup_close_
int fd
= -EBADF
;
241 const char *s
= "/dev/null";
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
) : "-");
248 assert_se(wrong
== 0);
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
;
259 test_setup_logging(LOG_DEBUG
);
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
);
265 r
= cg_all_unified();
267 return log_tests_skipped("We don't seem to be running with unified cgroup hierarchy");
270 return log_tests_skipped("Can't use mlock()");
272 r
= enter_cgroup_subroot(&cgroup
);
274 return log_tests_skipped("cgroupfs not available");
276 return log_tests_skipped_errno(r
, "Failed to prepare cgroup subtree");
278 r
= bpf_devices_supported();
280 return log_tests_skipped("BPF device filter not supported");
283 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, NULL
, &controller_path
);
286 _cleanup_(bpf_program_freep
) BPFProgram
*prog
= NULL
;
288 test_policy_closed(cgroup
, &prog
);
289 test_policy_strict(cgroup
, &prog
);
291 test_policy_allow_list_major("mem", cgroup
, &prog
);
292 test_policy_allow_list_major("1", cgroup
, &prog
);
294 test_policy_allow_list_major_star('c', cgroup
, &prog
);
295 test_policy_allow_list_major_star('b', cgroup
, &prog
);
297 test_policy_empty(false, cgroup
, &prog
);
298 test_policy_empty(true, cgroup
, &prog
);
300 ASSERT_OK(path_extract_directory(cgroup
, &parent
));
302 ASSERT_OK(cg_mask_supported(&supported
));
303 r
= cg_attach_everywhere(supported
, parent
, 0, NULL
, NULL
);