[PATCH RFC 2/3] LSM: Enforce exclusive hooks
Casey Schaufler
casey at schaufler-ca.com
Sat Apr 25 00:39:08 UTC 2026
On 4/23/2026 6:19 PM, Paul Moore wrote:
> 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.
Reusing lsm_exclusive is probably doable. For the RFC it made more
sense to make them separate.
> 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.
OKey Dokey.
> If this only reason for doing all of this is for secmark, just fix
> secmark instead of going through all of this trouble.
Secmark is the bellwether for using the mechanism.
>> + * 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?
It's important because it addresses the objection that the secmark
issue might be addressed by converting the secmark to a lsm_prop pointer.
I can certainly make that clearer.
>> + */
>> +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?
No, you didn't miss anything. It's a carryover from an early experiment.
>> +/**
>> + * 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?
This code isn't just looking to see if the hook is exclusive, it's looking
to see if the hook is exclusive and if the LSM requesting it has been given
the privilege to register exclusive hooks. This may not be the cleanest way
to go about it, so I will reconsider how best to do that.
> 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.
Indeed. I missed it here. Will correct.
>> + return true;
>> + }
>> + if (lsm_debug)
>> + lsm_pr("Exclusive hooks limited to %s.\n",
>> + hook->lsmid->name);
> Same as above.
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().
An LSM can easily check to see if it doesn't get exclusive hooks by looking
at lsm_exclusive_hooks after registration.
if (lsm_exclusive_hooks != LSM_ID_ME)
... do something (in)appropriate
More information about the Linux-security-module-archive
mailing list