Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [PATCH v2] landlock: Fix kernel-doc warning for pointer-to-array parameters
From: Günther Noack @ 2026-03-10 21:13 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, linux-security-module, Jonathan Corbet
In-Reply-To: <20260310172004.1839864-1-mic@digikod.net>

On Tue, Mar 10, 2026 at 06:20:03PM +0100, Mickaël Salaün wrote:
> The insert_rule() and create_rule() functions take a
> pointer-to-flexible-array parameter declared as:
> 
>   const struct landlock_layer (*const layers)[]
> 
> The kernel-doc parser cannot handle a qualifier between * and the
> parameter name in this syntax, producing spurious "Invalid param" and
> "not described" warnings.
> 
> Remove the const qualifier of the "layers" argument to avoid this
> parsing issue.
> 
> Cc: Günther Noack <gnoack@google.com>
> Cc: Jonathan Corbet <corbet@lwn.net>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> ---
> 
> Changes since v1:
> https://lore.kernel.org/r/20260304193134.250495-1-mic@digikod.net
> - Remove const instead of using a typedef (suggested by Günther).
> ---
>  security/landlock/ruleset.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 3234a5bc11ff..181df7736bb9 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -107,7 +107,7 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
>  
>  static struct landlock_rule *
>  create_rule(const struct landlock_id id,
> -	    const struct landlock_layer (*const layers)[], const u32 num_layers,
> +	    const struct landlock_layer (*layers)[], const u32 num_layers,
>  	    const struct landlock_layer *const new_layer)
>  {
>  	struct landlock_rule *new_rule;
> @@ -206,7 +206,7 @@ static void build_check_ruleset(void)
>   */
>  static int insert_rule(struct landlock_ruleset *const ruleset,
>  		       const struct landlock_id id,
> -		       const struct landlock_layer (*const layers)[],
> +		       const struct landlock_layer (*layers)[],
>  		       const size_t num_layers)
>  {
>  	struct rb_node **walker_node;
> -- 
> 2.53.0
> 

Reviewed-by: Günther Noack <gnoack3000@gmail.com>

^ permalink raw reply

* Re: [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data
From: Eric Biggers @ 2026-03-10 21:36 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity
In-Reply-To: <20260113-module-hashes-v4-6-0b932db9b56b@weissschuh.net>

On Tue, Jan 13, 2026 at 01:28:50PM +0100, Thomas Weißschuh wrote:
> The upcoming module hashes functionality will build the modules in
> between the generation of the BTF data and the final link of vmlinux.
> Having a dependency from the modules on vmlinux would make this
> impossible as it would mean having a cyclic dependency.
> Break this cyclic dependency by introducing a new target.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  scripts/Makefile.modfinal | 4 ++--
>  scripts/link-vmlinux.sh   | 6 ++++++
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index 149e12ff5700..adfef1e002a9 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
> @@ -56,8 +56,8 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
>  	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
>  
>  # Re-generate module BTFs if either module's .ko or vmlinux changed
> -%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
> -	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
> +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/.tmp_vmlinux_btf.stamp) FORCE
> +	+$(call if_changed_except,ld_ko_o,$(objtree)/.tmp_vmlinux_btf.stamp)
>  ifdef CONFIG_DEBUG_INFO_BTF_MODULES
>  	+$(if $(newer-prereqs),$(call cmd,btf_ko))
>  endif
> diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> index 4ab44c73da4d..8c98f8645a5c 100755
> --- a/scripts/link-vmlinux.sh
> +++ b/scripts/link-vmlinux.sh
> @@ -111,6 +111,7 @@ vmlinux_link()
>  gen_btf()
>  {
>  	local btf_data=${1}.btf.o
> +	local btf_stamp=.tmp_vmlinux_btf.stamp
>  
>  	info BTF "${btf_data}"
>  	LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
> @@ -131,6 +132,11 @@ gen_btf()
>  	fi
>  	printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
>  
> +	info STAMP $btf_stamp
> +	if ! cmp --silent $btf_data $btf_stamp; then
> +		cp $btf_data $btf_stamp
> +	fi

A "stamp file" is traditionally an empty file that is written when some
build step has completed.  The above code is instead copying the entire
.tmp_vmlinux1.btf.o file (megabytes in size) to .tmp_vmlinux_btf.stamp.

So, it's not clear to me why the stamp file is needed at all, versus
depending directly on .tmp_vmlinux1.btf.o.

I guess 'make' doesn't know about the dependencies of
.tmp_vmlinux1.btf.o.  But the same is true of the stamp file, right?  So
either way, how would 'make' know to finish rebuilding the file before
starting to execute the "Re-generate module BTFs" rule?

Also, passing the long option '--silent' to 'cmp' creates a dependency
on the GNU implementation of 'cmp', which isn't documented as a kernel
build dependency.  Probably better to use the short option '-s'.

Also, the stamp file isn't being deleted by 'make clean'.  It looks like
it would need to be added to cleanup() in link-vmlinux.sh.

- Eric

^ permalink raw reply

* Re: [PATCH 0/3] Firmware LSM hook
From: Paul Moore @ 2026-03-10 21:40 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: James Morris, Serge E. Hallyn, Jason Gunthorpe, Saeed Mahameed,
	Itay Avraham, Dave Jiang, Jonathan Cameron, linux-security-module,
	linux-kernel, linux-rdma, Chiara Meiohas, Maher Sanalla,
	Edward Srouji
In-Reply-To: <20260310193000.GM12611@unreal>

On Tue, Mar 10, 2026 at 3:30 PM Leon Romanovsky <leon@kernel.org> wrote:
> On Tue, Mar 10, 2026 at 02:24:40PM -0400, Paul Moore wrote:
> > On Tue, Mar 10, 2026 at 5:07 AM Leon Romanovsky <leon@kernel.org> wrote:
> > > On Mon, Mar 09, 2026 at 07:10:25PM -0400, Paul Moore wrote:
> > > > On Mon, Mar 9, 2026 at 3:37 PM Leon Romanovsky <leon@kernel.org> wrote:
> > > > > On Mon, Mar 09, 2026 at 02:32:39PM -0400, Paul Moore wrote:
> > > > > > On Mon, Mar 9, 2026 at 7:15 AM Leon Romanovsky <leon@kernel.org> wrote:
> >
> > ...
> >
> > > > > > Hi Leon,
> > > > > >
> > > > > > At the link below, you'll find guidance on submitting new LSM hooks.
> > > > > > Please take a look and let me know if you have any questions.
> > > > > >
> > > > > > https://github.com/LinuxSecurityModule/kernel/blob/main/README.md#new-lsm-hooks
> > > > >
> > > > > I assume that you are referring to this part:
> > > >
> > > > I'm referring to all of the guidance, but yes, at the very least that
> > > > is something that I think we need to see in a future revision of this
> > > > patchset.
> > > >
> > > > >  * New LSM hooks must demonstrate their usefulness by providing a meaningful
> > > > >    implementation for at least one in-kernel LSM. The goal is to demonstrate
> > > > >    the purpose and expected semantics of the hooks. Out of tree kernel code,
> > > > >    and pass through implementations, such as the BPF LSM, are not eligible
> > > > >    for LSM hook reference implementations.
> > > > >
> > > > > The point is that we are not inspecting a kernel call, but the FW mailbox,
> > > > > which has very little meaning to the kernel. From the kernel's perspective,
> > > > > all relevant checks have already been performed, but the existing capability
> > > > > granularity does not allow us to distinguish between FW_CMD1 and FW_CMD2.
> > > >
> > > > It might help if you could phrase this differently, as I'm not
> > > > entirely clear on your argument.  LSMs are not limited to enforcing
> > > > access controls on requests the kernel understands (see the SELinux
> > > > userspace object manager concept), and the idea of access controls
> > > > with greater granularity than capabilities is one of the main reasons
> > > > people look to LSMs for access control (SELinux, AppArmor, Smack,
> > > > etc.).
> > >
> > > I should note that my understanding of LSM is limited, so some parts of my
> > > answers may be inaccurate.
> > >
> > > What I am referring to is a different level of granularity — specifically,
> > > the internals of the firmware commands. In the proposed approach, BPF
> > > programs would make decisions based on data passed through the mailbox.
> > > That mailbox format varies across vendors, and may even differ between
> > > firmware versions from the same vendor.
> >
> > That helps, thank you.
> >
> > > > > Here we propose a generic interface that can be applied to all FWCTL
> > > > > devices without out-of-tree kernel code at all.
> > > >
> > > > I expected to see a patch implementing some meaningful support for
> > > > access controls using these hooks in one of the existing LSMs, I did
> > > > not see that in this patchset.
> > >
> > > In some cases, the mailbox is forwarded from user space unchanged, but
> > > in others the kernel modifies it before submitting it to the FW.
> >
> > Without a standard format, opcode definitions, etc. I suspect
> > integrating this into an LSM will present a number of challenges.
>
> The opcode is relatively easy to extract from the mailbox and pass to the LSM.
> All drivers implement some variant of mlx5ctl_validate_rpc()/devx_is_general_cmd()
> to validate the opcode. The problem is that this check alone is not sufficient.
>
> > Instead of performing an LSM access control check before submitting
> > the firmware command, it might be easier from an LSM perspective to
> > have the firmware call into the kernel/LSM for an access control
> > decision before performing a security-relevant action.
>
> Ultimately, the LSM must make a decision for each executed firmware
> command. This will need to be handled one way or another, and will
> likely require parsing the mailbox again.

As it's unlikely that parsing the mailbox is something that a LSM will
want to handle, my suggestion was to leverage the existing mailbox
parsing in the firmware and require the firmware to call into the LSM
when authorization is needed.

> > This removes the challenge of parsing/interpreting the arbitrary firmware commands,
> > but it does add some additional complexity of having to generically
> > represent the security relevant actions the firmware might request
>
> The difference here is that the proposed LSM hook is intended to disable
> certain functionality provided by the firmware, effectively depending on
> the operator’s preferences.

My suggestion would also allow a LSM hook to disable certain firmware
functionality; however, the firmware itself would need to call the LSM
to check if the functionality is authorized.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG
From: Eric Biggers @ 2026-03-10 22:01 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity
In-Reply-To: <20260113-module-hashes-v4-9-0b932db9b56b@weissschuh.net>

On Tue, Jan 13, 2026 at 01:28:53PM +0100, Thomas Weißschuh wrote:
> The loading policy functionality will also be used by the hash-based
> module validation. Split it out from CONFIG_MODULE_SIG so it is usable
> by both.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  include/linux/module.h  |  8 ++++----
>  kernel/module/Kconfig   |  5 ++++-
>  kernel/module/main.c    | 26 +++++++++++++++++++++++++-
>  kernel/module/signing.c | 21 ---------------------
>  4 files changed, 33 insertions(+), 27 deletions(-)
> 
> diff --git a/include/linux/module.h b/include/linux/module.h
> index f288ca5cd95b..f9601cba47cd 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -444,7 +444,7 @@ struct module {
>  	const u32 *gpl_crcs;
>  	bool using_gplonly_symbols;
>  
> -#ifdef CONFIG_MODULE_SIG
> +#ifdef CONFIG_MODULE_SIG_POLICY
>  	/* Signature was verified. */
>  	bool sig_ok;
>  #endif
[...]
> +config MODULE_SIG_POLICY
> +	def_bool MODULE_SIG

Maybe MODULE_AUTH_POLICY?  Hash-based module authentication does not use
signatures.

This issue appears elsewhere in the code too.  There are lots of places
that still refer to module signatures or "sigs", when really module
authentication is meant.

I'm not sure how far you want to go with the renaming, but it's
something to think about.  It's confusing to use the term "signature" to
mean something that is not a signature.

- Eric

^ permalink raw reply

* Re: [PATCH v4 10/17] module: Move integrity checks into dedicated function
From: Eric Biggers @ 2026-03-10 22:06 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity
In-Reply-To: <20260113-module-hashes-v4-10-0b932db9b56b@weissschuh.net>

On Tue, Jan 13, 2026 at 01:28:54PM +0100, Thomas Weißschuh wrote:
> +static int module_integrity_check(struct load_info *info, int flags)
> +{
> +	int err = 0;
> +
> +	if (IS_ENABLED(CONFIG_MODULE_SIG))
> +		err = module_sig_check(info, flags);
> +
> +	return err;
> +}

Maybe module_authenticity_check()?  The purpose is authenticity, not
merely integrity.

- Eric

^ permalink raw reply

* Re: [PATCH v2 1/2] kthread: remove kthread_exit()
From: Frederic Weisbecker @ 2026-03-10 22:26 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, linux-kernel, linux-modules, linux-nfs, bpf,
	kunit-dev, linux-doc, linux-trace-kernel, netfs, io-uring, audit,
	rcu, kvm, virtualization, netdev, linux-mm, linux-security-module,
	Christian Loehle, linux-fsdevel
In-Reply-To: <20260310-work-kernel-exit-v2-1-30711759d87b@kernel.org>

Le Tue, Mar 10, 2026 at 03:56:09PM +0100, Christian Brauner a écrit :
> In 28aaa9c39945 ("kthread: consolidate kthread exit paths to prevent use-after-free")
> we folded kthread_exit() into do_exit() when we fixed a nasty UAF bug.
> We left kthread_exit() around as an alias to do_exit(). Remove it
> completely.

Thanks for fixing that UAF! I unfortunately missed it.

> 
> Reported-by: Christian Loehle <christian.loehle@arm.com>
> Link: https://lore.kernel.org/1ff1bce2-8bb4-463c-a631-16e14f4ea7e2@arm.com
> Signed-off-by: Christian Brauner <brauner@kernel.org>

Reviewed-by: Frederic Weisbecker <frederic@kernel.org>

-- 
Frederic Weisbecker
SUSE Labs

^ permalink raw reply

* Re: [PATCH v2 2/2] tree-wide: rename do_exit() to task_exit()
From: Frederic Weisbecker @ 2026-03-10 22:30 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, linux-kernel, linux-modules, linux-nfs, bpf,
	kunit-dev, linux-doc, linux-trace-kernel, netfs, io-uring, audit,
	rcu, kvm, virtualization, netdev, linux-mm, linux-security-module,
	Christian Loehle, linux-fsdevel
In-Reply-To: <20260310-work-kernel-exit-v2-2-30711759d87b@kernel.org>

Le Tue, Mar 10, 2026 at 03:56:10PM +0100, Christian Brauner a écrit :
> Rename do_exit() to task_exit() so it's clear that this is an api and
> not a hidden internal helper.
> 
> Signed-off-by: Christian Brauner <brauner@kernel.org>

Acked-by: Frederic Weisbecker <frederic@kernel.org>

-- 
Frederic Weisbecker
SUSE Labs

^ permalink raw reply

* Re: [PATCH v6] lsm: Add LSM hook security_unix_find
From: Paul Moore @ 2026-03-10 22:39 UTC (permalink / raw)
  To: Günther Noack
  Cc: Justin Suess, brauner, demiobenour, fahimitahera, hi, horms,
	ivanov.mikhail1, jannh, jmorris, john.johansen,
	konstantin.meskhidze, linux-security-module, m, matthieu, mic,
	netdev, samasth.norway.ananda, serge, viro
In-Reply-To: <20260219.de5dc35ec231@gnoack.org>

On Thu, Feb 19, 2026 at 3:26 PM Günther Noack <gnoack3000@gmail.com> wrote:
> On Thu, Feb 19, 2026 at 03:04:59PM -0500, Justin Suess wrote:
> > Add a LSM hook security_unix_find.
> >
> > This hook is called to check the path of a named unix socket before a
> > connection is initiated. The peer socket may be inspected as well.
> >
> > Why existing hooks are unsuitable:
> >
> > Existing socket hooks, security_unix_stream_connect(),
> > security_unix_may_send(), and security_socket_connect() don't provide
> > TOCTOU-free / namespace independent access to the paths of sockets.
> >
> > (1) We cannot resolve the path from the struct sockaddr in existing hooks.
> > This requires another path lookup. A change in the path between the
> > two lookups will cause a TOCTOU bug.
> >
> > (2) We cannot use the struct path from the listening socket, because it
> > may be bound to a path in a different namespace than the caller,
> > resulting in a path that cannot be referenced at policy creation time.
> >
> > Cc: Günther Noack <gnoack3000@gmail.com>
> > Cc: Tingmao Wang <m@maowtm.org>
> > Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> > ---
> >  include/linux/lsm_hook_defs.h |  5 +++++
> >  include/linux/security.h      | 11 +++++++++++
> >  net/unix/af_unix.c            | 13 ++++++++++---
> >  security/security.c           | 20 ++++++++++++++++++++
> >  4 files changed, 46 insertions(+), 3 deletions(-)

...

> Reviewed-by: Günther Noack <gnoack3000@gmail.com>
>
> Thank you, this looks good. I'll include it in the next version of the
> Unix connect patch set again.

I'm looking for this patchset to review/ACK the new hook in context,
but I'm not seeing it in my inbox or lore.  Did I simply miss the
patchset or is it still a work in progress?  No worries if it hasn't
been posted yet, I just wanted to make sure I wasn't holding this up
any more than I already may have :)

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v2 2/2] tree-wide: rename do_exit() to task_exit()
From: Steven Rostedt @ 2026-03-11  0:02 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, linux-kernel, linux-modules, linux-nfs, bpf,
	kunit-dev, linux-doc, linux-trace-kernel, netfs, io-uring, audit,
	rcu, kvm, virtualization, netdev, linux-mm, linux-security-module,
	Christian Loehle, linux-fsdevel
In-Reply-To: <20260310-work-kernel-exit-v2-2-30711759d87b@kernel.org>

On Tue, 10 Mar 2026 15:56:10 +0100
Christian Brauner <brauner@kernel.org> wrote:

> diff --git a/tools/testing/selftests/bpf/progs/tracing_failure.c b/tools/testing/selftests/bpf/progs/tracing_failure.c
> index 65e485c4468c..5144f4cc5787 100644
> --- a/tools/testing/selftests/bpf/progs/tracing_failure.c
> +++ b/tools/testing/selftests/bpf/progs/tracing_failure.c
> @@ -25,7 +25,7 @@ int BPF_PROG(tracing_deny)
>  	return 0;
>  }
>  
> -SEC("?fexit/do_exit")
> +SEC("?fexit/task_exit")
>  int BPF_PROG(fexit_noreturns)
>  {
>  	return 0;
> diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
> index fee479295e2f..7e00d8ecd110 100644
> --- a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
> +++ b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
> @@ -82,7 +82,7 @@ check_error 'f vfs_read arg1=^'			# NO_ARG_BODY
>  # multiprobe errors
>  if grep -q "Create/append/" README && grep -q "imm-value" README; then
>  echo "f:fprobes/testevent $FUNCTION_FORK" > dynamic_events
> -check_error '^f:fprobes/testevent do_exit%return'	# DIFF_PROBE_TYPE
> +check_error '^f:fprobes/testevent task_exit%return'	# DIFF_PROBE_TYPE
>  
>  # Explicitly use printf "%s" to not interpret \1
>  printf "%s" "f:fprobes/testevent $FUNCTION_FORK abcd=\\1" > dynamic_events
> diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc
> index f0d5b7777ed7..a95e3824690a 100644
> --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc
> +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc
> @@ -5,7 +5,7 @@
>  
>  # Choose 2 symbols for target
>  SYM1=$FUNCTION_FORK
> -SYM2=do_exit
> +SYM2=task_exit
>  EVENT_NAME=kprobes/testevent
>  
>  DEF1="p:$EVENT_NAME $SYM1"
> diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
> index 8f1c58f0c239..b55ea3c05cfa 100644
> --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
> +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
> @@ -87,7 +87,7 @@ esac
>  # multiprobe errors
>  if grep -q "Create/append/" README && grep -q "imm-value" README; then
>  echo "p:kprobes/testevent $FUNCTION_FORK" > kprobe_events
> -check_error '^r:kprobes/testevent do_exit'	# DIFF_PROBE_TYPE
> +check_error '^r:kprobes/testevent task_exit'	# DIFF_PROBE_TYPE
>  
>  # Explicitly use printf "%s" to not interpret \1
>  printf "%s" "p:kprobes/testevent $FUNCTION_FORK abcd=\\1" > kprobe_events

These tests need to pass on old kernels too. So we can't just do a
"s/do_exit/task_exit/" conversion. It needs to test for task_exit first,
and if not found, fallback to do_exit.

See how we handled the _do_fork() > kernel_clone() rename:

  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/ftrace/test.d/functions#n182

-- Steve

^ permalink raw reply

* Re: [PATCH 00/61] treewide: Use IS_ERR_OR_NULL over manual NULL check - refactor
From: Russell King (Oracle) @ 2026-03-11  0:09 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Julia Lawall, Nicolas Palix, Chris Mason,
	David Sterba, Ilya Dryomov, Alex Markuze, Viacheslav Dubeyko,
	Theodore Ts'o, Andreas Dilger, Steve French, Paulo Alcantara,
	Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
	Eric Van Hensbergen, Latchesar Ionkov, Dominique Martinet,
	Christian Schoenebeck, Gao Xiang, Chao Yu, Yue Hu, Jeffle Xu,
	Sandeep Dhavale, Hongbo Li, Chunhai Guo, Miklos Szeredi,
	Konstantin Komarov, Andreas Gruenbacher, Kees Cook, Tony Luck,
	Guilherme G. Piccoli, Jan Kara, Phillip Lougher, Alexander Viro,
	Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Tejun Heo, David Vernet, Andrea Righi,
	Changwoo Min, Ingo Molnar, Peter Zijlstra, Juri Lelli,
	Vincent Guittot, Dietmar Eggemann, Ben Segall, Mel Gorman,
	Valentin Schneider, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
	Sami Tolvanen, Aaron Tomlin, Sylwester Nawrocki, Liam Girdwood,
	Mark Brown, Jaroslav Kysela, Takashi Iwai, Max Filippov,
	Paolo Bonzini, John Johansen, Paul Moore, James Morris,
	Serge E. Hallyn, Andrew Morton, Alasdair Kergon, Mike Snitzer,
	Mikulas Patocka, Benjamin Marzinski, David S. Miller, David Ahern,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Marcel Holtmann, Johan Hedberg, Luiz Augusto von Dentz,
	Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
	John Fastabend, Stanislav Fomichev, Jamal Hadi Salim, Jiri Pirko,
	Marcelo Ricardo Leitner, Xin Long, Trond Myklebust,
	Anna Schumaker, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Jon Maloy, Johannes Berg,
	Catalin Marinas, John Crispin, Thomas Bogendoerfer,
	Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Zhenyu Wang,
	Zhi Wang, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	Tvrtko Ursulin, Alex Deucher, Christian König, Sandy Huang,
	Heiko Stübner, Andy Yan, Igor Russkikh, Andrew Lunn,
	Pavan Chebbi, Michael Chan, Potnuri Bharat Teja, Tony Nguyen,
	Przemek Kitszel, Taras Chornyi, Maxime Coquelin, Alexandre Torgue,
	Iyappan Subramanian, Keyur Chudgar, Quan Nguyen, Heiner Kallweit,
	Marc Zyngier, Thomas Gleixner, Andrew Lunn, Gregory Clement,
	Sebastian Hesselbarth, Vinod Koul, Linus Walleij, Ulf Hansson,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Martin K. Petersen,
	Eduardo Valentin, Keerthy, Rafael J. Wysocki, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Alex Williamson, Mark Greer,
	Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Shuah Khan, Kieran Bingham, Mauro Carvalho Chehab, Joerg Roedel,
	Will Deacon, Robin Murphy, Lee Jones, Pavel Machek, Dave Penkler,
	K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
	Justin Sanders, Jens Axboe, Georgi Djakov, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Pali Rohár, Dmitry Torokhov
In-Reply-To: <20260310-b4-is_err_or_null-v1-0-bd63b656022d@avm.de>

On Tue, Mar 10, 2026 at 12:48:26PM +0100, Philipp Hahn wrote:
> While doing some static code analysis I stumbled over a common pattern,
> where IS_ERR() is combined with a NULL check. For that there is
> IS_ERR_OR_NULL().

One thing you need to check for each of these cases is whether the tests
are actually correct.

There are certainly cases amongst those that you have identified where
the check for NULL is redundant.

For example, get_phy_device() never returns NULL, yet in your netdev
patch, you have at least one instance where the return value of
get_phy_device() is checked for NULL and IS_ERR() which you then
turn into IS_ERR_OR_NULL(). Instead, the NULL check should be dropped
(as a separate patch.)

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* Re: [PATCH 38/61] net: Prefer IS_ERR_OR_NULL over manual NULL check
From: Russell King (Oracle) @ 2026-03-11  0:16 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Igor Russkikh, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Pavan Chebbi, Michael Chan, Potnuri Bharat Teja, Tony Nguyen,
	Przemek Kitszel, Taras Chornyi, Maxime Coquelin, Alexandre Torgue,
	Iyappan Subramanian, Keyur Chudgar, Quan Nguyen, Heiner Kallweit
In-Reply-To: <20260310-b4-is_err_or_null-v1-38-bd63b656022d@avm.de>

On Tue, Mar 10, 2026 at 12:49:04PM +0100, Philipp Hahn wrote:
> diff --git a/drivers/net/mdio/mdio-xgene.c b/drivers/net/mdio/mdio-xgene.c
> index a8f91a4b7fed0927ee14e408000cd3a2bfb9b09a..09b30b563295c6085dc1358ac361301e5cf6b2a8 100644
> --- a/drivers/net/mdio/mdio-xgene.c
> +++ b/drivers/net/mdio/mdio-xgene.c
> @@ -265,7 +265,7 @@ struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr)
>  	struct phy_device *phy_dev;
>  
>  	phy_dev = get_phy_device(bus, phy_addr, false);
> -	if (!phy_dev || IS_ERR(phy_dev))
> +	if (IS_ERR_OR_NULL(phy_dev))

As noted in reply to your cover message, the check for NULL here is
incorrect - get_phy_device() returns either a valid pointer or an
error pointer, but never NULL.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
From: Eric Biggers @ 2026-03-11  1:12 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity
In-Reply-To: <20260113-module-hashes-v4-15-0b932db9b56b@weissschuh.net>

On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.

I think this actually undersells the feature.  It's also much simpler
than the signature-based module authentication.  The latter relies on
PKCS#7, X.509, ASN.1, OID registry, crypto_sig API, etc in addition to
the implementations of the actual signature algorithm (RSA / ECDSA /
ML-DSA) and at least one hash algorithm.

I've even seen a case where the vmlinux size decreases by almost 200KB
just by disabling module signing.  That's not even counting the
signatures themselves, which ML-DSA has increased to 2-5 KB each.

The hashes are much simpler, even accounting for the Merkle tree proofs
that make them scalable.  They're less likely to have vulnerabilities
like the PKCS#7 bugs the kernel has had historically.  They also
eliminate the dependency on a lot of userspace libcrypto functionality
that has been causing portability problems, such as the CMS functions.

So I think this is how module authentication should have been done
originally, and I'm glad to see this is finally being fixed.

> +struct module_hashes_proof {
> +	__be32 pos;
> +	u8 hash_sigs[][MODULE_HASHES_HASH_SIZE];
> +} __packed;

Is the choice of big endian for consistency with struct
module_signature?  Little endian is the usual choice in new code.

> diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
> index a45ce3b24403..3b510651830d 100644
> --- a/include/linux/module_signature.h
> +++ b/include/linux/module_signature.h
> @@ -18,6 +18,7 @@ enum pkey_id_type {
>  	PKEY_ID_PGP,		/* OpenPGP generated key ID */
>  	PKEY_ID_X509,		/* X.509 arbitrary subjectKeyIdentifier */
>  	PKEY_ID_PKCS7,		/* Signature in PKCS#7 message */
> +	PKEY_ID_MERKLE,		/* Merkle proof for modules */

I recommend making the hash algorithm explicit:

        PKEY_ID_MERKLE_SHA256,	/* SHA-256 merkle proof for modules */

While I wouldn't encourage the addition of another hash algorithm
(specifying one good algorithm for now is absolutely the right choice),
if someone ever does need to add another one, we'd want them to be
guided to simply introduce a new value of this enum rather than hack it
in some other way.

> +static void hash_entry(const void *left, const void *right, void *out)

Byte arrays should use u8 instead of void

> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
[...]

> +struct file_entry {
> +	char *name;
> +	unsigned int pos;
> +	unsigned char hash[EVP_MAX_MD_SIZE];

Considering that the hash algorithm is fixed, EVP_MAX_MD_SIZE can be
replaced with a tighter local definition:

    #define MAX_HASH_SIZE 32

> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> +	struct leaf_hash **l;
> +	unsigned int *entries;
> +	unsigned int levels;
> +};

'struct leaf_hash' is confusing because it's actually used for the
hashes of internal nodes, not leaf nodes.

Maybe rename it to 'struct hash' and use it for both the hashes and leaf
nodes and internal nodes.

Also, clearer naming would improve readability, e.g.:

    struct merkle_tree {
            struct hash **level_hashes;
            unsigned int level_size;
            unsigned int num_levels;
    };

> +static void hash_data(void *p, unsigned int pos, size_t size, void *ret_hash)

static void hash_data(const uint8_t *data, unsigned int pos,
                      size_t size, struct hash *ret_hash)

> +	unsigned char magic = 0x01;

uint8_t

Also, when defining these magic numbers, maybe explicitly document that
they are domain separation prefixes:

        uint8_t magic = 0x01; /* domain separation prefix */

> +	unsigned int pos_be;

uint32_t

> +static void hash_entry(void *left, void *right, void *ret_hash)

Could use stronger typing:

static void hash_entry(const struct hash *left, const struct hash *right,
                       struct hash *ret_hash)

> +static struct mtree *build_merkle(struct file_entry *fh, size_t num)

Could use clearer names, and constify the file_entry array:

static struct merkle_tree *build_merkle(const struct file_entry *files,
                                        size_t num_files)

> +	/* First level of pairs */
> +	for (unsigned int i = 0; i < num; i += 2) {
> +		if (i == num - 1) {
> +			/* Odd number of files, no pair. Hash with itself */
> +			hash_entry(fh[i].hash, fh[i].hash, mt->l[0][i / 2].hash);
> +		} else {
> +			hash_entry(fh[i].hash, fh[i + 1].hash, mt->l[0][i / 2].hash);
> +		}
> +	}
> +	for (unsigned int i = 1; i < mt->levels; i++) {
> +		int odd = 0;
> +
> +		if (le & 1) {
> +			le++;
> +			odd++;
> +		}
> +
> +		mt->entries[i] = le / 2;
> +		mt->l[i] = xcalloc(sizeof(**mt->l), le);
> +
> +		for (unsigned int n = 0; n < le; n += 2) {
> +			if (n == le - 2 && odd) {
> +				/* Odd number of pairs, no pair. Hash with itself */
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n].hash,
> +					   mt->l[i][n / 2].hash);
> +			} else {
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n + 1].hash,
> +					   mt->l[i][n / 2].hash);
> +			}
> +		}
> +		le =  mt->entries[i];
> +	}

There should be an assertion at the end that we ended up with exactly 1
hash in the root level.

It might also be possible to refactor this code such that the leaf nodes
and internal nodes are handled in the same loop, rather than handling
the leaf nodes as a special case.

> +static void write_merkle_root(struct mtree *mt, const char *fp)

fp => filename since it's a string, not e.g. a 'FILE *'.

> +{
> +	char buf[1024];
> +	unsigned int levels;
> +	unsigned char *h;
> +	FILE *f;
> +
> +	if (mt) {
> +		levels = mt->levels;
> +		h = mt->l[mt->levels - 1][0].hash;
> +	} else {
> +		levels = 0;
> +		h = xcalloc(1, hash_size);
> +	}
> +
> +	f = fopen(fp, "w");
> +	if (!f)
> +		err(1, "Failed to create %s", buf);

Above should log the name of the file.  'buf' should be removed.

> +static char *xstrdup_replace_suffix(const char *str, const char *new_suffix)
> +{
> +	const char *current_suffix;
> +	size_t base_len;
> +
> +	current_suffix = strchr(str, '.');
> +	if (!current_suffix)
> +		errx(1, "No existing suffix in '%s'", str);

This doesn't handle base names that contain a period.  strrchr() would
work if the old suffix always contains exactly one period.  Otherwise
another solution would be needed to identify the old suffix.

> +static __attribute__((noreturn))
> +void format(void)

usage()

> +{
> +	fprintf(stderr,
> +		"Usage: scripts/modules-merkle-tree <root definition>\n");
> +	exit(2);

This should show both parameters, <root hash> <new suffix>

But they probably should be flipped to put the output second.

Though, is <new suffix> needed at all?  It looks like it doesn't
actually affect the output.

> +	hash_evp = EVP_get_digestbyname("sha256");

EVP_sha256()

> +	hash_size = EVP_MD_get_size(hash_evp);

The old name 'EVP_MD_size()' would have wider compatibility.

> +	ERR(hash_size <= 0, "EVP_get_digestbyname");

Log message should say EVP_MD_size

> +	for (unsigned int i = 0; i < num_files; i++) {

size_t, for consistency with the type of num_files

> +		fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
> +		if (fd < 0)
> +			err(1, "Can't create %s", signame);
> +
> +		build_proof(mt, i, fd);
> +		append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));

Maybe build_and_append_proof()?

- Eric

^ permalink raw reply

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
From: Eric Biggers @ 2026-03-11  1:18 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Thomas Weißschuh, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Sami Tolvanen, Daniel Gomez, Paul Moore,
	James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Nicolas Schier, Daniel Gomez, Aaron Tomlin,
	Christophe Leroy (CS GROUP), Nicolas Schier, Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity
In-Reply-To: <fab2af64-e396-45f9-8876-feff4002e04b@suse.com>

On Tue, Feb 03, 2026 at 01:19:20PM +0100, Petr Pavlu wrote:
> > +static unsigned int get_pow2(unsigned int val)
> > +{
> > +	return 31 - __builtin_clz(val);
> > +}
> > +
> > +static unsigned int roundup_pow2(unsigned int val)
> > +{
> > +	return 1 << (get_pow2(val - 1) + 1);
> > +}
> > +
> > +static unsigned int log2_roundup(unsigned int val)
> > +{
> > +	return get_pow2(roundup_pow2(val));
> > +}
> 
> In the edge case when the kernel is built with only one module, the code
> calls log2_roundup(1) -> roundup_pow2(1) -> get_pow2(0) ->
> __builtin_clz(0). The return value of __builtin_clz() is undefined if
> the input is zero.

A suggestion:

        static unsigned int log2_roundup(unsigned int val)
        {
                if (val <= 1)
                        return 0;
                return 32 - __builtin_clz(val - 1);
        }

- Eric

^ permalink raw reply

* Re: [PATCH 56/61] clk: Prefer IS_ERR_OR_NULL over manual NULL check
From: Chen-Yu Tsai @ 2026-03-11  2:07 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Michael Turquette, Stephen Boyd,
	Daniel Lezcano, Thomas Gleixner
In-Reply-To: <20260310-b4-is_err_or_null-v1-56-bd63b656022d@avm.de>

On Tue, Mar 10, 2026 at 9:57 PM Philipp Hahn <phahn-oss@avm.de> wrote:
>
> Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
> check.
>
> Semantich change: Previously the code only printed the warning on error,
> but not when the pointer was NULL. Now the warning is printed in both
> cases!
>
> Change found with coccinelle.
>
> To: Michael Turquette <mturquette@baylibre.com>
> To: Stephen Boyd <sboyd@kernel.org>
> To: Daniel Lezcano <daniel.lezcano@kernel.org>
> To: Thomas Gleixner <tglx@kernel.org>
> Cc: linux-clk@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Philipp Hahn <phahn-oss@avm.de>
> ---
>  drivers/clk/clk.c               | 4 ++--
>  drivers/clocksource/timer-pxa.c | 2 +-
>  2 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 47093cda9df32223c1120c3710261296027c4cd3..35146e3869a7dd93741d10b7223d4488a9216ed1 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -4558,7 +4558,7 @@ void clk_unregister(struct clk *clk)
>         unsigned long flags;
>         const struct clk_ops *ops;
>
> -       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
> +       if (WARN_ON_ONCE(IS_ERR_OR_NULL(clk)))
>                 return;
>
>         clk_debug_unregister(clk->core);
> @@ -4744,7 +4744,7 @@ void __clk_put(struct clk *clk)
>  {
>         struct module *owner;
>
> -       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
> +       if (WARN_ON_ONCE(IS_ERR_OR_NULL(clk)))

clk_get_optional() returns NULL if the clk isn't present.

Drivers would just pass this to clk_put(). Your change here would cause
this pattern to emit a very big warning.

I don't think this change should be landed.


ChenYu

>                 return;
>
>         clk_prepare_lock();
> diff --git a/drivers/clocksource/timer-pxa.c b/drivers/clocksource/timer-pxa.c
> index 7ad0e5adb2ffac4125c34710fc67f4b45f30331d..f65fb0b7fc318b766227e5e7a4c0fb08ba11c8f9 100644
> --- a/drivers/clocksource/timer-pxa.c
> +++ b/drivers/clocksource/timer-pxa.c
> @@ -218,7 +218,7 @@ void __init pxa_timer_nodt_init(int irq, void __iomem *base)
>
>         timer_base = base;
>         clk = clk_get(NULL, "OSTIMER0");
> -       if (clk && !IS_ERR(clk)) {
> +       if (!IS_ERR_OR_NULL(clk)) {
>                 clk_prepare_enable(clk);
>                 pxa_timer_common_init(irq, clk_get_rate(clk));
>         } else {
>
> --
> 2.43.0
>
>

^ permalink raw reply

* Re: [PATCH v5 2/9] landlock: Control pathname UNIX domain socket resolution by path
From: Kuniyuki Iwashima @ 2026-03-11  4:46 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, Eric Dumazet, Paolo Abeni, Willem de Bruijn,
	Sebastian Andrzej Siewior, Jason Xing, John Johansen,
	Tingmao Wang, Justin Suess, Jann Horn, linux-security-module,
	Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
	konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross,
	Tahera Fahimi, netdev
In-Reply-To: <20260308.AiYoh5KooBei@digikod.net>

On Sun, Mar 8, 2026 at 1:18 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> On Fri, Feb 20, 2026 at 03:33:28PM +0100, Günther Noack wrote:
> > +netdev, we could use some advice on the locking approach in af_unix (see below)
> >
> > On Wed, Feb 18, 2026 at 10:37:14AM +0100, Mickaël Salaün wrote:
> > > On Sun, Feb 15, 2026 at 11:51:50AM +0100, Günther Noack wrote:
> > > > diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> > > > index f88fa1f68b77..3a8fc3af0d64 100644
> > > > --- a/include/uapi/linux/landlock.h
> > > > +++ b/include/uapi/linux/landlock.h
> > > > @@ -248,6 +248,15 @@ struct landlock_net_port_attr {
> > > >   *
> > > >   *   This access right is available since the fifth version of the Landlock
> > > >   *   ABI.
> > > > + * - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX: Look up pathname UNIX domain sockets
> > > > + *   (:manpage:`unix(7)`).  On UNIX domain sockets, this restricts both calls to
> > > > + *   :manpage:`connect(2)` as well as calls to :manpage:`sendmsg(2)` with an
> > > > + *   explicit recipient address.
> > > > + *
> > > > + *   This access right only applies to connections to UNIX server sockets which
> > > > + *   were created outside of the newly created Landlock domain (e.g. from within
> > > > + *   a parent domain or from an unrestricted process).  Newly created UNIX
> > > > + *   servers within the same Landlock domain continue to be accessible.
> > >
> > > It might help to add a reference to the explicit scope mechanism.
> > >
> > > Please squash patch 9/9 into this one and also add a reference here to
> > > the rationale described in security/landlock.rst
> >
> > Sounds good, will do.
> >
> >
> > > > +static void unmask_scoped_access(const struct landlock_ruleset *const client,
> > > > +                          const struct landlock_ruleset *const server,
> > > > +                          struct layer_access_masks *const masks,
> > > > +                          const access_mask_t access)
> > >
> > > This helper should be moved to task.c and factored out with
> > > domain_is_scoped().  This should be a dedicated patch.
> >
> > (already discussed in another follow-up mail)
> >
> >
> > > > +static int hook_unix_find(const struct path *const path, struct sock *other,
> > > > +                   int flags)
> > > > +{
> > > > + const struct landlock_ruleset *dom_other;
> > > > + const struct landlock_cred_security *subject;
> > > > + struct layer_access_masks layer_masks;
> > > > + struct landlock_request request = {};
> > > > + static const struct access_masks fs_resolve_unix = {
> > > > +         .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> > > > + };
> > > > +
> > > > + /* Lookup for the purpose of saving coredumps is OK. */
> > > > + if (unlikely(flags & SOCK_COREDUMP))
> > > > +         return 0;
> > > > +
> > > > + /* Access to the same (or a lower) domain is always allowed. */
> > > > + subject = landlock_get_applicable_subject(current_cred(),
> > > > +                                           fs_resolve_unix, NULL);
> > > > +
> > > > + if (!subject)
> > > > +         return 0;
> > > > +
> > > > + if (!landlock_init_layer_masks(subject->domain, fs_resolve_unix.fs,
> > > > +                                &layer_masks, LANDLOCK_KEY_INODE))
> > > > +         return 0;
> > > > +
> > > > + /* Checks the layers in which we are connecting within the same domain. */
> > > > + dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
> > >
> > > We need to call unix_state_lock(other) before reading it, and check for
> > > SOCK_DEAD, and check sk_socket before dereferencing it.  Indeed,
> > > the socket can be make orphan (see unix_dgram_sendmsg and
> > > unix_stream_connect).  I *think* a socket cannot be "resurrected" or
> > > recycled once dead, so we may assume there is no race condition wrt
> > > dom_other, but please double check.  This lockless call should be made
> > > clear in the LSM hook.  It's OK to not lock the socket before
> > > security_unix_find() (1) because no LSM might implement and (2) they
> > > might not need to lock the socket (e.g. if the caller is not sandboxed).
> > >
> > > The updated code should look something like this:
> > >
> > > unix_state_unlock(other);
>
> unix_state_lock(other) of course...
>
> > > if (unlikely(sock_flag(other, SOCK_DEAD) || !other->sk_socket)) {
> > >     unix_state_unlock(other);
> > >     return 0;
> > > }
> > >
> > > dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
> > > unix_state_unlock(other);
> >
> > Thank you for spotting the locking concern!
> >
> > @anyone from netdev, could you please advise on the correct locking
> > approach here?
> >
> > * Do we need ot check SOCK_DEAD?

It depends ?  But I don't see your full patch and have no
idea how it will be used.


> >
> >   You are saying that we need to do that, but it's not clear to me
> >   why.
> >
> >   If you look at the places where unix_find_other() is called in
> >   af_unix.c, then you'll find that all of them check for SOCK_DEAD and
> >   then restart from unix_find_other() if they do actually discover
> >   that the socket is dead.

Note all callers of unix_find_other() later locks the other
and double check SOCK_DEAD.

The same check in unix_find_other() is to avoid unnecessary
locking in case the socket is dying.


> >  I think that this is catching this race
> >   condition scenario:
> >
> >   * a server socket exists and is alive
> >   * A client connects: af_unix.c's unix_stream_connect() calls
> >     unix_find_other() and finds the server socket...
> >   * (Concurrently): The server closes the socket and enters
> >     unix_release_sock().  This function:
> >     1. disassociates the server sock from the named socket inode
> >        number in the hash table (=> future unix_find_other() calls
> >        will fail).
> >     2. calls sock_orphan(), which sets SOCK_DEAD.
> >   * ...(client connection resuming): unix_stream_connect() continues,
> >     grabs the unix_state_lock(), which apparently protects everything
> >     here, checks that the socket is not dead - and discovers that it
> >     IS suddenly dead.  This was not supposed to happen.  The code
> >     recovers from that by retrying everything starting with the
> >     unix_find_other() call.  From unix_release_sock(), we know that
> >     the inode is not associated with the sock any more -- so the
> >     unix_find_socket_by_inode() call should be failing on the next
> >     attempt.
> >
> >   (This works with unix_dgram_connect() and unix_dgram_sendmsg() as
> >   well.)
> >
> >   The comments next to the SOCK_DEAD checks are also suggesting this.
> >
> > * What lock to use
> >
> >   I am having trouble reasoning about what lock is used for what in
> >   this code.
>
> It's not clear to me neither, and it looks like it's not consistent
> across protocols.
>
> >
> >   Is it possible that the lock protecting ->sk_socket is the
> >   ->sk_callback_lock, not the unix_state_lock()?

Yes, but unix_state_lock() is better.

BPF SOCKMAP is the major user of sk_callback_lock.


> > The only callers to
> >   sk_set_socket are either sock_orphan/sock_graft (both grabbing
> >   sk_callback_lock), or they are creating new struct sock objects that
> >   they own exclusively, and don't need locks yet.
> >
> >   Admittedly, in af_unix.c, sock_orphan() and sock_graft() only get
> >   called in contexts where the unix_state_lock() is held, so it would
> >   probably work as well to lock that, but it is maybe a more
> >   fine-grained approach to use sk_callback_lock?
> >
> >
> > So... how about a scheme where we only check for ->sk_socket not being
> > NULL:
> >
> > read_lock_bh(&other->sk_callback_lock);
> > struct sock *other_sk = other->sk_socket;
> > if (!other_sk) {
> >       read_unlock_bh(&other->sk_callback_lock);
> >       return 0;
> > }
> > /* XXX double check whether we need a lock here too */
> > struct file *file = other_sk->file;
> > if (!other_file) {
> >       read_unlock_bh(&other->sk_callback_lock);
> >       return 0;
> > }
> > read_unlock_bh(&other->sk_callback_lock);
> >
> > If this fails, that would in my understanding also be because the
> > socket has died after the path lookup.  We'd then return 0 (success),
> > because we know that the surrounding SOCK_DEAD logic will repeat
> > everything starting from the path lookup operation (this time likely
> > failing with ECONNREFUSED, but maybe also with a success, if another
> > server process was quick enough).
> >
> > Does this sound reasonable?
>
> Actually, since commit 983512f3a87f ("net: Drop the lock in
> skb_may_tx_timestamp()"), we can just use RCU + READ_ONCE(sk_socket) +
> READ_ONCE(file).  The socket and file should only be freed after the RCU
> grace periode.  As a safeguard, this commit should be a Depends-on.

Note this commit is for the networking fast path (interrupt context),
where we want to avoid unnecessary locking as much as possible.

AF_UNIX works in the process context only and does not
need to follow the pattern.


>
> However, it is safer to return -ECONNREFULED when sk_socket or file are
> NULL.
>
> I would be good to hear from netdev folks though.
>
> TIL, there is an LSM hook for sock_graft().
>
> > –Günther
> >

^ permalink raw reply

* [PATCH 0/5] rust: lsm: introduce safe Rust abstractions for the LSM framework
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie

This series introduces the first safe Rust abstractions for the Linux
Security Module (LSM) framework.  It allows a complete, policy-enforcing
LSM to be written entirely in Rust with no C boilerplate required from
the LSM author.

--- Motivation ---

The LSM framework is a natural target for Rust: hook registration is
unsafe by nature (raw function pointers, C ABI, __randomize_layout on
the hook list struct), and the trait system can enforce correct
implementation at compile time.

--- Design ---

The abstraction is trait-based:

  impl kernel::lsm::Hooks for MyLsm {
      fn file_open(file: &File) -> Result { ... }
  }
  kernel::define_lsm!(MyLsm, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);

The define_lsm! macro generates all the plumbing: the lsm_id, the hook
array, the __init function that calls security_add_hooks(), and the
lsm_info descriptor placed in .lsm_info.init so that security_init()
discovers it at boot.

Hook implementations receive safe Rust wrappers (&File, &Task) and
return kernel::error::Result.  The C-callable adapter functions are
monomorphised per LSM type -- no vtable, no runtime dispatch.

--- Implementation notes for reviewers ---

static mut __LSM_HOOKS:
  The hook array is static mut because it is written exactly once by
  __lsm_init() in the single-threaded boot context, before
  security_add_hooks() copies the entries into the static-call table.
  No access occurs after that point.  UnsafeCell or a lock would add
  overhead with no safety benefit in this strictly init-only path.

unsafe impl Sync on LsmId / LsmInfo:
  Both types are #[repr(transparent)] wrappers around lsm_id / lsm_info,
  which contain *const c_char pointers to 'static NUL-terminated string
  literals that are never mutated.  The Sync impl is sound for the same
  reason that &'static str is Sync.

MaybeUninit hook array:
  security_hook_list carries __randomize_layout.  Rust code cannot safely
  initialise it by field name (the field order after randomisation differs
  from what bindgen sees).  The array is left MaybeUninit and filled at
  runtime by C shims that call LSM_HOOK_INIT() -- the macro the kernel
  already uses for this purpose in C LSMs.

Function pointer types:
  All three hook signatures were verified against union security_list_options
  generated from lsm_hook_defs.h in this exact tree.  file_open:
  int(*)(struct file *); task_alloc: int(*)(struct task_struct *, u64)
  (u64, not unsigned long -- tree-specific); task_free:
  void(*)(struct task_struct *).  All match the C shim parameters exactly.

checkpatch note:
  checkpatch reports "Non-standard signature: Assisted-by" and
  "Unrecognized email address: Claude:claude-sonnet-4-6".  The
  Assisted-by tag and its format are defined in
  Documentation/process/coding-assistants.rst; checkpatch has not yet
  been updated to recognise it.  This is a false positive.

--- Limitations (v1) ---

- Three hooks: file_open, task_alloc, task_free.  Additional hooks will
  be added in follow-up patches as safe Rust wrappers for their argument
  types are contributed upstream.
- At most one Rust LSM per kernel build (unique static symbol names
  require a proc-macro; planned for v2).
- No security blob support (SecurityBlob<T> planned for v2).

--- Testing ---

Compiled and boot-tested on Linux 7.0-rc2
(commit 4ae12d8bd9a8 "Merge tag 'kbuild-fixes-7.0-2' of
git://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux").

Boot verified: with lsm=rust_lsm_sample on the kernel command line,
/sys/kernel/security/lsm reports "capability,rust_lsm_sample" and
task_alloc hook invocations appear in dmesg.

--- Patch overview ---

  [1/5] rust: bindings: include lsm_hooks.h -- expose LSM types to bindgen
  [2/5] rust: helpers: C shims for LSM_HOOK_INIT and security_add_hooks
  [3/5] rust: kernel: lsm -- Hooks trait, Adapter<T>, define_lsm! macro
  [4/5] security: rust_lsm_sample -- reference implementation and boot test
  [5/5] Documentation: rust: lsm abstraction developer guide; MAINTAINERS

Jamie Lindsey (5):
  rust: bindings: include lsm_hooks.h to expose LSM types to bindgen
  rust: helpers: add C shims for LSM hook initialisation
  rust: kernel: add LSM abstraction layer
  security: add Rust LSM sample (CONFIG_SECURITY_RUST_LSM)
  Documentation: rust: add LSM abstraction guide; update MAINTAINERS

 Documentation/rust/index.rst    |   1 +
 Documentation/rust/lsm.rst      | 246 ++++++++++++++++++++++++++
 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/lsm.c              |  49 ++++++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/lsm.rs              | 295 ++++++++++++++++++++++++++++++++
 security/Kconfig                |   2 +
 security/Makefile               |   1 +
 security/rust_lsm/Kconfig       |  14 ++
 security/rust_lsm/Makefile      |   2 +
 security/rust_lsm/rust_lsm.rs   |  66 +++++++
 13 files changed, 689 insertions(+)
 create mode 100644 Documentation/rust/lsm.rst
 create mode 100644 rust/helpers/lsm.c
 create mode 100644 rust/kernel/lsm.rs
 create mode 100644 security/rust_lsm/Kconfig
 create mode 100644 security/rust_lsm/Makefile
 create mode 100644 security/rust_lsm/rust_lsm.rs

-- 
2.53.0


^ permalink raw reply

* [PATCH 1/5] rust: bindings: include lsm_hooks.h to expose LSM types to bindgen
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311045601.649904-1-jamie@matrixforgelabs.com>

Add lsm_hooks.h to bindings_helper.h so that bindgen generates Rust
bindings for lsm_id, lsm_info, security_hook_list, and LSM_ID_*
constants.  These are required by the Rust LSM abstraction layer
introduced in subsequent patches.

Placed alphabetically between jump_label.h and mdio.h.

Assisted-by: Claude:claude-sonnet-4-6
---
 rust/bindings/bindings_helper.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..b819592868e3 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -63,6 +63,7 @@
 #include <linux/ioport.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
+#include <linux/lsm_hooks.h>
 #include <linux/mdio.h>
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
-- 
2.53.0


^ permalink raw reply related

* [PATCH 2/5] rust: helpers: add C shims for LSM hook initialisation
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311045601.649904-1-jamie@matrixforgelabs.com>

struct security_hook_list carries __randomize_layout, making its fields
inaccessible to Rust code (bindgen sees the natural field order, which
may differ when CONFIG_RANDSTRUCT is active).  Add thin C wrapper
functions that call LSM_HOOK_INIT() — the existing C macro that handles
the randomised layout correctly — so that Rust can populate hook slots
without touching struct fields directly.

Three hook shims are provided (file_open, task_alloc, task_free) plus a
wrapper for security_add_hooks() which is __init and must carry that
annotation to ensure correct placement in the init text section.

Hook signatures verified against union security_list_options generated
from lsm_hook_defs.h: all three match exactly, including u64 for
task_alloc's clone_flags parameter (not unsigned long).

Assisted-by: Claude:claude-sonnet-4-6
---
 rust/helpers/helpers.c |  1 +
 rust/helpers/lsm.c     | 49 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 rust/helpers/lsm.c

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a..c496917be073 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -35,6 +35,7 @@
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
+#include "lsm.c"
 #include "maple_tree.c"
 #include "mm.c"
 #include "mutex.c"
diff --git a/rust/helpers/lsm.c b/rust/helpers/lsm.c
new file mode 100644
index 000000000000..fb475428fd0d
--- /dev/null
+++ b/rust/helpers/lsm.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/lsm_hooks.h>
+
+/*
+ * Hook list initializers.
+ *
+ * struct security_hook_list carries __randomize_layout, so its fields must
+ * never be set by Rust code directly (bindgen sees only the "natural" field
+ * order, which may differ when CONFIG_RANDSTRUCT is enabled).  These helpers
+ * wrap LSM_HOOK_INIT(), the C macro the kernel already uses for this purpose,
+ * so that Rust can safely obtain an initialised security_hook_list without
+ * ever touching the struct's fields directly.
+ */
+
+__rust_helper void
+rust_helper_lsm_hook_init_file_open(struct security_hook_list *hl,
+				    int (*fn)(struct file *))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(file_open, fn);
+}
+
+__rust_helper void
+rust_helper_lsm_hook_init_task_alloc(struct security_hook_list *hl,
+				     int (*fn)(struct task_struct *, u64))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(task_alloc, fn);
+}
+
+__rust_helper void
+rust_helper_lsm_hook_init_task_free(struct security_hook_list *hl,
+				    void (*fn)(struct task_struct *))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(task_free, fn);
+}
+
+/*
+ * security_add_hooks() is __init — it installs callbacks into the static-call
+ * table and must only be called during kernel initialisation.  This wrapper
+ * keeps the __init annotation so the linker places it alongside the rest of
+ * the init text, and Rust callers invoke it only from their own __init path
+ * (the lsm_info.init callback).
+ */
+void __init
+rust_helper_security_add_hooks(struct security_hook_list *hooks, int count,
+			       const struct lsm_id *lsmid)
+{
+	security_add_hooks(hooks, count, lsmid);
+}
-- 
2.53.0


^ permalink raw reply related

* [PATCH 3/5] rust: kernel: add LSM abstraction layer
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311045601.649904-1-jamie@matrixforgelabs.com>

Introduce kernel::lsm, a safe Rust interface for writing Linux Security
Modules:

- Hooks trait: one method per LSM hook, all defaulting to no-op allow.
  Sync + Send + 'static bounds are required because hooks may be called
  concurrently from any CPU.

- Adapter<T>: zero-cost monomorphised bridge converting unsafe extern "C"
  callbacks (required by the hook ABI) to safe Rust.  No vtable; each
  hook is a standalone function pointer.

- LsmId / LsmInfo: #[repr(transparent)] wrappers with unsafe impl Sync
  around lsm_id and lsm_info.  Both types contain *const c_char pointers
  to 'static string literals which are never mutated, making the Sync
  impl sound.

- define_lsm! macro: generates the static __LSM_ID, the MaybeUninit hook
  array __LSM_HOOKS, the __lsm_init() init function that fills hook slots
  via the C shims and calls security_add_hooks(), and the __LSM_INFO
  descriptor placed in .lsm_info.init so that security_init() discovers
  and calls the init function at boot.

The static mut __LSM_HOOKS array is intentionally mutable: it is written
exactly once by __lsm_init() in the single-threaded boot context before
security_add_hooks() copies the entries into the static-call table.  No
access occurs after that point.  UnsafeCell or a lock would add overhead
with no safety benefit in this init-only path.

Gated on CONFIG_SECURITY via #[cfg(CONFIG_SECURITY)] in lib.rs.

This is v1.  Planned follow-ups: SecurityBlob<T> for per-inode/per-task
state, proc-macro support for multiple concurrent Rust LSMs, and
additional hook wrappers as safe Rust types for their arguments land
upstream.

Assisted-by: Claude:claude-sonnet-4-6
---
 rust/kernel/lib.rs |   2 +
 rust/kernel/lsm.rs | 295 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 297 insertions(+)
 create mode 100644 rust/kernel/lsm.rs

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..af3e2416a581 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -112,6 +112,8 @@
 #[cfg(CONFIG_KUNIT)]
 pub mod kunit;
 pub mod list;
+#[cfg(CONFIG_SECURITY)]
+pub mod lsm;
 pub mod maple_tree;
 pub mod miscdevice;
 pub mod mm;
diff --git a/rust/kernel/lsm.rs b/rust/kernel/lsm.rs
new file mode 100644
index 000000000000..bdd3ba4f72ba
--- /dev/null
+++ b/rust/kernel/lsm.rs
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Linux Security Module (LSM) abstractions.
+//!
+//! This module provides safe Rust abstractions for implementing Linux Security
+//! Modules.  An LSM written in Rust implements the [`Hooks`] trait, then
+//! registers itself at boot time with the [`define_lsm!`] macro.
+//!
+//! # Minimal example
+//!
+//! ```no_run
+//! use kernel::lsm;
+//! use kernel::prelude::*;
+//!
+//! struct MyLsm;
+//!
+//! impl lsm::Hooks for MyLsm {
+//!     fn file_open(file: &kernel::fs::File) -> Result {
+//!         pr_info!("file_open: flags={:#x}\n", file.flags());
+//!         Ok(())
+//!     }
+//! }
+//!
+//! kernel::define_lsm!(MyLsm, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);
+//! ```
+//!
+//! C headers: [`include/linux/lsm_hooks.h`](srctree/include/linux/lsm_hooks.h),
+//!            [`include/linux/security.h`](srctree/include/linux/security.h)
+
+use crate::{bindings, fs::File, task::Task};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+
+/// Wrapper around [`bindings::lsm_id`] that implements [`Sync`].
+///
+/// `lsm_id` contains a `*const c_char` name pointer (not `Sync` by default).
+/// The pointer always points to a string literal in static storage and is
+/// never mutated after construction, so sharing across threads is safe.
+#[repr(transparent)]
+pub struct LsmId(pub bindings::lsm_id);
+
+// SAFETY: `lsm_id` holds only a static string pointer and a numeric ID.
+// Both are immutable after construction.
+unsafe impl Sync for LsmId {}
+
+/// Wrapper around [`bindings::lsm_info`] that implements [`Sync`].
+///
+/// `lsm_info` contains several raw pointer fields.  All are set to static
+/// addresses during `define_lsm!` macro expansion and are never mutated
+/// after that, so sharing across threads is safe.
+#[repr(transparent)]
+pub struct LsmInfo(pub bindings::lsm_info);
+
+// SAFETY: `lsm_info` is initialised once (at compile time via const fn)
+// with pointers to static data, then placed in `.lsm_info.init` as a
+// read-only descriptor.
+unsafe impl Sync for LsmInfo {}
+
+/// The trait that a Rust LSM must implement.
+///
+/// Each method corresponds to a kernel LSM hook.  Methods have default
+/// no-op implementations so you only need to override the hooks you care
+/// about.
+///
+/// # Safety contract for implementors
+///
+/// All methods are called from kernel context and may be called concurrently.
+/// Implementors must ensure their implementations are:
+///
+/// - **Thread-safe** — multiple CPUs may call the same hook simultaneously.
+/// - **Non-sleeping where required** — [`Hooks::task_free`] runs in a context
+///   that must not sleep; see its documentation.
+///
+/// The `Sync + Send + 'static` bounds enforce these requirements at the
+/// type level.
+pub trait Hooks: Sync + Send + 'static {
+    /// Called when a file is being opened.
+    ///
+    /// Return `Ok(())` to allow the open, or an `Err` to deny it.
+    /// Common denial codes: [`EACCES`](crate::error::code::EACCES),
+    /// [`EPERM`](crate::error::code::EPERM).
+    fn file_open(_file: &File) -> crate::error::Result {
+        Ok(())
+    }
+
+    /// Called when a new task (thread or process) is being created.
+    ///
+    /// `clone_flags` contains the `CLONE_*` flags passed to `clone(2)`.
+    ///
+    /// Return `Ok(())` to allow creation, or an `Err` to deny it.
+    fn task_alloc(_task: &Task, _clone_flags: u64) -> crate::error::Result {
+        Ok(())
+    }
+
+    /// Called when a task is being freed.
+    ///
+    /// This hook runs during task teardown.  **Must not sleep.**  Any
+    /// per-task state stored in a security blob should be cleaned up here.
+    fn task_free(_task: &Task) {}
+}
+
+/// C-callable adapter functions that bridge the LSM framework to [`Hooks`].
+///
+/// This type is never instantiated.  Its associated functions serve as the
+/// `unsafe extern "C" fn` pointers registered with the kernel's LSM
+/// static-call infrastructure.
+///
+/// # Invariants
+///
+/// Each adapter function is called only from the LSM framework in the
+/// appropriate hook context, with valid, non-null pointer arguments.
+#[doc(hidden)]
+pub struct Adapter<T: Hooks>(PhantomData<T>);
+
+impl<T: Hooks> Adapter<T> {
+    /// Adapter for the `file_open` LSM hook.
+    ///
+    /// # Safety
+    ///
+    /// `file` must be a valid, non-null pointer to a `struct file` that
+    /// remains valid for the duration of this call.  Called only by the
+    /// LSM framework.
+    pub unsafe extern "C" fn file_open(
+        file: *mut bindings::file,
+    ) -> core::ffi::c_int {
+        // SAFETY: The LSM framework guarantees `file` is valid and non-null
+        // for the duration of this call.
+        let file_ref = unsafe { File::from_raw_file(file.cast_const()) };
+        match T::file_open(file_ref) {
+            Ok(()) => 0,
+            Err(e) => e.to_errno(),
+        }
+    }
+
+    /// Adapter for the `task_alloc` LSM hook.
+    ///
+    /// # Safety
+    ///
+    /// `task` must be a valid, non-null pointer to a newly-allocated
+    /// `task_struct`.  `clone_flags` are the flags passed to `clone(2)`.
+    /// Called only by the LSM framework.
+    pub unsafe extern "C" fn task_alloc(
+        task: *mut bindings::task_struct,
+        clone_flags: u64,
+    ) -> core::ffi::c_int {
+        // SAFETY: The LSM framework guarantees `task` is valid, non-null,
+        // and exclusively owned by the framework for the duration of this
+        // call.  `Task` is `#[repr(transparent)]` over
+        // `Opaque<task_struct>`, which is `#[repr(transparent)]` over
+        // `UnsafeCell<MaybeUninit<task_struct>>`, giving the same layout.
+        let task_ref = unsafe { &*(task as *const Task) };
+        match T::task_alloc(task_ref, clone_flags) {
+            Ok(()) => 0,
+            Err(e) => e.to_errno(),
+        }
+    }
+
+    /// Adapter for the `task_free` LSM hook.
+    ///
+    /// # Safety
+    ///
+    /// `task` must be a valid, non-null pointer to a `task_struct` that
+    /// is being freed.  Must not sleep.  Called only by the LSM framework.
+    pub unsafe extern "C" fn task_free(task: *mut bindings::task_struct) {
+        // SAFETY: Same layout argument as `task_alloc`.
+        let task_ref = unsafe { &*(task as *const Task) };
+        T::task_free(task_ref);
+    }
+}
+
+/// Constructs a [`bindings::lsm_info`] value for use by [`define_lsm!`].
+///
+/// All fields not explicitly set are zero-initialised, which the kernel
+/// interprets as:
+/// - `order`: `LSM_ORDER_MUTABLE` (0)
+/// - `flags`: none
+/// - `blobs`: no security blobs requested
+/// - `enabled`: use the default (always enabled)
+/// - `initcall_*`: no additional initcalls
+///
+/// # Safety
+///
+/// - `id` must point to a `lsm_id` with `'static` lifetime.
+/// - `init` must be a valid init function called once during `security_init()`.
+#[doc(hidden)]
+pub const unsafe fn new_lsm_info(
+    id: *const bindings::lsm_id,
+    init: Option<unsafe extern "C" fn() -> core::ffi::c_int>,
+) -> LsmInfo {
+    // SAFETY: `bindings::lsm_info` is a C struct.  Zero-initialisation
+    // gives valid zero/null values for every optional field.  The caller
+    // is responsible for providing a valid `id` and `init`.
+    let mut info: bindings::lsm_info =
+        unsafe { MaybeUninit::zeroed().assume_init() };
+    info.id = id;
+    info.init = init;
+    LsmInfo(info)
+}
+
+/// Registers a Rust LSM implementation with the kernel at boot time.
+///
+/// # Usage
+///
+/// ```no_run
+/// kernel::define_lsm!(MyLsmType, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);
+/// ```
+///
+/// The macro generates:
+/// - A `static lsm_id` identifying this LSM to the kernel.
+/// - A static array of `security_hook_list` entries (initialised via C shims
+///   to avoid `__randomize_layout` pitfalls).
+/// - An `unsafe extern "C"` init function that populates the hook list and
+///   calls `security_add_hooks()`.
+/// - A `static lsm_info` placed in the `.lsm_info.init` linker section so
+///   the kernel discovers and calls the init function during `security_init()`.
+///
+/// # Limitations (v1)
+///
+/// - Only the `file_open`, `task_alloc`, and `task_free` hooks are registered.
+///   Additional hooks will be added in follow-up patches as safe Rust wrappers
+///   for the argument types are contributed upstream.
+/// - At most one Rust LSM can be registered per kernel build.  Unique name
+///   generation for multiple LSMs requires a proc-macro (planned for v2).
+#[macro_export]
+macro_rules! define_lsm {
+    ($T:ty, $name:expr, $id:expr) => {
+        mod __rust_lsm_registration {
+            use super::*;
+
+            // The LSM identity — must be 'static because the static-call
+            // table holds a back-pointer to it via security_hook_list.lsmid.
+            // LsmId wraps lsm_id with `unsafe impl Sync` so it can be `static`.
+            static __LSM_ID: $crate::lsm::LsmId =
+                $crate::lsm::LsmId($crate::bindings::lsm_id {
+                    name: $name.as_ptr().cast(),
+                    id: $id,
+                });
+
+            // The hook list array.  MaybeUninit avoids needing security_hook_list
+            // to implement a const-initialiser; the C shims fill each slot in
+            // __lsm_init() below before any hook can fire.
+            //
+            // SAFETY: This array must be 'static — the static-call table holds
+            // back-pointers (scall->hl) into it after registration.
+            static mut __LSM_HOOKS: [
+                ::core::mem::MaybeUninit<$crate::bindings::security_hook_list>;
+                3
+            ] = [::core::mem::MaybeUninit::zeroed(); 3];
+
+            // The init function stored in lsm_info.init.  Called once by
+            // security_init() in single-threaded boot context.
+            unsafe extern "C" fn __lsm_init() -> ::core::ffi::c_int {
+                // SAFETY: Called once, single-threaded, during security_init().
+                // __LSM_HOOKS is exclusively owned here.
+                unsafe {
+                    // bindgen strips the rust_helper_ prefix from helper names.
+                    $crate::bindings::lsm_hook_init_file_open(
+                        __LSM_HOOKS[0].as_mut_ptr(),
+                        Some($crate::lsm::Adapter::<$T>::file_open),
+                    );
+                    $crate::bindings::lsm_hook_init_task_alloc(
+                        __LSM_HOOKS[1].as_mut_ptr(),
+                        Some($crate::lsm::Adapter::<$T>::task_alloc),
+                    );
+                    $crate::bindings::lsm_hook_init_task_free(
+                        __LSM_HOOKS[2].as_mut_ptr(),
+                        Some($crate::lsm::Adapter::<$T>::task_free),
+                    );
+                    $crate::bindings::security_add_hooks(
+                        __LSM_HOOKS[0].as_mut_ptr(),
+                        __LSM_HOOKS.len() as ::core::ffi::c_int,
+                        &raw const __LSM_ID.0,
+                    );
+                }
+                0
+            }
+
+            // The lsm_info descriptor placed in the .lsm_info.init ELF section.
+            // The kernel's security_init() iterates this section to discover and
+            // call the init function of every compiled-in LSM.
+            // LsmInfo wraps lsm_info with `unsafe impl Sync` so it can be `static`.
+            #[used]
+            #[link_section = ".lsm_info.init"]
+            static __LSM_INFO: $crate::lsm::LsmInfo = {
+                // SAFETY: __LSM_ID and __lsm_init have 'static lifetime.
+                unsafe {
+                    $crate::lsm::new_lsm_info(
+                        &raw const __LSM_ID.0,
+                        Some(__lsm_init),
+                    )
+                }
+            };
+        }
+    };
+}
-- 
2.53.0


^ permalink raw reply related

* [PATCH 4/5] security: add Rust LSM sample (CONFIG_SECURITY_RUST_LSM)
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311045601.649904-1-jamie@matrixforgelabs.com>

Add a minimal reference LSM written in Rust using the kernel::lsm
abstraction layer introduced in the preceding patch.  The module:

- Implements all three v1 hooks (file_open, task_alloc, task_free).
- Logs each invocation via pr_info() and returns 0 (allow) for every
  operation.  It enforces no policy.

Purposes:
1. Compile-test vehicle for kernel::lsm.
2. API demonstration: shows exactly what an LSM author writes.
3. Boot-test reference: if /sys/kernel/security/lsm lists
   "rust_lsm_sample" after boot, hook registration works end-to-end.

LSM_ID_UNDEF is used as the identity constant; a permanent LSM_ID_*
value from include/uapi/linux/lsm.h will be requested as part of the
upstream patch series.

Activated via lsm= kernel command-line parameter or CONFIG_LSM Kconfig
string.  Requires CONFIG_SECURITYFS=y for /sys/kernel/security/lsm to
be visible.

Compiled and boot-tested on Linux 7.0-rc2 (commit 4ae12d8b).

Assisted-by: Claude:claude-sonnet-4-6
---
 security/Kconfig              |  2 ++
 security/Makefile             |  1 +
 security/rust_lsm/Kconfig     | 14 ++++++++
 security/rust_lsm/Makefile    |  2 ++
 security/rust_lsm/rust_lsm.rs | 66 +++++++++++++++++++++++++++++++++++
 5 files changed, 85 insertions(+)
 create mode 100644 security/rust_lsm/Kconfig
 create mode 100644 security/rust_lsm/Makefile
 create mode 100644 security/rust_lsm/rust_lsm.rs

diff --git a/security/Kconfig b/security/Kconfig
index 6a4393fce9a1..fbd1ad4d36e8 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -301,6 +301,8 @@ config SECURITY_COMMONCAP_KUNIT_TEST
 
 	  If unsure, say N.
 
+source "security/rust_lsm/Kconfig"
+
 source "security/Kconfig.hardening"
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 4601230ba442..f04f10a1592f 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
 obj-$(CONFIG_SECURITY_IPE)		+= ipe/
+obj-$(CONFIG_SECURITY_RUST_LSM)		+= rust_lsm/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
diff --git a/security/rust_lsm/Kconfig b/security/rust_lsm/Kconfig
new file mode 100644
index 000000000000..e2c3c45b9f7f
--- /dev/null
+++ b/security/rust_lsm/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+config SECURITY_RUST_LSM
+	bool "Rust LSM sample"
+	depends on SECURITY && RUST
+	help
+	  A minimal Linux Security Module written in Rust that demonstrates
+	  the kernel::lsm abstractions.  It logs file_open, task_alloc, and
+	  task_free events via pr_info().
+
+	  This module serves as a reference implementation and compile-test
+	  vehicle for the Rust LSM abstraction layer.  It imposes no policy
+	  — all hook implementations return 0 (allow) after logging.
+
+	  If unsure, say N.
diff --git a/security/rust_lsm/Makefile b/security/rust_lsm/Makefile
new file mode 100644
index 000000000000..26a2319da08e
--- /dev/null
+++ b/security/rust_lsm/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SECURITY_RUST_LSM) += rust_lsm.o
diff --git a/security/rust_lsm/rust_lsm.rs b/security/rust_lsm/rust_lsm.rs
new file mode 100644
index 000000000000..3afba383ef65
--- /dev/null
+++ b/security/rust_lsm/rust_lsm.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust LSM sample — reference implementation of the kernel::lsm abstractions.
+//!
+//! This module demonstrates how a Linux Security Module is written in Rust.
+//! It registers three hooks (file_open, task_alloc, task_free), logs each
+//! invocation via pr_info!(), and allows every operation.
+//!
+//! It is not a policy module — it enforces nothing.  Its purpose is to:
+//!
+//! 1. Serve as a compile-test vehicle for the kernel::lsm abstraction layer.
+//! 2. Demonstrate the API surface that upstream-bound LSMs should target.
+//! 3. Provide a boot-test reference: if the kernel boots with this LSM
+//!    enabled and /sys/kernel/security/lsm lists "rust_lsm_sample", the
+//!    hook registration machinery is working end-to-end.
+//!
+//! Assisted-by: Claude:claude-sonnet-4-6
+
+// The build system injects #![no_std] for all kernel Rust objects; do not repeat it.
+
+// Required by pr_info! and other kernel logging macros.
+const __LOG_PREFIX: &[u8] = b"rust_lsm_sample\0";
+
+use kernel::bindings;
+use kernel::lsm;
+use kernel::prelude::*;
+
+/// The Rust LSM sample implementation.
+struct RustLsmSample;
+
+impl lsm::Hooks for RustLsmSample {
+    fn file_open(file: &kernel::fs::File) -> Result {
+        pr_info!("rust_lsm: file_open flags={:#x}\n", file.flags());
+        Ok(())
+    }
+
+    fn task_alloc(task: &kernel::task::Task, clone_flags: u64) -> Result {
+        pr_info!(
+            "rust_lsm: task_alloc pid={} clone_flags={:#x}\n",
+            task.pid(),
+            clone_flags
+        );
+        Ok(())
+    }
+
+    fn task_free(task: &kernel::task::Task) {
+        pr_info!("rust_lsm: task_free pid={}\n", task.pid());
+    }
+}
+
+// Register RustLsmSample with the kernel LSM framework.
+//
+// This macro generates:
+//   - A static `lsm_id` identifying this module as "rust_lsm_sample".
+//   - A static `security_hook_list[3]` array (filled by C shims via LSM_HOOK_INIT).
+//   - An `unsafe extern "C"` init function that calls security_add_hooks().
+//   - A static `lsm_info` in the `.lsm_info.init` ELF section so that
+//     security_init() discovers and calls the init function at boot.
+//
+// LSM_ID_UNDEF is used during development.  A permanent LSM_ID_* value from
+// include/uapi/linux/lsm.h will be requested as part of the upstream patch series.
+kernel::define_lsm!(
+    RustLsmSample,
+    "rust_lsm_sample\0",
+    bindings::LSM_ID_UNDEF as u64
+);
-- 
2.53.0


^ permalink raw reply related

* [PATCH 5/5] Documentation: rust: add LSM abstraction guide; update MAINTAINERS
From: Jamie Lindsey @ 2026-03-11  4:56 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311045601.649904-1-jamie@matrixforgelabs.com>

Add Documentation/rust/lsm.rst, a developer guide for writing Linux
Security Modules in Rust using the kernel::lsm abstraction layer.

Covers:
- The Hooks trait: per-hook semantics, return values, sleeping rules.
- The define_lsm! macro: arguments and what it generates.
- Kconfig and Makefile wiring patterns.
- lsm_count.h: how to update MAX_LSM_COUNT for new built-in LSMs.
- A complete minimal example.
- Boot activation via lsm= and CONFIG_LSM.
- v1 limitations (three hooks, one Rust LSM per build, no blob support).
- C headers of interest for LSM authors.

Link the new document from Documentation/rust/index.rst.

Add a MAINTAINERS entry (RUST LSM ABSTRACTIONS) covering:
  rust/helpers/lsm.c, rust/kernel/lsm.rs, security/rust_lsm/
Listed on both rust-for-linux@vger.kernel.org and
linux-security-module@vger.kernel.org.

Assisted-by: Claude:claude-sonnet-4-6
---
 Documentation/rust/index.rst |   1 +
 Documentation/rust/lsm.rst   | 246 +++++++++++++++++++++++++++++++++++
 MAINTAINERS                  |   9 ++
 3 files changed, 256 insertions(+)
 create mode 100644 Documentation/rust/lsm.rst

diff --git a/Documentation/rust/index.rst b/Documentation/rust/index.rst
index b78ed0efa784..a4cb4bf8faf2 100644
--- a/Documentation/rust/index.rst
+++ b/Documentation/rust/index.rst
@@ -37,6 +37,7 @@ more details.
     coding-guidelines
     arch-support
     testing
+    lsm
 
 You can also find learning materials for Rust in its section in
 :doc:`../process/kernel-docs`.
diff --git a/Documentation/rust/lsm.rst b/Documentation/rust/lsm.rst
new file mode 100644
index 000000000000..c4e16e7d2be5
--- /dev/null
+++ b/Documentation/rust/lsm.rst
@@ -0,0 +1,246 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Linux Security Modules in Rust
+================================
+
+This document describes how to write a Linux Security Module (LSM) using
+the Rust abstractions provided in ``rust/kernel/lsm.rs``.
+
+
+Overview
+--------
+
+The LSM framework allows security policies to be implemented as kernel
+modules that register hooks into security-sensitive operations.  Traditionally
+these are written in C; the Rust abstraction layer provides a safe,
+trait-based interface that enforces correct hook registration at compile time.
+
+A Rust LSM:
+
+1. Implements the :ref:`Hooks trait <rust_lsm_hooks_trait>` for the subset
+   of hooks it cares about.
+2. Registers itself at boot time using the :ref:`define_lsm! macro
+   <rust_lsm_define_macro>`.
+
+No C code is required by the LSM author.  The abstraction layer generates
+the necessary C-callable adapter functions and places the ``lsm_info``
+descriptor in the ``.lsm_info.init`` ELF section, where
+``security_init()`` discovers it during boot.
+
+
+.. _rust_lsm_hooks_trait:
+
+The ``Hooks`` trait
+-------------------
+
+``kernel::lsm::Hooks`` is the central trait.  Each method corresponds to one
+LSM hook.  All methods have a default no-op implementation, so an implementor
+only needs to override the hooks it cares about::
+
+    pub trait Hooks: Sync + Send + 'static {
+        fn file_open(_file: &File) -> Result { Ok(()) }
+        fn task_alloc(_task: &Task, _clone_flags: u64) -> Result { Ok(()) }
+        fn task_free(_task: &Task) {}
+    }
+
+The ``Sync + Send + 'static`` bounds are required because hook functions may
+be called concurrently from any CPU.
+
+Return values
+~~~~~~~~~~~~~
+
+Hooks that can deny an operation return ``kernel::error::Result``:
+
+- ``Ok(())`` — allow the operation.
+- ``Err(e)`` — deny the operation; ``e.to_errno()`` is returned to the caller.
+
+Common denial error codes:
+
+- ``EACCES`` — permission denied (policy decision).
+- ``EPERM`` — operation not permitted (capability check).
+
+The ``task_free`` hook cannot deny anything and has no return value.
+
+Sleeping
+~~~~~~~~
+
+``task_free`` is called in a non-sleeping context.  Implementations **must
+not** sleep or allocate memory with ``GFP_KERNEL`` inside ``task_free``.
+All other hooks may sleep unless the call site is documented otherwise.
+
+
+Available hooks (v1)
+~~~~~~~~~~~~~~~~~~~~~
+
+.. list-table::
+   :widths: 25 75
+   :header-rows: 1
+
+   * - Hook
+     - When called
+   * - ``file_open``
+     - A file is being opened.  Return ``Err`` to deny the open.
+   * - ``task_alloc``
+     - A new task (thread or process) is being created via ``clone(2)``
+       or ``fork(2)``.  ``clone_flags`` contains the ``CLONE_*`` flags.
+       Return ``Err`` to deny creation.
+   * - ``task_free``
+     - A task is being freed.  Must not sleep.  Use this to clean up any
+       per-task state associated with the LSM.
+
+
+.. _rust_lsm_define_macro:
+
+The ``define_lsm!`` macro
+--------------------------
+
+``kernel::define_lsm!`` registers a type implementing ``Hooks`` with the
+kernel LSM framework::
+
+    kernel::define_lsm!(MyLsmType, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);
+
+Arguments:
+
+.. list-table::
+   :widths: 20 80
+   :header-rows: 1
+
+   * - Argument
+     - Description
+   * - ``$T``
+     - The type implementing ``Hooks``.  Must be ``Sync + Send + 'static``.
+   * - ``$name``
+     - A ``&str`` literal with a NUL terminator (``\0``).  This name is used
+       in ``/sys/kernel/security/lsm`` and in the ``lsm=`` kernel command-line
+       parameter.
+   * - ``$id``
+     - The LSM identity constant from ``include/uapi/linux/lsm.h``.  Use
+       ``bindings::LSM_ID_UNDEF as u64`` during development; request a
+       permanent value as part of the upstream patch series.
+
+The macro generates:
+
+- A ``static __LSM_ID: LsmId`` holding the LSM identity.
+- A ``static mut __LSM_HOOKS`` array of ``MaybeUninit<security_hook_list>``
+  entries, one per registered hook.
+- An ``unsafe extern "C" fn __lsm_init()`` that fills each hook slot using
+  the C ``LSM_HOOK_INIT`` shim (necessary because ``security_hook_list`` has
+  ``__randomize_layout``) and calls ``security_add_hooks()``.
+- A ``static __LSM_INFO: LsmInfo`` in the ``.lsm_info.init`` ELF section,
+  which ``security_init()`` iterates at boot to discover and call the init
+  function.
+
+
+Enabling in Kconfig
+-------------------
+
+To include a Rust LSM in the build, add a ``Kconfig`` entry that selects
+``RUST`` and depends on ``SECURITY``::
+
+    config SECURITY_MY_LSM
+        bool "My Rust LSM"
+        depends on SECURITY
+        select RUST
+        help
+          A minimal Rust LSM that ...
+
+Wire the object into the build in ``security/Makefile``::
+
+    obj-$(CONFIG_SECURITY_MY_LSM) += my_lsm/
+
+And in ``security/my_lsm/Makefile``::
+
+    obj-$(CONFIG_SECURITY_MY_LSM) += my_lsm.o
+
+The source file must **not** declare ``#![no_std]`` — the build system injects
+it automatically for all kernel Rust objects.
+
+Define ``__LOG_PREFIX`` at the top of the source file so that ``pr_info!`` and
+friends work correctly::
+
+    const __LOG_PREFIX: &[u8] = b"my_lsm\0";
+
+
+Minimal example
+---------------
+
+The following is a complete, minimal Rust LSM that logs every file open and
+allows all operations::
+
+    // SPDX-License-Identifier: GPL-2.0
+
+    //! Minimal Rust LSM example.
+
+    const __LOG_PREFIX: &[u8] = b"my_lsm\0";
+
+    use kernel::bindings;
+    use kernel::lsm;
+    use kernel::prelude::*;
+
+    struct MyLsm;
+
+    impl lsm::Hooks for MyLsm {
+        fn file_open(file: &kernel::fs::File) -> Result {
+            pr_info!("file_open: flags={:#x}\n", file.flags());
+            Ok(())
+        }
+    }
+
+    kernel::define_lsm!(MyLsm, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);
+
+The reference implementation is at ``security/rust_lsm/rust_lsm.rs``
+(``CONFIG_SECURITY_RUST_LSM``).
+
+
+Activating at boot
+------------------
+
+A Rust LSM compiled into the kernel is activated by listing its name in the
+``lsm=`` kernel command-line parameter or by adding it to the ``CONFIG_LSM``
+Kconfig string.
+
+The LSM name must also be counted in ``include/linux/lsm_count.h`` so that
+``MAX_LSM_COUNT`` is large enough to accommodate it alongside other compiled-in
+LSMs.  Add a ``CONFIG_SECURITY_MY_LSM``-guarded macro alongside the existing
+entries::
+
+    #ifdef CONFIG_SECURITY_MY_LSM
+    #define MY_LSM_ENABLED 1,
+    #else
+    #define MY_LSM_ENABLED
+    #endif
+
+    #define MAX_LSM_COUNT \
+        COUNT_LSMS( \
+            ...existing entries... \
+            MY_LSM_ENABLED)
+
+To verify the LSM is active after boot::
+
+    cat /sys/kernel/security/lsm
+
+
+Limitations (v1)
+-----------------
+
+- Only three hooks are available: ``file_open``, ``task_alloc``, and
+  ``task_free``.  Additional hooks will be added in follow-up patches as safe
+  Rust wrappers for their argument types are contributed upstream.
+
+- At most one Rust LSM can be registered per kernel build with the current
+  macro design.  Supporting multiple Rust LSMs requires unique static symbol
+  generation, planned for v2 using a proc-macro.
+
+- No security blob support.  Per-task or per-inode data associated with a
+  Rust LSM requires the ``SecurityBlob<T>`` abstraction planned for v2.
+
+
+C headers of interest
+---------------------
+
+- ``include/linux/lsm_hooks.h`` — ``lsm_info``, ``security_hook_list``,
+  ``LSM_HOOK_INIT``, ``LSM_ORDER_*``.
+- ``include/linux/lsm_hook_defs.h`` — canonical hook signatures.
+- ``include/uapi/linux/lsm.h`` — ``LSM_ID_*`` constants.
+- ``include/linux/lsm_count.h`` — ``MAX_LSM_COUNT`` computation.
+- ``security/security.c`` — ``security_init()``, ``security_add_hooks()``.
diff --git a/MAINTAINERS b/MAINTAINERS
index 89007f9ed35e..a23f5a15e038 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23196,6 +23196,15 @@ S:	Maintained
 T:	git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next
 F:	scripts/generate_rust_analyzer.py
 
+RUST LSM ABSTRACTIONS
+M:	Jamie Lindsey <jamie@matrixforgelabs.com>
+L:	rust-for-linux@vger.kernel.org
+L:	linux-security-module@vger.kernel.org
+S:	Maintained
+F:	rust/helpers/lsm.c
+F:	rust/kernel/lsm.rs
+F:	security/rust_lsm/
+
 RXRPC SOCKETS (AF_RXRPC)
 M:	David Howells <dhowells@redhat.com>
 M:	Marc Dionne <marc.dionne@auristor.com>
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH 57/61] reset: Prefer IS_ERR_OR_NULL over manual NULL check
From: Masami Hiramatsu @ 2026-03-11  4:59 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Philipp Zabel
In-Reply-To: <20260310-b4-is_err_or_null-v1-57-bd63b656022d@avm.de>

On Tue, 10 Mar 2026 12:49:23 +0100
Philipp Hahn <phahn-oss@avm.de> wrote:

> Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
> check.
> 
> Semantich change: Previously the code only printed the warning on error,
> but not when the pointer was NULL. Now the warning is printed in both
> cases!
> 
> Change found with coccinelle.
> 
> To: Philipp Zabel <p.zabel@pengutronix.de>
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Philipp Hahn <phahn-oss@avm.de>
> ---
>  drivers/reset/core.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index fceec45c8afc1e74fe46311bdc023ff257e8d770..649bb4ebabb20a09349ccbfc62f8280621df450e 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -715,7 +715,7 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_acquire);
>   */
>  void reset_control_release(struct reset_control *rstc)
>  {
> -	if (!rstc || WARN_ON(IS_ERR(rstc)))
> +	if (WARN_ON(IS_ERR_OR_NULL(rstc)))

This changes the behavior when rstc == NULL.
WARN_ON does not hit when rstc == NULL in the original code.

Thanks,

>  		return;
>  
>  	if (reset_control_is_array(rstc))
> 
> -- 
> 2.43.0
> 
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>

^ permalink raw reply

* [PATCH v2 0/5] rust: lsm: introduce safe Rust abstractions for the LSM framework
From: Jamie Lindsey @ 2026-03-11  5:09 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie

v2: add missing Signed-off-by tags, fix short commit hash in patch 4.
No code changes from v1.

This series introduces the first safe Rust abstractions for the Linux
Security Module (LSM) framework.  It allows a complete, policy-enforcing
LSM to be written entirely in Rust with no C boilerplate required from
the LSM author.

--- Motivation ---

The LSM framework is a natural target for Rust: hook registration is
unsafe by nature (raw function pointers, C ABI, __randomize_layout on
the hook list struct), and the trait system can enforce correct
implementation at compile time.

--- Design ---

The abstraction is trait-based:

  impl kernel::lsm::Hooks for MyLsm {
      fn file_open(file: &File) -> Result { ... }
  }
  kernel::define_lsm!(MyLsm, "my_lsm\0", bindings::LSM_ID_UNDEF as u64);

The define_lsm! macro generates all the plumbing: the lsm_id, the hook
array, the __init function that calls security_add_hooks(), and the
lsm_info descriptor placed in .lsm_info.init so that security_init()
discovers it at boot.

Hook implementations receive safe Rust wrappers (&File, &Task) and
return kernel::error::Result.  The C-callable adapter functions are
monomorphised per LSM type -- no vtable, no runtime dispatch.

--- Implementation notes for reviewers ---

static mut __LSM_HOOKS:
  The hook array is static mut because it is written exactly once by
  __lsm_init() in the single-threaded boot context, before
  security_add_hooks() copies the entries into the static-call table.
  No access occurs after that point.  UnsafeCell or a lock would add
  overhead with no safety benefit in this strictly init-only path.

unsafe impl Sync on LsmId / LsmInfo:
  Both types are #[repr(transparent)] wrappers around lsm_id / lsm_info,
  which contain *const c_char pointers to 'static NUL-terminated string
  literals that are never mutated.  The Sync impl is sound for the same
  reason that &'static str is Sync.

MaybeUninit hook array:
  security_hook_list carries __randomize_layout.  Rust code cannot safely
  initialise it by field name (the field order after randomisation differs
  from what bindgen sees).  The array is left MaybeUninit and filled at
  runtime by C shims that call LSM_HOOK_INIT() -- the macro the kernel
  already uses for this purpose in C LSMs.

Function pointer types:
  All three hook signatures were verified against union security_list_options
  generated from lsm_hook_defs.h in this exact tree.  file_open:
  int(*)(struct file *); task_alloc: int(*)(struct task_struct *, u64)
  (u64, not unsigned long -- tree-specific); task_free:
  void(*)(struct task_struct *).  All match the C shim parameters exactly.

checkpatch note:
  checkpatch reports "Non-standard signature: Assisted-by" and
  "Unrecognized email address: Claude:claude-sonnet-4-6".  The
  Assisted-by tag and its format are defined in
  Documentation/process/coding-assistants.rst; checkpatch has not yet
  been updated to recognise it.  This is a false positive.

--- Limitations (v1) ---

- Three hooks: file_open, task_alloc, task_free.  Additional hooks will
  be added in follow-up patches as safe Rust wrappers for their argument
  types are contributed upstream.
- At most one Rust LSM per kernel build (unique static symbol names
  require a proc-macro; planned for v2).
- No security blob support (SecurityBlob<T> planned for v2).

--- Testing ---

Compiled and boot-tested on Linux 7.0-rc2
(commit 4ae12d8bd9a8 "Merge tag 'kbuild-fixes-7.0-2' of
git://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux").

Boot verified: with lsm=rust_lsm_sample on the kernel command line,
/sys/kernel/security/lsm reports "capability,rust_lsm_sample" and
task_alloc hook invocations appear in dmesg.

--- Patch overview ---

  [1/5] rust: bindings: include lsm_hooks.h -- expose LSM types to bindgen
  [2/5] rust: helpers: C shims for LSM_HOOK_INIT and security_add_hooks
  [3/5] rust: kernel: lsm -- Hooks trait, Adapter<T>, define_lsm! macro
  [4/5] security: rust_lsm_sample -- reference implementation and boot test
  [5/5] Documentation: rust: lsm abstraction developer guide; MAINTAINERS

Jamie Lindsey (5):
  rust: bindings: include lsm_hooks.h to expose LSM types to bindgen
  rust: helpers: add C shims for LSM hook initialisation
  rust: kernel: add LSM abstraction layer
  security: add Rust LSM sample (CONFIG_SECURITY_RUST_LSM)
  Documentation: rust: add LSM abstraction guide; update MAINTAINERS

 Documentation/rust/index.rst    |   1 +
 Documentation/rust/lsm.rst      | 246 ++++++++++++++++++++++++++
 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/lsm.c              |  49 ++++++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/lsm.rs              | 295 ++++++++++++++++++++++++++++++++
 security/Kconfig                |   2 +
 security/Makefile               |   1 +
 security/rust_lsm/Kconfig       |  14 ++
 security/rust_lsm/Makefile      |   2 +
 security/rust_lsm/rust_lsm.rs   |  66 +++++++
 13 files changed, 689 insertions(+)
 create mode 100644 Documentation/rust/lsm.rst
 create mode 100644 rust/helpers/lsm.c
 create mode 100644 rust/kernel/lsm.rs
 create mode 100644 security/rust_lsm/Kconfig
 create mode 100644 security/rust_lsm/Makefile
 create mode 100644 security/rust_lsm/rust_lsm.rs

-- 
2.53.0


^ permalink raw reply

* [PATCH v2 1/5] rust: bindings: include lsm_hooks.h to expose LSM types to bindgen
From: Jamie Lindsey @ 2026-03-11  5:09 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311050854.657422-1-jamie@matrixforgelabs.com>

Add lsm_hooks.h to bindings_helper.h so that bindgen generates Rust
bindings for lsm_id, lsm_info, security_hook_list, and LSM_ID_*
constants.  These are required by the Rust LSM abstraction layer
introduced in subsequent patches.

Placed alphabetically between jump_label.h and mdio.h.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Jamie Lindsey <jamie@matrixforgelabs.com>
---
 rust/bindings/bindings_helper.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..b819592868e3 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -63,6 +63,7 @@
 #include <linux/ioport.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
+#include <linux/lsm_hooks.h>
 #include <linux/mdio.h>
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 2/5] rust: helpers: add C shims for LSM hook initialisation
From: Jamie Lindsey @ 2026-03-11  5:09 UTC (permalink / raw)
  To: rust-for-linux, linux-security-module
  Cc: ojeda, paul, aliceryhl, jmorris, serge, jamie
In-Reply-To: <20260311050854.657422-1-jamie@matrixforgelabs.com>

struct security_hook_list carries __randomize_layout, making its fields
inaccessible to Rust code (bindgen sees the natural field order, which
may differ when CONFIG_RANDSTRUCT is active).  Add thin C wrapper
functions that call LSM_HOOK_INIT() — the existing C macro that handles
the randomised layout correctly — so that Rust can populate hook slots
without touching struct fields directly.

Three hook shims are provided (file_open, task_alloc, task_free) plus a
wrapper for security_add_hooks() which is __init and must carry that
annotation to ensure correct placement in the init text section.

Hook signatures verified against union security_list_options generated
from lsm_hook_defs.h: all three match exactly, including u64 for
task_alloc's clone_flags parameter (not unsigned long).

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Jamie Lindsey <jamie@matrixforgelabs.com>
---
 rust/helpers/helpers.c |  1 +
 rust/helpers/lsm.c     | 49 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 rust/helpers/lsm.c

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a..c496917be073 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -35,6 +35,7 @@
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
+#include "lsm.c"
 #include "maple_tree.c"
 #include "mm.c"
 #include "mutex.c"
diff --git a/rust/helpers/lsm.c b/rust/helpers/lsm.c
new file mode 100644
index 000000000000..fb475428fd0d
--- /dev/null
+++ b/rust/helpers/lsm.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/lsm_hooks.h>
+
+/*
+ * Hook list initializers.
+ *
+ * struct security_hook_list carries __randomize_layout, so its fields must
+ * never be set by Rust code directly (bindgen sees only the "natural" field
+ * order, which may differ when CONFIG_RANDSTRUCT is enabled).  These helpers
+ * wrap LSM_HOOK_INIT(), the C macro the kernel already uses for this purpose,
+ * so that Rust can safely obtain an initialised security_hook_list without
+ * ever touching the struct's fields directly.
+ */
+
+__rust_helper void
+rust_helper_lsm_hook_init_file_open(struct security_hook_list *hl,
+				    int (*fn)(struct file *))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(file_open, fn);
+}
+
+__rust_helper void
+rust_helper_lsm_hook_init_task_alloc(struct security_hook_list *hl,
+				     int (*fn)(struct task_struct *, u64))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(task_alloc, fn);
+}
+
+__rust_helper void
+rust_helper_lsm_hook_init_task_free(struct security_hook_list *hl,
+				    void (*fn)(struct task_struct *))
+{
+	*hl = (struct security_hook_list)LSM_HOOK_INIT(task_free, fn);
+}
+
+/*
+ * security_add_hooks() is __init — it installs callbacks into the static-call
+ * table and must only be called during kernel initialisation.  This wrapper
+ * keeps the __init annotation so the linker places it alongside the rest of
+ * the init text, and Rust callers invoke it only from their own __init path
+ * (the lsm_info.init callback).
+ */
+void __init
+rust_helper_security_add_hooks(struct security_hook_list *hooks, int count,
+			       const struct lsm_id *lsmid)
+{
+	security_add_hooks(hooks, count, lsmid);
+}
-- 
2.53.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox