[PATCH RFC 2/3] LSM: Enforce exclusive hooks

Paul Moore paul at paul-moore.com
Fri Apr 24 01:19:15 UTC 2026


On Feb 25, 2026 Casey Schaufler <casey at schaufler-ca.com> wrote:
> 
> If an LSM hook is marked as exclusive via LSM_FLAG_EXCLUSIVE
> in lsm_hook_defs.h it will not be added to the set of hooks to
> be executed if an different LSM has already registered an
> exclusive hook.
> 
> Signed-off-by: Casey Schaufler <casey at schaufler-ca.com>
> ---
>  include/linux/security.h |  2 ++
>  security/lsm_init.c      | 66 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 68 insertions(+)
> 
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 83a646d72f6f..e3c137a1b30a 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -2404,4 +2404,6 @@ static inline void security_initramfs_populated(void)
>  }
>  #endif /* CONFIG_SECURITY */
>  
> +extern u64 lsm_exclusive_hooks;

We already have the 'lsm_exclusive' variable in lsm_init.c, don't create
another variable that does the same thing.  If the scope of the existing
variable isn't what you need, change that.

Although to be honest, I'm not in love with what you're doing with the
variable anyway, more on that later in the review.

>  #endif /* ! __LINUX_SECURITY_H */
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 05bd52e6b1f2..dc3c84387a7e 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -356,6 +356,70 @@ static int __init lsm_static_call_init(struct security_hook_list *hl)
>  	return -ENOSPC;
>  }
>  
> +/*
> + * Hooks that are restricted to use by a single security module.
> + *
> + * Secmark hooks have not been converted from secids to lsm_props
> + * due to space limitations in packet headers.

If this is a general purpose mechanism for all types of LSM hooks, please
don't put commentary in here about a single class of hook.

If this only reason for doing all of this is for secmark, just fix
secmark instead of going through all of this trouble.

> + * Conversions from a secid to a secctx are restricted to the
> + * single security module. All cases where there may be multiple
> + * modules providing the input data have been converted to use
> + * a lsm_prop instead of a secid.

Okay, yes, the paragraph above is true, I'm just not sure why it is
important here?

> + */
> +struct lsm_exclusive {
> +	struct lsm_static_call *name;
> +	char *namestr;
> +	u32 flags;
> +};
> +
> +static __initdata struct lsm_exclusive lsm_exclusive_set[] = {
> +#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...)	\
> +	{ .name = static_calls_table.NAME, .flags = FLAGS, .namestr = "" #NAME "" , },
> +#include <linux/lsm_hook_defs.h>
> +#undef LSM_HOOK
> +};
> +u64 lsm_exclusive_hooks;
> +EXPORT_SYMBOL(lsm_exclusive_hooks);

Unless I missed something, we really shouldn't need to export this, why
did you need EXPORT_SYMBOL() here?

> +/**
> + * lsm_exclusive_hook_denial - Check if exclusive hook is in use
> + * @hook: the hook to check
> + *
> + * Check if the hook in question is restricted to a single using LSM,
> + * and if the LSM providing single LSM hooks is defined.
> + *
> + * Returns true if the hook is exclusive and they are already provided,
> + * false otherwise.
> + */
> +static bool __init lsm_exclusive_hook_denial(struct security_hook_list *hook)
> +{
> +	int i;
> +
> +	if (lsm_exclusive_hooks == hook->lsmid->id)
> +		return false;
> +
> +	for (i = 0; i < ARRAY_SIZE(lsm_exclusive_set); i++) {
> +		if (!(lsm_exclusive_set[i].flags & LSM_FLAG_EXCLUSIVE))
> +			continue;

The logic on this looks a bit odd.  What is wrong with something like the
pseduo code below?

    for (i = 0; ARRAY_SIZE(lsm_hooks); i++) {
      if (lsm_hooks[i] == hook) {
        if (lsm_hooks[i].flags & LSM_FLAG_EXCLUSIVE)
          return true;
        else
          return false;
      }
    }

> +		if (hook->scalls == lsm_exclusive_set[i].name) {
> +			if (lsm_exclusive_hooks) {
> +				if (lsm_debug)
> +					lsm_pr("%s denied for %s.\n",
> +						lsm_exclusive_set[i].namestr,
> +						hook->lsmid->name);

The lsm_pr_dbg() macro exists for this very reason.

> +				return true;
> +			}
> +			if (lsm_debug)
> +				lsm_pr("Exclusive hooks limited to %s.\n",
> +					hook->lsmid->name);

Same as above.

> +			lsm_exclusive_hooks = hook->lsmid->id;
> +			break;
> +		}
> +	}
> +	return false;
> +}
>
>  /**
>   * security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists
>   * @hooks: LSM hooks to add
> @@ -371,6 +435,8 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
>  
>  	for (i = 0; i < count; i++) {
>  		hooks[i].lsmid = lsmid;
> +		if (lsm_exclusive_hook_denial(&hooks[i]))
> +			continue;
>  		if (lsm_static_call_init(&hooks[i]))
>  			panic("exhausted LSM callback slots with LSM %s\n",
>  			      lsmid->name);

I don't think we'd want to simply skip over a hook registration if the
LSM doesn't have access to an exclusive hook.  At the very least it risks
unexpected behavior in the LSM and at the worst it prevents the LSM from
properly enforcing it's security policy.  There is a reason we have the
panic() call in the existing code if we are not able to register a hook.

The simpliest solution here would be to panic, like we do today.

However, if you want to get fancy and enable LSMs to optionally adjust
their behavior to cope with the loss of a hook callback (I'm looking at
your patch 3/3 now), come up with a mechanism to report back to the LSM
that one or more of the callbacks were not registered.  One quick thought
would be to return a non-zero error code and add an indicator/bool/flag
to the security_hook_list that would be set by security_add_hooks().

--
paul-moore.com



More information about the Linux-security-module-archive mailing list