]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: Inspect the signature verdict exposed to BPF LSM
authorDaniel Borkmann <daniel@iogearbox.net>
Fri, 5 Jun 2026 21:35:18 +0000 (23:35 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 6 Jun 2026 03:33:48 +0000 (20:33 -0700)
Add a minimal BPF LSM program on lsm/bpf_prog_load that, for loads on
the monitored thread, reads back prog->aux->sig.{verdict,keyring_type,
keyring_serial}, and a signed_loader subtest that drives the same
gen_loader loader through the hook twice: i) /unsigned/ where the LSM
must observe UNSIGNED, no keyring and serial 0; ii) /signed/ where the
very same insns signed against the session keyring must be observed as
VERIFIED with a user keyring, and the recorded keyring_serial must be
equal to the resolved session keyring serial. Loading (not running) the
loader is sufficient since the verdict is attached at load time.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t signed_loader
  [    1.970530] clocksource: Switched to clocksource tsc
  #405/1   signed_loader/metadata_check_shape:OK
  #405/2   signed_loader/metadata_match:OK
  #405/3   signed_loader/metadata_sha_mismatch:OK
  #405/4   signed_loader/metadata_not_exclusive:OK
  #405/5   signed_loader/metadata_hash_not_computed:OK
  #405/6   signed_loader/signature_enforced:OK
  #405/7   signed_loader/signature_too_large:OK
  #405/8   signed_loader/signature_bad_keyring:OK
  #405/9   signed_loader/metadata_ctx_max_entries_ignored:OK
  #405/10  signed_loader/metadata_ctx_initial_value_ignored:OK
  #405/11  signed_loader/signature_authenticates_insns:OK
  #405/12  signed_loader/hash_requires_frozen:OK
  #405/13  signed_loader/no_update_after_freeze:OK
  #405/14  signed_loader/freeze_writable_mmap:OK
  #405/15  signed_loader/no_writable_mmap_frozen:OK
  #405/16  signed_loader/map_hash_matches_libbpf:OK
  #405/17  signed_loader/map_hash_multi_element:OK
  #405/18  signed_loader/map_hash_bad_size:OK
  #405/19  signed_loader/map_hash_unsupported_type:OK
  #405/20  signed_loader/lsm_signature_verdict:OK
  #405     signed_loader:OK
  Summary: 1/20 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260605213518.544262-2-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/signed_loader.c
tools/testing/selftests/bpf/progs/test_signed_loader_lsm.c [new file with mode: 0644]

index dcfdd2d96b05c5782163e98c5a440b11d61f6ea0..5fc417e31fc615fae903791d5d7cbc3ebcde8aa2 100644 (file)
 #include "test_signed_loader.skel.h"
 #include "test_signed_loader_map.skel.h"
 #include "test_signed_loader_data.skel.h"
+#include "test_signed_loader_lsm.skel.h"
 
 #define SIG_MATCH_INSNS 33 /* excl (5) + 4 * sha-dword (7) */
 
+enum {
+       BPF_SIG_UNSIGNED = 0,
+       BPF_SIG_VERIFIED,
+};
+
+enum {
+       BPF_SIG_KEYRING_NONE = 0,
+       BPF_SIG_KEYRING_BUILTIN,
+       BPF_SIG_KEYRING_SECONDARY,
+       BPF_SIG_KEYRING_PLATFORM,
+       BPF_SIG_KEYRING_USER,
+};
+
 static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
                       const void *sig, __u32 sig_sz, __s32 keyring_id)
 {
@@ -970,6 +984,112 @@ static void map_hash_unsupported_type(void)
        close(fd);
 }
 
+static int setup_meta_map(const struct gen_loader_fixture *f)
+{
+       LIBBPF_OPTS(bpf_map_create_opts, mopts,
+                   .excl_prog_hash = f->excl,
+                   .excl_prog_hash_size = sizeof(f->excl));
+       __u32 key = 0;
+       int fd;
+
+       fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
+                           f->data_sz, 1, &mopts);
+       if (fd < 0)
+               return -errno;
+       if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) {
+               close(fd);
+               return -errno;
+       }
+       return fd;
+}
+
+static void lsm_signature_verdict(void)
+{
+       char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL;
+       struct test_signed_loader_lsm *lsm = NULL;
+       int map_fd = -1, prog_fd = -1;
+       bool have_fixture = false;
+       struct gen_loader_fixture f;
+       __u32 sig_sz = 8192;
+       __s32 ses_serial;
+       __u8 sig[8192];
+
+       lsm = test_signed_loader_lsm__open_and_load();
+       if (!ASSERT_OK_PTR(lsm, "lsm_skel_load"))
+               return;
+       lsm->bss->monitored_tid = sys_gettid();
+       if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach"))
+               goto out;
+
+       have_fixture = true;
+       if (gen_loader_fixture_init(&f) != 0)
+               goto out;
+
+       map_fd = setup_meta_map(&f);
+       if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned"))
+               goto out;
+       lsm->bss->seen = 0;
+       prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0);
+       close(map_fd);
+       map_fd = -1;
+       if (!ASSERT_OK_FD(prog_fd, "unsigned loader load"))
+               goto out;
+       close(prog_fd);
+       prog_fd = -1;
+       if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set"))
+               goto out;
+       ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load");
+       ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict");
+       ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type");
+       ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial");
+
+       syscall(__NR_request_key, "keyring", "_uid.0", NULL,
+               KEY_SPEC_SESSION_KEYRING);
+       dir = mkdtemp(dir_tmpl);
+       if (!ASSERT_OK_PTR(dir, "mkdtemp"))
+               goto out;
+       if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+               rmdir(dir);
+               dir = NULL;
+               goto out;
+       }
+       if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig,
+                               &sig_sz), "sign-file"))
+               goto out;
+
+       map_fd = setup_meta_map(&f);
+       if (!ASSERT_OK_FD(map_fd, "meta_map_signed"))
+               goto out;
+       lsm->bss->seen = 0;
+       prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
+                             sig_sz, KEY_SPEC_SESSION_KEYRING);
+       close(map_fd);
+       map_fd = -1;
+       if (!ASSERT_OK_FD(prog_fd, "signed loader load"))
+               goto out;
+       close(prog_fd);
+       prog_fd = -1;
+
+       ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID,
+                            KEY_SPEC_SESSION_KEYRING, 0);
+       ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load");
+       ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, "signed verdict");
+       ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type");
+       ASSERT_GT(ses_serial, 0, "session keyring serial resolved");
+       ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial,
+                 "signed: validated against session keyring");
+out:
+       if (map_fd >= 0)
+               close(map_fd);
+       if (prog_fd >= 0)
+               close(prog_fd);
+       if (have_fixture)
+               gen_loader_fixture_fini(&f);
+       if (dir)
+               run_setup("cleanup", dir);
+       test_signed_loader_lsm__destroy(lsm);
+}
+
 void test_signed_loader(void)
 {
        if (test__start_subtest("metadata_check_shape"))
@@ -1010,4 +1130,6 @@ void test_signed_loader(void)
                map_hash_bad_size();
        if (test__start_subtest("map_hash_unsupported_type"))
                map_hash_unsupported_type();
+       if (test__start_subtest("lsm_signature_verdict"))
+               lsm_signature_verdict();
 }
diff --git a/tools/testing/selftests/bpf/progs/test_signed_loader_lsm.c b/tools/testing/selftests/bpf/progs/test_signed_loader_lsm.c
new file mode 100644 (file)
index 0000000..575a9b7
--- /dev/null
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u32 monitored_tid;
+
+int sig_keyring_serial;
+int sig_keyring_type;
+int sig_verdict;
+int seen;
+
+SEC("lsm/bpf_prog_load")
+int BPF_PROG(inspect_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
+            struct bpf_token *token, bool kernel)
+{
+       __u32 tid = bpf_get_current_pid_tgid() & 0xffffffff;
+
+       if (!monitored_tid || tid != monitored_tid)
+               return 0;
+
+       seen++;
+       sig_keyring_serial = prog->aux->sig.keyring_serial;
+       sig_keyring_type = prog->aux->sig.keyring_type;
+       sig_verdict = prog->aux->sig.verdict;
+       return 0;
+}