From 7973f564687f7e6dc5bcfee09aec3cb19ed34f1d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 9 Nov 2019 11:50:25 +0100 Subject: [PATCH] test-bpf-devices: new test for the devices bpf code --- src/core/bpf-devices.c | 18 +-- src/test/meson.build | 10 ++ src/test/test-bpf-devices.c | 268 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 8 deletions(-) create mode 100644 src/test/test-bpf-devices.c diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c index 60cc2f6..2683db8 100644 --- a/src/core/bpf-devices.c +++ b/src/core/bpf-devices.c @@ -172,14 +172,14 @@ int bpf_devices_apply_policy( bool whitelist, const char *cgroup_path, BPFProgram **prog_installed) { + + _cleanup_free_ char *controller_path = NULL; int r; /* This will assign *keep_program if everything goes well. */ - if (!prog) { - /* Remove existing program. */ - *prog_installed = bpf_program_unref(*prog_installed); - return 0; - } + + if (!prog) + goto finish; const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !whitelist; @@ -213,7 +213,6 @@ int bpf_devices_apply_policy( if (r < 0) return log_error_errno(r, "Extending device control BPF program failed: %m"); - _cleanup_free_ char *controller_path = NULL; r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, NULL, &controller_path); if (r < 0) return log_error_errno(r, "Failed to determine cgroup path: %m"); @@ -223,9 +222,12 @@ int bpf_devices_apply_policy( return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m", cgroup_path); + finish: /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */ - bpf_program_unref(*prog_installed); - *prog_installed = bpf_program_ref(prog); + if (prog_installed) { + bpf_program_unref(*prog_installed); + *prog_installed = bpf_program_ref(prog); + } return 0; } diff --git a/src/test/meson.build b/src/test/meson.build index 10ac3c9..b8ef163 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -425,6 +425,16 @@ tests += [ [libbasic], []], + [['src/test/test-bpf-devices.c'], + [libcore, + libshared], + [libmount, + threads, + librt, + libseccomp, + libselinux, + libblkid]], + [['src/test/test-bpf-firewall.c'], [libcore, libshared], diff --git a/src/test/test-bpf-devices.c b/src/test/test-bpf-devices.c new file mode 100644 index 0000000..aef9d35 --- /dev/null +++ b/src/test/test-bpf-devices.c @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include + +#include "alloc-util.h" +#include "bpf-devices.h" +#include "bpf-program.h" +#include "cgroup-setup.h" +#include "errno-list.h" +#include "fd-util.h" +#include "fs-util.h" +#include "path-util.h" +#include "tests.h" + +static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_prog) { + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; + unsigned wrong = 0; + int r; + + log_info("/* %s */", __func__); + + r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true); + assert_se(r >= 0); + + r = bpf_devices_whitelist_static(prog, cgroup_path); + assert_se(r >= 0); + + r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog); + assert_se(r >= 0); + + const char *s; + FOREACH_STRING(s, "/dev/null", + "/dev/zero", + "/dev/full", + "/dev/random", + "/dev/urandom", + "/dev/tty", + "/dev/ptmx") { + _cleanup_close_ int fd, fd2; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd < 0 && errno == EPERM; + /* We ignore errors other than EPERM, e.g. ENOENT or ENXIO */ + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 < 0 && errno == EPERM; + } + assert_se(wrong == 0); +} + +static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_prog) { + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; + unsigned wrong = 0; + int r; + + log_info("/* %s */", __func__); + + r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); + assert_se(r >= 0); + + r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/null", "rw"); + assert_se(r >= 0); + + r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/random", "r"); + assert_se(r >= 0); + + r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/zero", "w"); + assert_se(r >= 0); + + r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); + assert_se(r >= 0); + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/null"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd < 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 < 0; + } + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/random"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd < 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 >= 0; + } + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/zero"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd >= 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 < 0; + } + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/full"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd >= 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 >= 0; + } + + assert_se(wrong == 0); +} + +static void test_policy_whitelist_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) { + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; + unsigned wrong = 0; + int r; + + log_info("/* %s(%s) */", __func__, pattern); + + r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); + assert_se(r >= 0); + + r = bpf_devices_whitelist_major(prog, cgroup_path, pattern, 'c', "rw"); + assert_se(r >= 0); + + r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); + assert_se(r >= 0); + + /* /dev/null, /dev/full have major==1, /dev/tty has major==5 */ + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/null"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd < 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 < 0; + } + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/full"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd < 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 < 0; + } + + { + _cleanup_close_ int fd, fd2; + const char *s = "/dev/tty"; + + fd = open(s, O_CLOEXEC|O_RDONLY|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + wrong += fd >= 0; + + fd2 = open(s, O_CLOEXEC|O_WRONLY|O_NOCTTY); + log_debug("open(%s, \"w\") = %d/%s", s, fd2, fd2 < 0 ? errno_to_name(errno) : "-"); + wrong += fd2 >= 0; + } + + assert_se(wrong == 0); +} + +static void test_policy_whitelist_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) { + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; + unsigned wrong = 0; + int r; + + log_info("/* %s(type=%c) */", __func__, type); + + r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); + assert_se(r >= 0); + + r = bpf_devices_whitelist_major(prog, cgroup_path, "*", type, "rw"); + assert_se(r >= 0); + + r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); + assert_se(r >= 0); + + { + _cleanup_close_ int fd; + const char *s = "/dev/null"; + + fd = open(s, O_CLOEXEC|O_RDWR|O_NOCTTY); + log_debug("open(%s, \"r\") = %d/%s", s, fd, fd < 0 ? errno_to_name(errno) : "-"); + if (type == 'c') + wrong += fd < 0; + else + wrong += fd >= 0; + } + + assert_se(wrong == 0); +} + +int main(int argc, char *argv[]) { + _cleanup_free_ char *cgroup = NULL, *parent = NULL; + _cleanup_(rmdir_and_freep) char *controller_path = NULL; + CGroupMask supported; + struct rlimit rl; + int r; + + test_setup_logging(LOG_DEBUG); + + assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0); + rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE); + (void) setrlimit(RLIMIT_MEMLOCK, &rl); + + if (!can_memlock()) + return log_tests_skipped("Can't use mlock()"); + + r = enter_cgroup_subroot(&cgroup); + if (r == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); + + r = bpf_devices_supported(); + if (!r) + return log_tests_skipped("BPF device filter not supported"); + assert_se(r == 1); + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &controller_path); + assert_se(r >= 0); + + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; + + test_policy_closed(cgroup, &prog); + test_policy_strict(cgroup, &prog); + + test_policy_whitelist_major("mem", cgroup, &prog); + test_policy_whitelist_major("1", cgroup, &prog); + + test_policy_whitelist_major_star('c', cgroup, &prog); + test_policy_whitelist_major_star('b', cgroup, &prog); + + assert_se(parent = dirname_malloc(cgroup)); + + assert_se(cg_mask_supported(&supported) >= 0); + r = cg_attach_everywhere(supported, parent, 0, NULL, NULL); + assert_se(r >= 0); + + return 0; +} -- 2.7.4