From: Eric Curtin Date: Mon, 27 Jul 2026 10:42:37 +0000 (+0100) Subject: core/bpf-restrict-fs: avoid loading the LSM BPF program twice at boot X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c99678eedaad88defe440d6102952926a61e72cf;p=thirdparty%2Fsystemd.git core/bpf-restrict-fs: avoid loading the LSM BPF program twice at boot 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. --- diff --git a/src/core/bpf-restrict-fs.c b/src/core/bpf-restrict-fs.c index a0bd4823b0d..8db05f4db35 100644 --- a/src/core/bpf-restrict-fs.c +++ b/src/core/bpf-restrict-fs.c @@ -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); } diff --git a/src/test/test-bpf-restrict-fs.c b/src/test/test-bpf-restrict-fs.c index 8e41537f21f..810f7af02cf 100644 --- a/src/test/test-bpf-restrict-fs.c +++ b/src/test/test-bpf-restrict-fs.c @@ -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")));