From: Justin Suess <utilityemal77@gmail.com>
To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net,
viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org
Cc: gnoack@google.com, jack@suse.cz, song@kernel.org,
yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org,
bpf@vger.kernel.org, linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org,
Justin Suess <utilityemal77@gmail.com>
Subject: [PATCH bpf-next 11/13] selftests/bpf: Add tests for the Landlock policy kfuncs
Date: Thu, 30 Jul 2026 22:20:44 -0400 [thread overview]
Message-ID: <20260731022047.189137-12-utilityemal77@gmail.com> (raw)
In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com>
Exercise the LSM policy kfuncs end to end and cover the verifier-time
restrictions enforced by the BPF-side kfunc filter.
The success flow mirrors the intended deployment: a syscall program,
run by the test runner through BPF_PROG_RUN, acquires the ruleset
created by the runner from its fd (resolved in the runner's own fd
table) and parks it in a map kptr slot; a program attached to the
sleepable bprm_creds_for_exec() hook takes it from the map, enforces
it on the monitored execution with bpf_landlock_restrict_binprm()
with flags configured per scenario, and puts it back for the next
execution. The runner creates a real Landlock ruleset handling
LANDLOCK_ACCESS_FS_WRITE_FILE without any rule and checks that:
- a monitored child ends up landlocked (writing to a tmp file fails
while a control execution succeeds);
- the audit log flags are accepted;
- LANDLOCK_RESTRICT_SELF_TSYNC is rejected with -EINVAL and leaves
the execution unrestricted;
- a second bpf_landlock_restrict_binprm() call on the same execution
replaces the previously staged domain instead of failing or
stacking;
- a staged restriction is discarded when the execution fails after
the bprm hook: the child execs an ENOEXEC file with a restriction
staged, and after the failed execve(2) verifies that it is not
landlocked;
- a negative fd resolved through the acquire kfunc returns NULL.
The failure programs check that verification rejects:
- a tracing program calling the kfuncs (LSM and syscall programs
only),
- an LSM program calling the acquire kfunc (syscall programs only,
where the ruleset fd is meaningful),
- a syscall program calling the enforcement kfunc (sleepable bprm
LSM hooks only),
- an LSM program on a hook other than the sleepable bprm hooks,
- a non-sleepable LSM program on an allowed hook,
- a program leaking the acquired ruleset reference.
The test needs CONFIG_SECURITY_LANDLOCK and the landlock LSM enabled
in the test kernel; the runner skips if the Landlock syscalls are
unavailable.
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
tools/testing/selftests/bpf/config | 1 +
tools/testing/selftests/bpf/config.x86_64 | 2 +-
.../bpf/prog_tests/lsm_policy_kfuncs.c | 329 ++++++++++++++++++
.../bpf/progs/lsm_policy_kfuncs_failure.c | 100 ++++++
.../bpf/progs/lsm_policy_kfuncs_success.c | 107 ++++++
5 files changed, 538 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index ea7044f30adc..2fa734497461 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -120,6 +120,7 @@ CONFIG_SAMPLES=y
CONFIG_SAMPLE_LIVEPATCH=m
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
+CONFIG_SECURITY_LANDLOCK=y
CONFIG_SYN_COOKIES=y
CONFIG_TEST_BPF=m
CONFIG_UDMABUF=y
diff --git a/tools/testing/selftests/bpf/config.x86_64 b/tools/testing/selftests/bpf/config.x86_64
index 523e0d29bbd4..2c4d857f69f5 100644
--- a/tools/testing/selftests/bpf/config.x86_64
+++ b/tools/testing/selftests/bpf/config.x86_64
@@ -125,7 +125,7 @@ CONFIG_LEGACY_VSYSCALL_NONE=y
CONFIG_LOG_BUF_SHIFT=21
CONFIG_LOG_CPU_MAX_BUF_SHIFT=0
CONFIG_LOGO=y
-CONFIG_LSM="selinux,bpf,integrity"
+CONFIG_LSM="landlock,selinux,bpf,integrity"
CONFIG_MAC_PARTITION=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_MCORE2=y
diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c
new file mode 100644
index 000000000000..c91929f98e88
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright © 2026 Justin Suess <utilityemal77@gmail.com> */
+
+#include <test_progs.h>
+#include <errno.h>
+#include <linux/landlock.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "lsm_policy_kfuncs_success.skel.h"
+#include "lsm_policy_kfuncs_failure.skel.h"
+
+/* Fallbacks for old system headers. */
+#ifndef LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON
+#define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1)
+#endif
+#ifndef LANDLOCK_RESTRICT_SELF_TSYNC
+#define LANDLOCK_RESTRICT_SELF_TSYNC (1U << 3)
+#endif
+
+static int create_ruleset(void)
+{
+ const struct landlock_ruleset_attr attr = {
+ .handled_access_fs = LANDLOCK_ACCESS_FS_WRITE_FILE,
+ };
+
+ return syscall(__NR_landlock_create_ruleset, &attr, sizeof(attr), 0);
+}
+
+static void reset_prog_state(struct lsm_policy_kfuncs_success *skel)
+{
+ skel->bss->called = false;
+ skel->bss->no_ruleset = false;
+ skel->bss->restrict_err = -1;
+ skel->bss->restrict2_err = -1;
+ skel->bss->kfunc_flags = 0;
+ skel->bss->double_call = false;
+}
+
+/*
+ * Runs the syscall program that acquires the ruleset from
+ * @ruleset_fd, in the runner's fd table, and parks it in the map kptr
+ * slot for the LSM program.
+ */
+static int load_ruleset_into_map(struct lsm_policy_kfuncs_success *skel)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, opts);
+ int err;
+
+ err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.load_ruleset),
+ &opts);
+ if (!ASSERT_OK(err, "load_ruleset_run"))
+ return -1;
+ if (!ASSERT_OK(opts.retval, "load_ruleset_retval"))
+ return -1;
+ ASSERT_TRUE(skel->bss->got_null_for_bad_fd, "bad_fd_null");
+ return 0;
+}
+
+/*
+ * Forks a child that execs "sh -c '<shell_cmd>'". The monitored pid
+ * is only known, and can only be published to the BPF program, once
+ * the child exists: the child waits on a pipe until the parent has
+ * updated it. Returns the child's exit status, or -1 on error.
+ */
+static int run_exec_child(struct lsm_policy_kfuncs_success *skel,
+ bool monitored, const char *shell_cmd)
+{
+ int pipe_fds[2], status;
+ char buf = 0;
+ pid_t pid;
+
+ if (!ASSERT_OK(pipe(pipe_fds), "pipe"))
+ return -1;
+
+ pid = fork();
+ if (!ASSERT_GE(pid, 0, "fork")) {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return -1;
+ }
+ if (pid == 0) {
+ char *argv[] = { "sh", "-c", (char *)shell_cmd, NULL };
+
+ close(pipe_fds[1]);
+ read(pipe_fds[0], &buf, 1);
+ close(pipe_fds[0]);
+ execv("/bin/sh", argv);
+ exit(127);
+ }
+ close(pipe_fds[0]);
+ skel->bss->monitored_pid = monitored ? pid : 0;
+ write(pipe_fds[1], &buf, 1);
+ close(pipe_fds[1]);
+
+ if (!ASSERT_EQ(waitpid(pid, &status, 0), pid, "waitpid"))
+ return -1;
+ if (!ASSERT_TRUE(WIFEXITED(status), "child_exited"))
+ return -1;
+ return WEXITSTATUS(status);
+}
+
+/*
+ * Exit codes: 4 = unexpected write outcome, 0 = everything as
+ * expected.
+ */
+static void format_child_cmd(char *cmd, size_t len, bool expect_write_ok,
+ const char *tmp_path)
+{
+ if (expect_write_ok)
+ snprintf(cmd, len, "echo x > %s || exit 4; exit 0", tmp_path);
+ else
+ snprintf(cmd, len,
+ "if echo x > %s 2>/dev/null; then exit 4; fi; exit 0",
+ tmp_path);
+}
+
+static void test_restrict_binprm(void)
+{
+ struct lsm_policy_kfuncs_success *skel = NULL;
+ char tmp_path[] = "/tmp/lsm_policy_kfuncs_XXXXXX";
+ char cmd[256];
+ int ruleset_fd, tmp_fd, ret;
+
+ tmp_fd = mkstemp(tmp_path);
+ if (!ASSERT_GE(tmp_fd, 0, "mkstemp"))
+ return;
+ close(tmp_fd);
+
+ ruleset_fd = create_ruleset();
+ if (ruleset_fd < 0) {
+ if (errno == EOPNOTSUPP || errno == ENOSYS)
+ test__skip();
+ else
+ ASSERT_GE(ruleset_fd, 0, "landlock_create_ruleset");
+ goto out_unlink;
+ }
+
+ skel = lsm_policy_kfuncs_success__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ goto out;
+ skel->bss->ruleset_fd = ruleset_fd;
+
+ if (!ASSERT_OK(lsm_policy_kfuncs_success__attach(skel), "skel_attach"))
+ goto out;
+
+ if (load_ruleset_into_map(skel))
+ goto out;
+
+ /* Control: an unmonitored execution may write to the tmp file. */
+ reset_prog_state(skel);
+ format_child_cmd(cmd, sizeof(cmd), true, tmp_path);
+ ret = run_exec_child(skel, false, cmd);
+ if (!ASSERT_EQ(ret, 0, "control_child_exit"))
+ goto out;
+ ASSERT_FALSE(skel->bss->called, "control_not_monitored");
+
+ /*
+ * A monitored execution starts landlocked: the ruleset handles
+ * LANDLOCK_ACCESS_FS_WRITE_FILE without any rule, so the write
+ * must fail.
+ */
+ reset_prog_state(skel);
+ format_child_cmd(cmd, sizeof(cmd), false, tmp_path);
+ ret = run_exec_child(skel, true, cmd);
+ if (!ASSERT_EQ(ret, 0, "restricted_child_exit"))
+ goto out;
+ ASSERT_TRUE(skel->bss->called, "lsm_prog_called");
+ ASSERT_FALSE(skel->bss->no_ruleset, "ruleset_in_map");
+ ASSERT_EQ(skel->bss->restrict_err, 0, "restrict_binprm");
+
+ /* The audit log flags of landlock_restrict_self(2) apply too. */
+ reset_prog_state(skel);
+ skel->bss->kfunc_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
+ format_child_cmd(cmd, sizeof(cmd), false, tmp_path);
+ ret = run_exec_child(skel, true, cmd);
+ if (!ASSERT_EQ(ret, 0, "log_flags_child_exit"))
+ goto out;
+ ASSERT_EQ(skel->bss->restrict_err, 0, "log_flags_restrict_binprm");
+
+ /*
+ * LANDLOCK_RESTRICT_SELF_TSYNC targets the calling threads, not
+ * an execution: the kfunc must reject it and the execution must
+ * stay unrestricted.
+ */
+ reset_prog_state(skel);
+ skel->bss->kfunc_flags = LANDLOCK_RESTRICT_SELF_TSYNC;
+ format_child_cmd(cmd, sizeof(cmd), true, tmp_path);
+ ret = run_exec_child(skel, true, cmd);
+ if (!ASSERT_EQ(ret, 0, "tsync_child_exit"))
+ goto out;
+ ASSERT_TRUE(skel->bss->called, "tsync_prog_called");
+ ASSERT_EQ(skel->bss->restrict_err, -EINVAL, "tsync_rejected");
+
+ /*
+ * A second call on the same execution replaces the staged
+ * domain (and releases the first one): the result is a single
+ * restriction, not an error.
+ */
+ reset_prog_state(skel);
+ skel->bss->double_call = true;
+ format_child_cmd(cmd, sizeof(cmd), false, tmp_path);
+ ret = run_exec_child(skel, true, cmd);
+ if (!ASSERT_EQ(ret, 0, "double_child_exit"))
+ goto out;
+ ASSERT_EQ(skel->bss->restrict_err, 0, "double_restrict_first");
+ ASSERT_EQ(skel->bss->restrict2_err, 0, "double_restrict_second");
+out:
+ lsm_policy_kfuncs_success__destroy(skel);
+ close(ruleset_fd);
+out_unlink:
+ unlink(tmp_path);
+}
+
+/*
+ * Checks that a staged restriction is discarded, and the staged
+ * domain released, when the execution fails after the bprm hook: the
+ * calling task must not end up landlocked.
+ */
+static void test_restrict_binprm_discard(void)
+{
+ struct lsm_policy_kfuncs_success *skel = NULL;
+ char tmp_path[] = "/tmp/lsm_policy_kfuncs_XXXXXX";
+ char garbage_path[] = "/tmp/lsm_policy_garbage_XXXXXX";
+ int ruleset_fd = -1, tmp_fd, garbage_fd, pipe_fds[2], status;
+ char buf = 0;
+ pid_t pid;
+
+ tmp_fd = mkstemp(tmp_path);
+ if (!ASSERT_GE(tmp_fd, 0, "mkstemp"))
+ return;
+ close(tmp_fd);
+
+ /*
+ * An executable file that no binfmt handler accepts: the exec
+ * fails with ENOEXEC after bprm_creds_for_exec() has run.
+ */
+ garbage_fd = mkstemp(garbage_path);
+ if (!ASSERT_GE(garbage_fd, 0, "mkstemp_garbage"))
+ goto out_unlink;
+ if (!ASSERT_EQ(write(garbage_fd, "junk\n", 5), 5, "write_garbage")) {
+ close(garbage_fd);
+ goto out_unlink;
+ }
+ if (!ASSERT_OK(fchmod(garbage_fd, 0700), "chmod_garbage")) {
+ close(garbage_fd);
+ goto out_unlink;
+ }
+ close(garbage_fd);
+
+ ruleset_fd = create_ruleset();
+ if (ruleset_fd < 0) {
+ if (errno == EOPNOTSUPP || errno == ENOSYS)
+ test__skip();
+ else
+ ASSERT_GE(ruleset_fd, 0, "landlock_create_ruleset");
+ goto out_unlink;
+ }
+
+ skel = lsm_policy_kfuncs_success__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ goto out;
+ skel->bss->ruleset_fd = ruleset_fd;
+ reset_prog_state(skel);
+
+ if (!ASSERT_OK(lsm_policy_kfuncs_success__attach(skel), "skel_attach"))
+ goto out;
+
+ if (load_ruleset_into_map(skel))
+ goto out;
+
+ if (!ASSERT_OK(pipe(pipe_fds), "pipe"))
+ goto out;
+
+ pid = fork();
+ if (!ASSERT_GE(pid, 0, "fork"))
+ goto out;
+ if (pid == 0) {
+ char *argv[] = { "garbage", NULL };
+ int fd;
+
+ close(pipe_fds[1]);
+ read(pipe_fds[0], &buf, 1);
+ close(pipe_fds[0]);
+ execv(garbage_path, argv);
+ /*
+ * The failed execution must leave no trace: no
+ * Landlock domain, i.e. writing must still work
+ * (exit 6).
+ */
+ fd = open(tmp_path, O_WRONLY | O_TRUNC);
+ if (fd < 0)
+ exit(6);
+ close(fd);
+ exit(0);
+ }
+ close(pipe_fds[0]);
+ skel->bss->monitored_pid = pid;
+ write(pipe_fds[1], &buf, 1);
+ close(pipe_fds[1]);
+
+ if (!ASSERT_EQ(waitpid(pid, &status, 0), pid, "waitpid"))
+ goto out;
+ if (!ASSERT_TRUE(WIFEXITED(status), "child_exited"))
+ goto out;
+ ASSERT_EQ(WEXITSTATUS(status), 0, "discard_child_exit");
+ ASSERT_TRUE(skel->bss->called, "lsm_prog_called");
+ ASSERT_EQ(skel->bss->restrict_err, 0, "restrict_binprm");
+out:
+ lsm_policy_kfuncs_success__destroy(skel);
+ if (ruleset_fd >= 0)
+ close(ruleset_fd);
+out_unlink:
+ unlink(garbage_path);
+ unlink(tmp_path);
+}
+
+void test_lsm_policy_kfuncs(void)
+{
+ if (test__start_subtest("restrict_binprm"))
+ test_restrict_binprm();
+ if (test__start_subtest("restrict_binprm_discard"))
+ test_restrict_binprm_discard();
+ RUN_TESTS(lsm_policy_kfuncs_failure);
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c
new file mode 100644
index 000000000000..0335db547040
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright © 2026 Justin Suess <utilityemal77@gmail.com> */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct bpf_landlock_ruleset;
+
+extern struct bpf_landlock_ruleset *
+bpf_landlock_get_ruleset_from_fd(int fd) __ksym;
+extern void
+bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *ruleset) __ksym;
+extern int bpf_landlock_restrict_binprm(struct linux_binprm *bprm,
+ struct bpf_landlock_ruleset *ruleset,
+ u32 flags) __ksym;
+
+/*
+ * The LSM policy kfuncs are limited to LSM and syscall programs by
+ * the BPF-side kfunc filter: a tracing program calling one must fail
+ * verification.
+ */
+SEC("tp_btf/task_newtask")
+__failure __msg("calling kernel function bpf_landlock_get_ruleset_from_fd is not allowed")
+int BPF_PROG(tracing_prog, struct task_struct *task, u64 clone_flags)
+{
+ struct bpf_landlock_ruleset *ruleset;
+
+ ruleset = bpf_landlock_get_ruleset_from_fd(-1);
+ if (ruleset)
+ bpf_landlock_put_ruleset(ruleset);
+ return 0;
+}
+
+/*
+ * A ruleset fd is only meaningful in the fd table of the task that
+ * set the ruleset up: the acquire kfunc is exclusive to syscall
+ * programs and must be rejected in an LSM program, even on an
+ * allowed hook.
+ */
+SEC("lsm.s/bprm_creds_for_exec")
+__failure __msg("calling kernel function bpf_landlock_get_ruleset_from_fd is not allowed")
+int BPF_PROG(lsm_get, struct linux_binprm *bprm)
+{
+ struct bpf_landlock_ruleset *ruleset;
+
+ ruleset = bpf_landlock_get_ruleset_from_fd(-1);
+ if (ruleset)
+ bpf_landlock_put_ruleset(ruleset);
+ return 0;
+}
+
+/*
+ * Enforcement needs an execution to restrict: the enforcement kfunc
+ * is exclusive to the sleepable bprm LSM hooks and must be rejected
+ * in a syscall program.
+ */
+SEC("syscall")
+__failure __msg("calling kernel function bpf_landlock_restrict_binprm is not allowed")
+int syscall_restrict(void *ctx)
+{
+ return bpf_landlock_restrict_binprm(NULL, NULL, 0);
+}
+
+/*
+ * Any LSM attach point other than the sleepable bprm hooks must be
+ * rejected.
+ */
+SEC("lsm.s/file_open")
+__failure __msg("calling kernel function bpf_landlock_put_ruleset is not allowed")
+int BPF_PROG(wrong_hook, struct file *file)
+{
+ bpf_landlock_put_ruleset(NULL);
+ return 0;
+}
+
+/*
+ * The kfuncs may sleep: a non-sleepable program on an allowed hook
+ * must be rejected.
+ */
+SEC("lsm/bprm_creds_for_exec")
+__failure
+__msg("program must be sleepable to call sleepable kfunc bpf_landlock_put_ruleset")
+int BPF_PROG(nonsleepable_prog, struct linux_binprm *bprm)
+{
+ bpf_landlock_put_ruleset(NULL);
+ return 0;
+}
+
+/* An acquired ruleset reference must be released before returning. */
+SEC("syscall")
+__failure __msg("Unreleased reference")
+int leak_ruleset(void *ctx)
+{
+ bpf_landlock_get_ruleset_from_fd(-1);
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c
new file mode 100644
index 000000000000..2107206ae144
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright © 2026 Justin Suess <utilityemal77@gmail.com> */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+struct bpf_landlock_ruleset;
+
+extern struct bpf_landlock_ruleset *
+bpf_landlock_get_ruleset_from_fd(int fd) __ksym;
+extern void
+bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *ruleset) __ksym;
+extern int bpf_landlock_restrict_binprm(struct linux_binprm *bprm,
+ struct bpf_landlock_ruleset *ruleset,
+ u32 flags) __ksym;
+
+struct ruleset_slot {
+ struct bpf_landlock_ruleset __kptr *ruleset;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, struct ruleset_slot);
+} ruleset_map SEC(".maps");
+
+int monitored_pid;
+int ruleset_fd;
+u32 kfunc_flags;
+bool double_call;
+bool got_null_for_bad_fd;
+bool no_ruleset;
+int restrict_err;
+int restrict2_err;
+bool called;
+
+/*
+ * Runs in the test runner's context through BPF_PROG_RUN:
+ * @ruleset_fd is resolved in the runner's fd table and the acquired
+ * ruleset is handed to the LSM program through the map kptr slot.
+ */
+SEC("syscall")
+int load_ruleset(void *ctx)
+{
+ struct bpf_landlock_ruleset *ruleset, *old;
+ struct ruleset_slot *slot;
+ int key = 0;
+
+ slot = bpf_map_lookup_elem(&ruleset_map, &key);
+ if (!slot)
+ return 1;
+
+ /* A fd that is not a Landlock ruleset must resolve to NULL. */
+ ruleset = bpf_landlock_get_ruleset_from_fd(-1);
+ if (!ruleset)
+ got_null_for_bad_fd = true;
+ else
+ bpf_landlock_put_ruleset(ruleset);
+
+ ruleset = bpf_landlock_get_ruleset_from_fd(ruleset_fd);
+ if (!ruleset)
+ return 2;
+
+ old = bpf_kptr_xchg(&slot->ruleset, ruleset);
+ if (old)
+ bpf_landlock_put_ruleset(old);
+ return 0;
+}
+
+SEC("lsm.s/bprm_creds_for_exec")
+int BPF_PROG(restrict_exec, struct linux_binprm *bprm)
+{
+ struct bpf_landlock_ruleset *ruleset, *old;
+ struct ruleset_slot *slot;
+ int key = 0;
+
+ if (monitored_pid != (bpf_get_current_pid_tgid() >> 32))
+ return 0;
+
+ called = true;
+
+ slot = bpf_map_lookup_elem(&ruleset_map, &key);
+ if (!slot)
+ return 0;
+
+ ruleset = bpf_kptr_xchg(&slot->ruleset, NULL);
+ if (!ruleset) {
+ no_ruleset = true;
+ return 0;
+ }
+
+ restrict_err = bpf_landlock_restrict_binprm(bprm, ruleset, kfunc_flags);
+ if (double_call)
+ /* Replaces the domain staged by the first call. */
+ restrict2_err = bpf_landlock_restrict_binprm(bprm, ruleset,
+ kfunc_flags);
+
+ /* Keep the ruleset for the next monitored execution. */
+ old = bpf_kptr_xchg(&slot->ruleset, ruleset);
+ if (old)
+ bpf_landlock_put_ruleset(old);
+ return 0;
+}
--
2.54.0
next prev parent reply other threads:[~2026-07-31 2:21 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-31 2:20 [PATCH bpf-next 00/13] BPF interface for applying Landlock rulesets Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 01/13] lsm: Add LSM hook security_policy_kptr_from_fd Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 02/13] lsm: Add LSM hook security_policy_kptr_put Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 03/13] lsm: Add LSM hook security_bprm_enforce_policy_kptr Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 04/13] landlock: Expose the ruleset fd lookup to the rest of Landlock Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 05/13] landlock: Factor the credential restriction out of landlock_restrict_self() Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 06/13] landlock: Implement the LSM policy kptr hooks Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 07/13] bpf: Add the LSM policy kfunc infrastructure Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 08/13] bpf: Add the bpf_landlock_put_ruleset kfunc and ruleset destructor Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 09/13] bpf: Add the bpf_landlock_get_ruleset_from_fd kfunc Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 10/13] bpf: Add the bpf_landlock_restrict_binprm kfunc Justin Suess
2026-07-31 2:20 ` Justin Suess [this message]
2026-07-31 2:20 ` [PATCH bpf-next 12/13] landlock: Document the BPF kfunc interface Justin Suess
2026-07-31 2:20 ` [PATCH bpf-next 13/13] lsm: Document the LSM policy kptr hooks Justin Suess
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260731022047.189137-12-utilityemal77@gmail.com \
--to=utilityemal77@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=daniel@iogearbox.net \
--cc=gnoack@google.com \
--cc=jack@suse.cz \
--cc=kees@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=martin.lau@linux.dev \
--cc=mic@digikod.net \
--cc=paul@paul-moore.com \
--cc=song@kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox