]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/bpf-restrict-fs: avoid loading the LSM BPF program twice at boot
authorEric Curtin <eric.curtin@docker.com>
Mon, 27 Jul 2026 10:42:37 +0000 (11:42 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 28 Jul 2026 12:33:38 +0000 (21:33 +0900)
bpf_restrict_fs_setup() is always called right after a successful
bpf_restrict_fs_supported(true) probe (see manager_setup() in
manager.c). Previously the probe independently opened, sized and
kernel-verifier-loaded the BPF object via prepare_restrict_fs_bpf(),
then did a trial LSM attach/detach via bpf_can_link_lsm_program() to
confirm the program *could* attach, and threw the whole object away
-- only for bpf_restrict_fs_setup() to build and verifier-load an
identical one from scratch again for the real, permanent attach.

The trial attach in the probe is redundant: if BPF_LSM_MAC attach
isn't actually usable (e.g. no BPF trampoline support on the running
architecture/kernel), bpf_restrict_fs_setup()'s own
sym_bpf_program__attach_lsm() call will simply fail, and that failure
is already logged and handled gracefully by its caller in manager.c
(logged as a warning, systemd continues without RestrictFileSystems=
enforcement). So bpf_restrict_fs_supported() only needs to check
whether the BPF LSM hook is enabled in the kernel at all
(lsm_supported("bpf")); it doesn't need to open/load/attach the BPF
program itself. Drop that from the probe, so the program is opened,
sized and verified by the kernel exactly once per boot instead of
twice.

Since the probe no longer verifies that the LSM BPF program can
actually attach, test-bpf-restrict-fs.c can no longer rely on
bpf_restrict_fs_supported(true) alone to skip on kernels/architectures
where the hook is listed but the real attach fails (e.g. missing BPF
trampoline support). Have the test also check m->restrict_fs after
manager_startup() and skip if the program never got attached, instead
of proceeding to hard-fail the enforcement assertions.

src/core/bpf-restrict-fs.c
src/test/test-bpf-restrict-fs.c

index a0bd4823b0d3a80a63f0b949a797361b4a99fbe1..8db05f4db35b6ccd15ae002a8b14e5aef119f301 100644 (file)
@@ -69,7 +69,6 @@ static int prepare_restrict_fs_bpf(struct restrict_fs_bpf **ret_obj) {
 }
 
 bool bpf_restrict_fs_supported(bool initialize) {
-        _cleanup_(restrict_fs_bpf_freep) struct restrict_fs_bpf *obj = NULL;
         static int supported = -1;
         int r;
 
@@ -95,15 +94,13 @@ bool bpf_restrict_fs_supported(bool initialize) {
                 return (supported = false);
         }
 
-        r = prepare_restrict_fs_bpf(&obj);
-        if (r < 0)
-                return (supported = false);
-
-        if (!bpf_can_link_lsm_program(obj->progs.restrict_filesystems)) {
-                log_warning("bpf-restrict-fs: Failed to link program; assuming BPF LSM is not available.");
-                return (supported = false);
-        }
-
+        /* We don't try to open/load/attach the BPF program here: that's the expensive part (a real kernel
+         * verifier pass plus, on the real attach in bpf_restrict_fs_setup(), an LSM link), and doing it
+         * twice (once here just to throw the result away, once for real in bpf_restrict_fs_setup()) means
+         * paying that cost twice on every boot for no benefit. If BPF_LSM_MAC attach isn't actually usable
+         * (e.g. no BPF trampoline support on this architecture/kernel), bpf_restrict_fs_setup()'s own
+         * attach will fail when it is later called, and that failure is already logged and handled
+         * gracefully by its caller. */
         return (supported = true);
 }
 
index 8e41537f21ff7fe0a61f69cfd7e045985318e130..810f7af02cfac5c5e8b60e1b864d2b3e27f0426c 100644 (file)
@@ -89,6 +89,13 @@ int main(int argc, char *argv[]) {
         ASSERT_OK(manager_new(RUNTIME_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m));
         ASSERT_OK(manager_startup(m, NULL, NULL, NULL, NULL));
 
+        /* bpf_restrict_fs_supported() above only checks that the BPF LSM hook is enabled in the kernel; it
+         * doesn't verify that the LSM BPF program managed to actually attach (e.g. some architectures/older
+         * kernels lack BPF trampoline support). If it didn't, manager_startup() already logged a warning
+         * and continued without enforcement, so skip here rather than hard-failing the assertions below. */
+        if (!m->restrict_fs)
+                return log_tests_skipped("Failed to attach LSM BPF program");
+
         /* We need to enable access to the filesystem where the binary is so we
          * add @common-block and @application */
         ASSERT_FAIL(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block", "@application")));