Skip to content

lsm: give BPF programs a way to query locked_down state - #13318

Open
kernel-patches-daemon-bpf[bot] wants to merge 2 commits into
bpf-next_basefrom
series/1146486=>bpf-next
Open

lsm: give BPF programs a way to query locked_down state#13318
kernel-patches-daemon-bpf[bot] wants to merge 2 commits into
bpf-next_basefrom
series/1146486=>bpf-next

Conversation

@kernel-patches-daemon-bpf

Copy link
Copy Markdown

Pull request for series with
subject: lsm: give BPF programs a way to query locked_down state
version: 1
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: d82ebfc
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-review-bot

Copy link
Copy Markdown
commit 0f2dda23fe388b944271604f30f6ccbb12571aad
Author: Justin Suess <utilityemal77@gmail.com>

selftests/bpf: Test bpf_security_locked_down kfunc

Test the bpf_security_locked_down() kfunc. An LSM program attached to
the locked_down hook denies LOCKDOWN_HIBERNATION, so a syscall program
querying the kfunc observes both verdicts deterministically without
touching real lockdown state: 0 for LOCKDOWN_KEXEC and -EPERM for
LOCKDOWN_HIBERNATION. Out-of-range reasons must return -EINVAL.

Programs in denied calling contexts (a tracing program, and an LSM
program attached to the locked_down hook itself) must be rejected at
load time by the kfunc filter.

> The selftest config guarantees the verdicts are stable: the bpf LSM is
> in CONFIG_LSM and the lockdown LSM is not, so the kernel cannot already
> be locked down.

Does the selftest config actually guarantee this? Looking at
tools/testing/selftests/bpf/config, it sets only CONFIG_BPF_LSM=y,
CONFIG_SECURITY=y and CONFIG_SECURITYFS=y. There is no CONFIG_LSM line at
all in that file.

The only CONFIG_LSM in the bpf selftest configs is
tools/testing/selftests/bpf/config.x86_64, which has
CONFIG_LSM="selinux,bpf,integrity". On other arch configs (aarch64,
s390x, riscv64, ppc64el), CONFIG_LSM falls back to the security/Kconfig
default, which does list "lockdown".

What actually prevents the lockdown LSM from being active is that
CONFIG_SECURITY_LOCKDOWN_LSM defaults to n, so the lockdown LSM registers
no hook. This is a weaker guarantee than the changelog states, and only
holds when test_progs is run against the shipped selftest config.

> diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/lsm_kfuncs.c
> new file mode 100644
> index 000000000000..c836851d8397
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/lsm_kfuncs.c
> @@ -0,0 +1,28 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <test_progs.h>
> +#include "lsm_kfuncs.skel.h"
> +#include "lsm_kfuncs_fail.skel.h"
> +
> +void test_lsm_kfuncs(void)
> +{
> +	LIBBPF_OPTS(bpf_test_run_opts, opts);
> +	struct lsm_kfuncs *skel;
> +
> +	RUN_TESTS(lsm_kfuncs_fail);
> +
> +	skel = lsm_kfuncs__open_and_load();
> +	if (!ASSERT_OK_PTR(skel, "open_and_load"))
> +		return;
> +	if (!ASSERT_OK(lsm_kfuncs__attach(skel), "attach"))
> +		goto out;
> +
> +	if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.query),
> +					      &opts), "test_run"))
> +		goto out;
> +	ASSERT_EQ(skel->data->ret_clear, 0, "not locked down");

Can this assertion produce false failures on a kernel that is already
locked down? If CONFIG_SECURITY_LOCKDOWN_LSM=y with lockdown active
(lockdown=integrity/confidentiality, or Secure Boot with
CONFIG_LOCK_DOWN_KERNEL_FORCE_*), security_locked_down() walks every
registered locked_down hook, not just the test's BPF program.

security/lockdown/lockdown.c:
    LSM_HOOK_INIT(locked_down, lockdown_is_locked_down)

would return -EPERM for LOCKDOWN_KEXEC (an integrity-level reason), so
ret_clear becomes -EPERM and the test reports a hard FAIL rather than a
skip.

> +	ASSERT_EQ(skel->data->ret_denied, -EPERM, "locked down");
> +	ASSERT_EQ(skel->data->ret_invalid_low, -EINVAL, "LOCKDOWN_NONE invalid");
> +	ASSERT_EQ(skel->data->ret_invalid_high, -EINVAL, "CONFIDENTIALITY_MAX invalid");
> +out:
> +	lsm_kfuncs__destroy(skel);
> +}

[ ... ]

> diff --git a/tools/testing/selftests/bpf/progs/lsm_kfuncs.c b/tools/testing/selftests/bpf/progs/lsm_kfuncs.c
> new file mode 100644
> index 000000000000..2637b9bc9025
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_kfuncs.c
> @@ -0,0 +1,34 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include "vmlinux.h"
> +#include <errno.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +
> +char _license[] SEC("license") = "GPL";
> +
> +extern int bpf_security_locked_down(enum lockdown_reason what) __ksym;
> +
> +/* Reason nothing in the test environment genuinely queries or locks. */
> +#define DENY_REASON LOCKDOWN_HIBERNATION
> +#define ALLOW_REASON LOCKDOWN_KEXEC
> +
> +int ret_clear = 1;
> +int ret_denied = 1;
> +int ret_invalid_low = 1;
> +int ret_invalid_high = 1;
> +
> +SEC("lsm/locked_down")
> +int BPF_PROG(lockdown_hook, enum lockdown_reason what)
> +{
> +	return what == DENY_REASON ? -EPERM : 0;
> +}
> +
> +SEC("syscall")
> +int query(void *ctx)
> +{
> +	ret_clear = bpf_security_locked_down(ALLOW_REASON);

Would it make sense to have the test tolerate or skip when the running
kernel is already locked down? The current approach asserts on global host
state the test does not control. Consider checking if ret_clear != 0 and
skipping with a message, so the test does not produce false regression
signals on distro kernels with lockdown active.

> +	ret_denied = bpf_security_locked_down(DENY_REASON);
> +	ret_invalid_low = bpf_security_locked_down(LOCKDOWN_NONE);
> +	ret_invalid_high = bpf_security_locked_down(LOCKDOWN_CONFIDENTIALITY_MAX);
> +	return 0;
> +}

[ ... ]



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: selftests/bpf: Test bpf_security_locked_down kfunc
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31882515336

@kernel-patches-review-bot

Copy link
Copy Markdown
commit b2376c61ce4503d06c16a46048f23bca6aec7bb0
Author: Justin Suess <utilityemal77@gmail.com>

lsm: add bpf_security_locked_down() kfunc

This commit adds a new kfunc bpf_security_locked_down() that wraps
security_locked_down(), creates a new file for LSM kfuncs, validates the
lockdown reason parameter, and restricts access to LSM and SYSCALL
program types while preventing recursion.

> lsm: add bpf_security_locked_down() kfunc
>
> Add a new kfunc bpf_security_locked_down, which calls
> security_locked_down and returns the result.
>
> Create a new file security/lsm_kfuncs.c for LSM framework kfuncs.
>
> Reject reasons outside (LOCKDOWN_NONE, LOCKDOWN_CONFIDENTIALITY_MAX)
> with -EINVAL before dispatching the hook. Limit the kfunc to
> BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL programs, and refuse it
> to programs attached to the locked_down hook itself, which would
> recurse into the dispatch.
>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>

This isn't a bug, but could the changelog lead with the use case that
motivates exposing security_locked_down() to BPF programs, instead of
restating what each hunk does?



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: lsm: add bpf_security_locked_down() kfunc
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31882515336

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: ce7c9f6
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: c93cbdb
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: c93cbdb
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 8eb1892
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: d99bda7
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 34e0eb7
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 5fe7007
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 77877bf
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: adb7719
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 6b0835a
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 6ab6a94
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 3d9393f
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: f79066c
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1146486
version: 1

Add a new kfunc bpf_security_locked_down, which calls
security_locked_down and returns the result.

Create a new file security/lsm_kfuncs.c for LSM framework kfuncs.

Reject reasons outside (LOCKDOWN_NONE, LOCKDOWN_CONFIDENTIALITY_MAX)
with -EINVAL before dispatching the hook. Limit the kfunc to
BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL programs, and refuse it
to programs attached to the locked_down hook itself, which would
recurse into the dispatch.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
Test the bpf_security_locked_down() kfunc. An LSM program attached to
the locked_down hook denies LOCKDOWN_HIBERNATION, so a syscall program
querying the kfunc observes both verdicts deterministically without
touching real lockdown state: 0 for LOCKDOWN_KEXEC and -EPERM for
LOCKDOWN_HIBERNATION. Out-of-range reasons must return -EINVAL.

Programs in denied calling contexts (a tracing program, and an LSM
program attached to the locked_down hook itself) must be rejected at
load time by the kfunc filter.

The selftest config guarantees the verdicts are stable: the bpf LSM is
in CONFIG_LSM and the lockdown LSM is not, so the kernel cannot already
be locked down.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant