Linux Security Modules development
 help / color / mirror / Atom feed
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 00/13] BPF interface for applying Landlock rulesets
Date: Thu, 30 Jul 2026 22:20:33 -0400	[thread overview]
Message-ID: <20260731022047.189137-1-utilityemal77@gmail.com> (raw)

Howdy,

This series lets BPF programs apply an existing, userspace-created
Landlock ruleset to a program during exec.  The goal is unchanged from
the RFC [1]: BPF does not create, inspect, or mutate Landlock policy,
it only decides whether a ruleset that was already created and
validated through Landlock's existing userspace API should be applied,
based on runtime exec context.

Motivation
---
Deploying Landlock today requires the sandboxed program's
cooperation.  A process can only restrict itself, so applying a
policy system-wide means wrapping every launch path with a helper
that calls landlock_restrict_self(2) before exec, and anything
spawned outside those wrappers runs unconfined.  Supervising exec
from userspace instead (ptrace, seccomp user notifications) is racy
and slow.  Meanwhile, the tools that do enforce system-wide policy
with BPF LSM programs today (container security agents such as
KubeArmor and Tetragon) end up reimplementing path-based access
control in BPF, fraught with horrors of path reconstruction, bind
mounts, rename races, and worse, which is exactly the problem Landlock
already solves in the kernel, with maintained and versioned semantics.

This series composes BPF and Landlock along their natural grain.  The
intended deployment: a supervisor creates one ruleset per policy
class through the existing syscalls, a syscall BPF program parks
them in map kptr fields, and an LSM BPF program picks which (if
any) to apply to an execution based on its runtime context.  The
policy semantics stay Landlock's; BPF contributes only the
programmable decision of when and to whom.  Deciding inside the
exec path closes the race a userspace supervisor cannot: the
policy is in place before the first instruction of the new program
runs.

Addressing RFC feedback
---

The original thread at [1] received useful feedback from the BPF,
LSM and Landlock maintainers. I've hopefully been able to address
all of it here.

The interface is redesigned around the conclusions of the RFC thread.
While it's been over a week since sending [2] detailing some of the
redesign, I figure it's better to show the code.

Four main takeaways came out of the RFC feedback:

1) no kernel or BPF code may call directly into an individual LSM,
every LSM interface must go through the LSM framework.

2) LSMs interact with the kernel subsystems through LSM hooks. kfuncs
are just hooks from the LSM perspective.

3) A new map type is untenable; new maps will not be added for single
consumers. Especially because new map types become ABI.

4) We must implement a generic interface usable by all LSM, without
forgoing the type safety guarantees provided by BPF or making
a ioctl-like multiplexer.

This series does all, as proposed in [2]: the BPF subsystem owns
per-LSM, strongly typed kfuncs in kernel/bpf/bpf_lsm.c, and every
kfunc call passes through a generic LSM hook.  Individual LSMs
implement ordinary LSM hooks and define no BPF interfaces at all.

The three generic hooks carry the policy reference across the LSM
boundary in union lsm_policy_kptr, tagged by the LSM_ID_* value passed
alongside.  The shims dispatch only to the LSM matching that id
(following the setprocattr pattern) and return -EOPNOTSUPP when it is
compiled out or not enabled, so program loading stays independent of
the boot-time LSM configuration and kernel/bpf/ has no build-time
dependency on Landlock.

The LSM hooks
===

  security_policy_kptr_from_fd(lsmid, fd, &policy)
  security_policy_kptr_put(lsmid, &policy)
  security_bprm_enforce_policy_kptr(lsmid, bprm, &policy, flags)

Landlock implements them as ordinary LSM hooks.  The from_fd hook
validates a ruleset fd the same way as the Landlock syscalls; the
enforce hook stages a restriction on the credentials prepared for an
execution, computed by the same helpers as landlock_restrict_self(2);
and a bprm_committing_creds() hook applies it past the exec point of
no return, where nothing can fail.  An execution either starts
confined by the domain or leaves the calling task untouched: a failed
execution releases the staged restriction when its aborted credentials
are freed, so no domain is created for an execution that never
happens.

The Landlock kfuncs
===

  bpf_landlock_get_ruleset_from_fd(fd)   KF_ACQUIRE | KF_RET_NULL
  bpf_landlock_restrict_binprm(bprm, ruleset, flags)
  bpf_landlock_put_ruleset(ruleset)      KF_RELEASE

All sleepable-only.  The acquire kfunc is exclusive to syscall
programs, which run in the context of the task whose fd table gives a
ruleset fd meaning; enforcement is exclusive to LSM programs attached
to bprm_creds_for_exec or bprm_creds_from_file; release works in both.
A kptr destructor is registered so acquired rulesets can be stashed in
ordinary map kptr fields, which is why the put hook must tolerate
non-sleepable contexts (the free is deferred).

The selftests exercise the deployment described above end to end:
a syscall program acquires the supervisor's ruleset and parks it in
a map kptr field; the LSM program takes it from the map and enforces
it on the executions it monitors.  The landlock_restrict_self(2)
flags apply with their usual semantics, except
LANDLOCK_RESTRICT_SELF_TSYNC, which targets the calling threads
rather than the execution and is rejected with -EINVAL.

Changes since the RFC (quite big):
- every kfunc call now passes through a generic LSM hook; the kfuncs
  moved from security/landlock to kernel/bpf/bpf_lsm.c and nothing
  calls directly into Landlock
- the BPF-facing interface stays strongly typed per LSM; the tagged
  union only travels across the LSM boundary. This is superior to
  the void* model proposed in [2].
- BPF_MAP_TYPE_LANDLOCK_RULESET is nixed: the acquire kfunc plus an
  ordinary map kptr field with a registered destructor replaces the
  dedicated map type
- the whole restriction is now staged in the bprm credentials and
  applied at bprm_committing_creds(), where nothing can fail, instead
  of only staging the no_new_privs bit
- domain allocations on the kfunc path no longer charge the mediated
  task's memcg: the enforce hook computes the restriction in a root
  memcg charging scope
- LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS was split into its own series
  [3] and is not carried here; this series only stages the
  Landlock domain. Nothing about it was necessary for this series.
- no new UAPI: no map type, no new flag, no ABI bump (since this
  series touches no Landlock ABI directly and the kfunc/syscall
  side share common code paths).

Subsystem Summary
---

To LSM maintainers: This was the biggest change. There are three new
generic hooks and the union lsm_policy_kptr to abstract the lsm-specific
features declared next to lsm_prop in security.h with one member per
participating LSM.  The hooks are ordinary, LSM-agnostic entry points
usable by any kernel-internal caller, with targeted dispatch by lsm_id
following the setprocattr precedent. It will return -EOPNOTSUPP when the
named LSM is compiled out or disabled.  Nothing in security/ knows about
BPF, and BPF knows nothing about the enabled LSMs.

To Landlock maintainers: there is no ABI or behavior change for
existing users.  The hook implementations reuse the syscall code
paths: ruleset fd validation is shared with the Landlock syscalls,
and the restriction is computed by the same helpers as
landlock_restrict_self(2), factored into prepare/apply steps so
future restrict_self features can hopefully work for both entry
points.  What is new is the entry point: a restriction staged on the
bprm credentials and committed at bprm_committing_creds(), past the
point of no return. The memory accounting issue pointed out by
Mickaël is addressed as well. [4]

To BPF maintainers: BPF owns the three strongly typed kfuncs in
kernel/bpf/bpf_lsm.c, gated per program type (acquire in syscall
programs, enforcement in sleepable LSM programs on the two bprm
hooks), with a registered kptr destructor so rulesets can live in
ordinary map kptr fields.  No new map type, no BPF internals touched.
kernel/bpf/ has no build-time dependency on Landlock or any other LSM
since it just calls the LSM hooks.

Patches 1-3 add the LSM hooks, 4-5 prepare Landlock (fd lookup
exposure, the restrict_self refactoring), 6 implements the hooks,
7-10 add the BPF-owned kfuncs, 11 selftests (end-to-end enforcement,
TSYNC rejection, staged-restriction replacement and discard on a
failed exec, plus six verifier rejection cases), 12-13 documentation.

Tested with a BPF CI run and manual run of the Landlock selftests.

Based on bpf-next/master, but applies cleanly to lsm/next.

[1] https://lore.kernel.org/linux-security-module/20260407200157.3874806-1-utilityemal77@gmail.com/
[2] https://lore.kernel.org/linux-security-module/al_TBtXYUNGLZHC2@zenbox/
[3] https://lore.kernel.org/linux-security-module/20260717220320.1030123-1-utilityemal77@gmail.com/
[4] https://lore.kernel.org/linux-security-module/20260701.ze4eph1eKo7a@digikod.net/

Justin Suess (13):
  lsm: Add LSM hook security_policy_kptr_from_fd
  lsm: Add LSM hook security_policy_kptr_put
  lsm: Add LSM hook security_bprm_enforce_policy_kptr
  landlock: Expose the ruleset fd lookup to the rest of Landlock
  landlock: Factor the credential restriction out of
    landlock_restrict_self()
  landlock: Implement the LSM policy kptr hooks
  bpf: Add the LSM policy kfunc infrastructure
  bpf: Add the bpf_landlock_put_ruleset kfunc and ruleset destructor
  bpf: Add the bpf_landlock_get_ruleset_from_fd kfunc
  bpf: Add the bpf_landlock_restrict_binprm kfunc
  selftests/bpf: Add tests for the Landlock policy kfuncs
  landlock: Document the BPF kfunc interface
  lsm: Document the LSM policy kptr hooks

 Documentation/security/landlock.rst           |  25 ++
 Documentation/security/lsm-development.rst    |  27 ++
 include/linux/lsm_hook_defs.h                 |   5 +
 include/linux/security.h                      |  43 +++
 kernel/bpf/bpf_lsm.c                          | 202 +++++++++++
 security/landlock/Makefile                    |   2 +
 security/landlock/bpf.c                       | 130 +++++++
 security/landlock/bpf.h                       |  21 ++
 security/landlock/cred.c                      | 116 +++++-
 security/landlock/cred.h                      |  51 +++
 security/landlock/limits.h                    |   4 +
 security/landlock/ruleset.c                   |   2 +-
 security/landlock/ruleset.h                   |   3 +
 security/landlock/setup.c                     |   2 +
 security/landlock/syscalls.c                  |  70 +---
 security/security.c                           | 106 ++++++
 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 ++++++
 21 files changed, 1292 insertions(+), 56 deletions(-)
 create mode 100644 security/landlock/bpf.c
 create mode 100644 security/landlock/bpf.h
 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


base-commit: f0e80dee4e32fd11e6ee1b714b75f681c7cafd3e
-- 
2.54.0


             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 Justin Suess [this message]
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 ` [PATCH bpf-next 11/13] selftests/bpf: Add tests for the Landlock policy kfuncs Justin Suess
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-1-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