* Re: [RFC PATCH v2 07/34] lsm: rename ordered_lsm_init() to lsm_init_ordered()
From: Casey Schaufler @ 2025-07-24 15:28 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-43-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> The new name more closely fits the rest of the naming scheme in
> security/lsm_init.c. This patch also adds a trivial comment block to
> the top of the function.
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> security/lsm_init.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 7e736c20458a..c8af26a6ce14 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -288,7 +288,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> kfree(sep);
> }
>
> -static void __init ordered_lsm_init(void)
> +/**
> + * lsm_init_ordered - Initialize the ordered LSMs
> + */
> +static void __init lsm_init_ordered(void)
> {
> unsigned int first = 0;
> struct lsm_info **lsm;
> @@ -336,9 +339,6 @@ static void __init ordered_lsm_init(void)
> init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
>
> - /*
> - * Create any kmem_caches needed for blobs
> - */
> if (blob_sizes.lbs_file)
> lsm_file_cache = kmem_cache_create("lsm_file_cache",
> blob_sizes.lbs_file, 0,
> @@ -492,7 +492,7 @@ int __init security_init(void)
> }
>
> /* Load LSMs in specified order. */
> - ordered_lsm_init();
> + lsm_init_ordered();
>
> return 0;
> }
^ permalink raw reply
* Re: [RFC PATCH v2 06/34] lsm: integrate lsm_early_cred() and lsm_early_task() into caller
From: Casey Schaufler @ 2025-07-24 15:20 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-42-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> With only one caller of lsm_early_cred() and lsm_early_task(), insert
> the functions' code directly into the caller and ger rid of the two
> functions.
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> security/lsm_init.c | 35 +++++------------------------------
> 1 file changed, 5 insertions(+), 30 deletions(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index d20c6df55e7d..7e736c20458a 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -288,34 +288,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> kfree(sep);
> }
>
> -/**
> - * lsm_early_cred - during initialization allocate a composite cred blob
> - * @cred: the cred that needs a blob
> - *
> - * Allocate the cred blob for all the modules
> - */
> -static void __init lsm_early_cred(struct cred *cred)
> -{
> - int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> -
> - if (rc)
> - panic("%s: Early cred alloc failed.\n", __func__);
> -}
> -
> -/**
> - * lsm_early_task - during initialization allocate a composite task blob
> - * @task: the task that needs a blob
> - *
> - * Allocate the task blob for all the modules
> - */
> -static void __init lsm_early_task(struct task_struct *task)
> -{
> - int rc = lsm_task_alloc(task);
> -
> - if (rc)
> - panic("%s: Early task alloc failed.\n", __func__);
> -}
> -
> static void __init ordered_lsm_init(void)
> {
> unsigned int first = 0;
> @@ -376,8 +348,11 @@ static void __init ordered_lsm_init(void)
> blob_sizes.lbs_inode, 0,
> SLAB_PANIC, NULL);
>
> - lsm_early_cred((struct cred *) current->cred);
> - lsm_early_task(current);
> + if (lsm_cred_alloc((struct cred *)current->cred, GFP_KERNEL))
> + panic("%s: early cred alloc failed.\n", __func__);
> + if (lsm_task_alloc(current))
> + panic("%s: early task alloc failed.\n", __func__);
> +
> lsm_order_for_each(lsm) {
> initialize_lsm(*lsm);
> }
^ permalink raw reply
* Re: [RFC PATCH v2 04/34] lsm: introduce looping macros for the initialization code
From: Casey Schaufler @ 2025-07-24 15:10 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-40-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> There are three common for loop patterns in the LSM initialization code
> to loop through the ordered LSM list and the registered "early" LSMs.
> This patch implements these loop patterns as macros to help simplify the
> code and reduce the change for errors.
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
I'm not a big fan of loop macros, but they seem useful here.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> security/lsm_init.c | 42 +++++++++++++++++++++++++++---------------
> 1 file changed, 27 insertions(+), 15 deletions(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 7beb028a507b..a73c3769dfea 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -32,6 +32,15 @@ static __initdata bool debug;
> pr_info(__VA_ARGS__); \
> } while (0)
>
> +#define lsm_order_for_each(iter) \
> + for ((iter) = ordered_lsms; *(iter); (iter)++)
> +#define lsm_for_each_raw(iter) \
> + for ((iter) = __start_lsm_info; \
> + (iter) < __end_lsm_info; (iter)++)
> +#define lsm_early_for_each_raw(iter) \
> + for ((iter) = __start_early_lsm_info; \
> + (iter) < __end_early_lsm_info; (iter)++)
> +
> static int lsm_append(const char *new, char **result);
>
> /* Save user chosen LSM */
> @@ -96,9 +105,10 @@ static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> {
> struct lsm_info **check;
>
> - for (check = ordered_lsms; *check; check++)
> + lsm_order_for_each(check) {
> if (*check == lsm)
> return true;
> + }
>
> return false;
> }
> @@ -206,7 +216,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> char *sep, *name, *next;
>
> /* LSM_ORDER_FIRST is always first. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + lsm_for_each_raw(lsm) {
> if (lsm->order == LSM_ORDER_FIRST)
> append_ordered_lsm(lsm, " first");
> }
> @@ -221,8 +231,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> * if the selected one was separately disabled: disable
> * all non-matching Legacy Major LSMs.
> */
> - for (major = __start_lsm_info; major < __end_lsm_info;
> - major++) {
> + lsm_for_each_raw(major) {
> if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> strcmp(major->name, chosen_major_lsm) != 0) {
> set_enabled(major, false);
> @@ -238,7 +247,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> while ((name = strsep(&next, ",")) != NULL) {
> bool found = false;
>
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + lsm_for_each_raw(lsm) {
> if (strcmp(lsm->name, name) == 0) {
> if (lsm->order == LSM_ORDER_MUTABLE)
> append_ordered_lsm(lsm, origin);
> @@ -253,7 +262,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
>
> /* Process "security=", if given. */
> if (chosen_major_lsm) {
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + lsm_for_each_raw(lsm) {
> if (exists_ordered_lsm(lsm))
> continue;
> if (strcmp(lsm->name, chosen_major_lsm) == 0)
> @@ -262,13 +271,13 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
> }
>
> /* LSM_ORDER_LAST is always last. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + lsm_for_each_raw(lsm) {
> if (lsm->order == LSM_ORDER_LAST)
> append_ordered_lsm(lsm, " last");
> }
>
> /* Disable all LSMs not in the ordered list. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + lsm_for_each_raw(lsm) {
> if (exists_ordered_lsm(lsm))
> continue;
> set_enabled(lsm, false);
> @@ -287,13 +296,14 @@ static void __init report_lsm_order(void)
> pr_info("initializing lsm=");
>
> /* Report each enabled LSM name, comma separated. */
> - for (early = __start_early_lsm_info;
> - early < __end_early_lsm_info; early++)
> + lsm_early_for_each_raw(early) {
> if (is_enabled(early))
> pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> + }
> + lsm_order_for_each(lsm) {
> if (is_enabled(*lsm))
> pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> + }
>
> pr_cont("\n");
> }
> @@ -340,8 +350,9 @@ static void __init ordered_lsm_init(void)
> } else
> ordered_lsm_parse(builtin_lsm_order, "builtin");
>
> - for (lsm = ordered_lsms; *lsm; lsm++)
> + lsm_order_for_each(lsm) {
> lsm_prepare(*lsm);
> + }
>
> report_lsm_order();
>
> @@ -376,8 +387,9 @@ static void __init ordered_lsm_init(void)
>
> lsm_early_cred((struct cred *) current->cred);
> lsm_early_task(current);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> + lsm_order_for_each(lsm) {
> initialize_lsm(*lsm);
> + }
> }
>
> static bool match_last_lsm(const char *list, const char *lsm)
> @@ -479,7 +491,7 @@ int __init early_security_init(void)
> {
> struct lsm_info *lsm;
>
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + lsm_early_for_each_raw(lsm) {
> if (!lsm->enabled)
> lsm->enabled = &lsm_enabled_true;
> lsm_prepare(lsm);
> @@ -506,7 +518,7 @@ int __init security_init(void)
> * Append the names of the early LSM modules now that kmalloc() is
> * available
> */
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + lsm_early_for_each_raw(lsm) {
> init_debug(" early started: %s (%s)\n", lsm->name,
> is_enabled(lsm) ? "enabled" : "disabled");
> if (lsm->enabled)
^ permalink raw reply
* Re: [RFC PATCH v2 01/34] lsm: split the notifier code out into lsm_notifier.c
From: Casey Schaufler @ 2025-07-24 14:49 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-37-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> In an effort to decompose security/security.c somewhat to make it less
> twisted and unwieldy, pull out the LSM notifier code into a new file
> as it is fairly well self-contained.
>
> No code changes.
>
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> security/Makefile | 2 +-
> security/lsm_notifier.c | 31 +++++++++++++++++++++++++++++++
> security/security.c | 23 -----------------------
> 3 files changed, 32 insertions(+), 24 deletions(-)
> create mode 100644 security/lsm_notifier.c
>
> diff --git a/security/Makefile b/security/Makefile
> index 22ff4c8bd8ce..14d87847bce8 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> # Object file lists
> -obj-$(CONFIG_SECURITY) += security.o
> +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o
> obj-$(CONFIG_SECURITYFS) += inode.o
> obj-$(CONFIG_SECURITY_SELINUX) += selinux/
> obj-$(CONFIG_SECURITY_SMACK) += smack/
> diff --git a/security/lsm_notifier.c b/security/lsm_notifier.c
> new file mode 100644
> index 000000000000..c92fad5d57d4
> --- /dev/null
> +++ b/security/lsm_notifier.c
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM notifier functions
> + *
> + */
> +
> +#include <linux/notifier.h>
> +#include <linux/security.h>
> +
> +static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
> +
> +int call_blocking_lsm_notifier(enum lsm_event event, void *data)
> +{
> + return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
> + event, data);
> +}
> +EXPORT_SYMBOL(call_blocking_lsm_notifier);
> +
> +int register_blocking_lsm_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
> + nb);
> +}
> +EXPORT_SYMBOL(register_blocking_lsm_notifier);
> +
> +int unregister_blocking_lsm_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
> + nb);
> +}
> +EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
> diff --git a/security/security.c b/security/security.c
> index fc8405928cc7..ea09a71d9767 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -90,8 +90,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
> [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
> };
>
> -static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
> -
> static struct kmem_cache *lsm_file_cache;
> static struct kmem_cache *lsm_inode_cache;
>
> @@ -643,27 +641,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
> }
> }
>
> -int call_blocking_lsm_notifier(enum lsm_event event, void *data)
> -{
> - return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
> - event, data);
> -}
> -EXPORT_SYMBOL(call_blocking_lsm_notifier);
> -
> -int register_blocking_lsm_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
> - nb);
> -}
> -EXPORT_SYMBOL(register_blocking_lsm_notifier);
> -
> -int unregister_blocking_lsm_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
> - nb);
> -}
> -EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
> -
> /**
> * lsm_blob_alloc - allocate a composite blob
> * @dest: the destination for the blob
^ permalink raw reply
* Re: [PATCH 0/2] Secure Boot lock down
From: Nicolas Bouchinet @ 2025-07-24 15:03 UTC (permalink / raw)
To: sergeh
Cc: Paul Moore, Hamza Mahfooz, Xiu Jianfeng, linux-kernel,
Ard Biesheuvel, James Morris, Serge E. Hallyn, Yue Haibing,
Tanya Agarwal, Kees Cook, linux-efi, linux-security-module
In-Reply-To: <aII_lcRmLr5n70ix@lei>
On Thu, Jul 24, 2025 at 02:13:41PM +0000, sergeh@kernel.org wrote:
> On Thu, Jul 24, 2025 at 02:59:39PM +0200, Nicolas Bouchinet wrote:
> > Hi Hamza, thanks for your patch.
> >
> > Thanks, Paul, for the forward.
> >
> > Sorry for the delay, we took a bit of time to do some lore archaeology
> > and discuss it with Xiu.
> >
> > As you might know, this has already been through debates in 2017 [1]. At
> > that time, the decision was not to merge this behavior.
> >
> > Distros have indeed carried downstream patches reflecting this behavior
> > for a long time and have been affected by vulnerabilities like
> > CVE-2025-1272 [2], which is caused by the magic sprinkled in
> > setup_arch().
> >
> > While your implementation looks cleaner to me. One of the points in
> > previous debates was to have a Lockdown side Kconfig knob to enable or
> > not this behavior. It would gate the registration of the Lockdown LSM to
> > the security_lock_kernel_down() hook.
>
> Well, but there is a default-n kconfig. What do you mean by "Lockdown
> side Kconfig knob"? I'm sure I'm missing something, but not sure
> what...
>
Sorry, if I have been unclear, I talk about something like a
"LOCK_DOWN_IF_SECURE_BOOT" config in `security/lockdown/Kconfig`.
In addition to the "EFI_KERNEL_LOCK_DOWN_IN_SECURE_BOOT" in
`drivers/firmware/efi/Kconfig`.
- "EFI_KERNEL_LOCK_DOWN_IN_SECURE_BOOT" would gate the call to the
`security_lock_kernel_down` hook and thus to any LSM registered to it.
- "LOCK_DOWN_IF_SECURE_BOOT" would gate the Lockdown LSM registration to
the `security_lock_kernel_down` hook.
Thanks,
Nicolas
^ permalink raw reply
* Re: [RFC PATCH v2 03/34] lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare()
From: Casey Schaufler @ 2025-07-24 14:52 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-39-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> Simplify and consolidate the lsm_allowed() and prepare_lsm() functions
> into a new function, lsm_prepare().
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> security/lsm_init.c | 103 ++++++++++++++++++--------------------------
> 1 file changed, 43 insertions(+), 60 deletions(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 70e7d4207dae..7beb028a507b 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -123,22 +123,6 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> is_enabled(lsm) ? "enabled" : "disabled");
> }
>
> -/* Is an LSM allowed to be initialized? */
> -static bool __init lsm_allowed(struct lsm_info *lsm)
> -{
> - /* Skip if the LSM is disabled. */
> - if (!is_enabled(lsm))
> - return false;
> -
> - /* Not allowed if another exclusive LSM already initialized. */
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> - init_debug("exclusive disabled: %s\n", lsm->name);
> - return false;
> - }
> -
> - return true;
> -}
> -
> static void __init lsm_set_blob_size(int *need, int *lbs)
> {
> int offset;
> @@ -151,51 +135,50 @@ static void __init lsm_set_blob_size(int *need, int *lbs)
> *need = offset;
> }
>
> -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> +/**
> + * lsm_prepare - Prepare the LSM framework for a new LSM
> + * @lsm: LSM definition
> + */
> +static void __init lsm_prepare(struct lsm_info *lsm)
> {
> - if (!needed)
> + struct lsm_blob_sizes *blobs;
> +
> + if (!is_enabled(lsm)) {
> + set_enabled(lsm, false);
> + return;
> + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> + init_debug("exclusive disabled: %s\n", lsm->name);
> + set_enabled(lsm, false);
> return;
> -
> - lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> - lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> - /*
> - * The inode blob gets an rcu_head in addition to
> - * what the modules might need.
> - */
> - if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> - blob_sizes.lbs_inode = sizeof(struct rcu_head);
> - lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> - lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> - lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> - lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> - lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_set_blob_size(&needed->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> -}
> -
> -/* Prepare LSM for initialization. */
> -static void __init prepare_lsm(struct lsm_info *lsm)
> -{
> - int enabled = lsm_allowed(lsm);
> -
> - /* Record enablement (to handle any following exclusive LSMs). */
> - set_enabled(lsm, enabled);
> -
> - /* If enabled, do pre-initialization work. */
> - if (enabled) {
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> - exclusive = lsm;
> - init_debug("exclusive chosen: %s\n", lsm->name);
> - }
> -
> - lsm_set_blob_sizes(lsm->blobs);
> }
> +
> + /* Mark the LSM as enabled. */
> + set_enabled(lsm, true);
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> + init_debug("exclusive chosen: %s\n", lsm->name);
> + exclusive = lsm;
> + }
> +
> + /* Register the LSM blob sizes. */
> + blobs = lsm->blobs;
> + lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file);
> + lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib);
> + /* inode blob gets an rcu_head in addition to LSM blobs. */
> + if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode);
> + lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
> + lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key);
> + lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> + lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event);
> + lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock);
> + lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock);
> + lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task);
> + lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> + lsm_set_blob_size(&blobs->lbs_xattr_count,
> + &blob_sizes.lbs_xattr_count);
> + lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
> }
>
> /* Initialize a given LSM, if it is enabled. */
> @@ -358,7 +341,7 @@ static void __init ordered_lsm_init(void)
> ordered_lsm_parse(builtin_lsm_order, "builtin");
>
> for (lsm = ordered_lsms; *lsm; lsm++)
> - prepare_lsm(*lsm);
> + lsm_prepare(*lsm);
>
> report_lsm_order();
>
> @@ -499,7 +482,7 @@ int __init early_security_init(void)
> for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> if (!lsm->enabled)
> lsm->enabled = &lsm_enabled_true;
> - prepare_lsm(lsm);
> + lsm_prepare(lsm);
> initialize_lsm(lsm);
> }
>
^ permalink raw reply
* Re: [RFC PATCH v2 02/34] lsm: split the init code out into lsm_init.c
From: Casey Schaufler @ 2025-07-24 14:50 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Mimi Zohar, Roberto Sassu, Fan Wu,
Mickaël Salaün, Günther Noack, Kees Cook,
Micah Morton, Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng,
Casey Schaufler
In-Reply-To: <20250721232142.77224-38-paul@paul-moore.com>
On 7/21/2025 4:21 PM, Paul Moore wrote:
> Continue to pull code out of security/security.c to help improve
> readability by pulling all of the LSM framework initialization
> code out into a new file.
>
> No code changes.
>
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> ---
> include/linux/lsm_hooks.h | 3 +-
> security/Makefile | 2 +-
> security/lsm.h | 22 ++
> security/lsm_init.c | 537 ++++++++++++++++++++++++++++++++++
> security/security.c | 591 +++-----------------------------------
> 5 files changed, 595 insertions(+), 560 deletions(-)
> create mode 100644 security/lsm.h
> create mode 100644 security/lsm_init.c
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 090d1d3e19fe..eeb4bfd60b79 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -167,11 +167,10 @@ struct lsm_info {
> __used __section(".early_lsm_info.init") \
> __aligned(sizeof(unsigned long))
>
> +
> /* DO NOT tamper with these variables outside of the LSM framework */
> extern char *lsm_names;
> extern struct lsm_static_calls_table static_calls_table __ro_after_init;
> -extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> -extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
>
> /**
> * lsm_get_xattr_slot - Return the next available slot and increment the index
> diff --git a/security/Makefile b/security/Makefile
> index 14d87847bce8..4601230ba442 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> # Object file lists
> -obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o
> +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o
> obj-$(CONFIG_SECURITYFS) += inode.o
> obj-$(CONFIG_SECURITY_SELINUX) += selinux/
> obj-$(CONFIG_SECURITY_SMACK) += smack/
> diff --git a/security/lsm.h b/security/lsm.h
> new file mode 100644
> index 000000000000..0e1731bad4a7
> --- /dev/null
> +++ b/security/lsm.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM functions
> + */
> +
> +#ifndef _LSM_H_
> +#define _LSM_H_
> +
> +#include <linux/lsm_hooks.h>
> +
> +/* LSM blob configuration */
> +extern struct lsm_blob_sizes blob_sizes;
> +
> +/* LSM blob caches */
> +extern struct kmem_cache *lsm_file_cache;
> +extern struct kmem_cache *lsm_inode_cache;
> +
> +/* LSM blob allocators */
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +int lsm_task_alloc(struct task_struct *task);
> +
> +#endif /* _LSM_H_ */
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> new file mode 100644
> index 000000000000..70e7d4207dae
> --- /dev/null
> +++ b/security/lsm_init.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM initialization functions
> + */
> +
> +#define pr_fmt(fmt) "LSM: " fmt
> +
> +#include <linux/init.h>
> +#include <linux/lsm_hooks.h>
> +
> +#include "lsm.h"
> +
> +char *lsm_names;
> +
> +/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
> +extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> +extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
> +
> +/* Boot-time LSM user choice */
> +static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +static __initdata const char *chosen_lsm_order;
> +static __initdata const char *chosen_major_lsm;
> +
> +/* Ordered list of LSMs to initialize. */
> +static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> +static __initdata struct lsm_info *exclusive;
> +
> +static __initdata bool debug;
> +#define init_debug(...) \
> + do { \
> + if (debug) \
> + pr_info(__VA_ARGS__); \
> + } while (0)
> +
> +static int lsm_append(const char *new, char **result);
> +
> +/* Save user chosen LSM */
> +static int __init choose_major_lsm(char *str)
> +{
> + chosen_major_lsm = str;
> + return 1;
> +}
> +__setup("security=", choose_major_lsm);
> +
> +/* Explicitly choose LSM initialization order. */
> +static int __init choose_lsm_order(char *str)
> +{
> + chosen_lsm_order = str;
> + return 1;
> +}
> +__setup("lsm=", choose_lsm_order);
> +
> +/* Enable LSM order debugging. */
> +static int __init enable_debug(char *str)
> +{
> + debug = true;
> + return 1;
> +}
> +__setup("lsm.debug", enable_debug);
> +
> +/* Mark an LSM's enabled flag. */
> +static int lsm_enabled_true __initdata = 1;
> +static int lsm_enabled_false __initdata = 0;
> +static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> +{
> + /*
> + * When an LSM hasn't configured an enable variable, we can use
> + * a hard-coded location for storing the default enabled state.
> + */
> + if (!lsm->enabled) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + else
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_true) {
> + if (!enabled)
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_false) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + } else {
> + *lsm->enabled = enabled;
> + }
> +}
> +
> +static inline bool is_enabled(struct lsm_info *lsm)
> +{
> + if (!lsm->enabled)
> + return false;
> +
> + return *lsm->enabled;
> +}
> +
> +/* Is an LSM already listed in the ordered LSMs list? */
> +static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> +{
> + struct lsm_info **check;
> +
> + for (check = ordered_lsms; *check; check++)
> + if (*check == lsm)
> + return true;
> +
> + return false;
> +}
> +
> +/* Append an LSM to the list of ordered LSMs to initialize. */
> +static int last_lsm __initdata;
> +static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> +{
> + /* Ignore duplicate selections. */
> + if (exists_ordered_lsm(lsm))
> + return;
> +
> + if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> + return;
> +
> + /* Enable this LSM, if it is not already set. */
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + ordered_lsms[last_lsm++] = lsm;
> +
> + init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> +}
> +
> +/* Is an LSM allowed to be initialized? */
> +static bool __init lsm_allowed(struct lsm_info *lsm)
> +{
> + /* Skip if the LSM is disabled. */
> + if (!is_enabled(lsm))
> + return false;
> +
> + /* Not allowed if another exclusive LSM already initialized. */
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> + init_debug("exclusive disabled: %s\n", lsm->name);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void __init lsm_set_blob_size(int *need, int *lbs)
> +{
> + int offset;
> +
> + if (*need <= 0)
> + return;
> +
> + offset = ALIGN(*lbs, sizeof(void *));
> + *lbs = offset + *need;
> + *need = offset;
> +}
> +
> +static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> +{
> + if (!needed)
> + return;
> +
> + lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> + lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> + /*
> + * The inode blob gets an rcu_head in addition to
> + * what the modules might need.
> + */
> + if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> + lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> + lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> + lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> + lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> + lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> + lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> + lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> + lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> + lsm_set_blob_size(&needed->lbs_xattr_count,
> + &blob_sizes.lbs_xattr_count);
> + lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> +}
> +
> +/* Prepare LSM for initialization. */
> +static void __init prepare_lsm(struct lsm_info *lsm)
> +{
> + int enabled = lsm_allowed(lsm);
> +
> + /* Record enablement (to handle any following exclusive LSMs). */
> + set_enabled(lsm, enabled);
> +
> + /* If enabled, do pre-initialization work. */
> + if (enabled) {
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> + exclusive = lsm;
> + init_debug("exclusive chosen: %s\n", lsm->name);
> + }
> +
> + lsm_set_blob_sizes(lsm->blobs);
> + }
> +}
> +
> +/* Initialize a given LSM, if it is enabled. */
> +static void __init initialize_lsm(struct lsm_info *lsm)
> +{
> + if (is_enabled(lsm)) {
> + int ret;
> +
> + init_debug("initializing %s\n", lsm->name);
> + ret = lsm->init();
> + WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> + }
> +}
> +
> +/*
> + * Current index to use while initializing the lsm id list.
> + */
> +u32 lsm_active_cnt __ro_after_init;
> +const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> +
> +/* Populate ordered LSMs list from comma-separated LSM name list. */
> +static void __init ordered_lsm_parse(const char *order, const char *origin)
> +{
> + struct lsm_info *lsm;
> + char *sep, *name, *next;
> +
> + /* LSM_ORDER_FIRST is always first. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_FIRST)
> + append_ordered_lsm(lsm, " first");
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + struct lsm_info *major;
> +
> + /*
> + * To match the original "security=" behavior, this
> + * explicitly does NOT fallback to another Legacy Major
> + * if the selected one was separately disabled: disable
> + * all non-matching Legacy Major LSMs.
> + */
> + for (major = __start_lsm_info; major < __end_lsm_info;
> + major++) {
> + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> + strcmp(major->name, chosen_major_lsm) != 0) {
> + set_enabled(major, false);
> + init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> + chosen_major_lsm, major->name);
> + }
> + }
> + }
> +
> + sep = kstrdup(order, GFP_KERNEL);
> + next = sep;
> + /* Walk the list, looking for matching LSMs. */
> + while ((name = strsep(&next, ",")) != NULL) {
> + bool found = false;
> +
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (strcmp(lsm->name, name) == 0) {
> + if (lsm->order == LSM_ORDER_MUTABLE)
> + append_ordered_lsm(lsm, origin);
> + found = true;
> + }
> + }
> +
> + if (!found)
> + init_debug("%s ignored: %s (not built into kernel)\n",
> + origin, name);
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + if (strcmp(lsm->name, chosen_major_lsm) == 0)
> + append_ordered_lsm(lsm, "security=");
> + }
> + }
> +
> + /* LSM_ORDER_LAST is always last. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_LAST)
> + append_ordered_lsm(lsm, " last");
> + }
> +
> + /* Disable all LSMs not in the ordered list. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + set_enabled(lsm, false);
> + init_debug("%s skipped: %s (not in requested order)\n",
> + origin, lsm->name);
> + }
> +
> + kfree(sep);
> +}
> +
> +static void __init report_lsm_order(void)
> +{
> + struct lsm_info **lsm, *early;
> + int first = 0;
> +
> + pr_info("initializing lsm=");
> +
> + /* Report each enabled LSM name, comma separated. */
> + for (early = __start_early_lsm_info;
> + early < __end_early_lsm_info; early++)
> + if (is_enabled(early))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + if (is_enabled(*lsm))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> +
> + pr_cont("\n");
> +}
> +
> +/**
> + * lsm_early_cred - during initialization allocate a composite cred blob
> + * @cred: the cred that needs a blob
> + *
> + * Allocate the cred blob for all the modules
> + */
> +static void __init lsm_early_cred(struct cred *cred)
> +{
> + int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> +
> + if (rc)
> + panic("%s: Early cred alloc failed.\n", __func__);
> +}
> +
> +/**
> + * lsm_early_task - during initialization allocate a composite task blob
> + * @task: the task that needs a blob
> + *
> + * Allocate the task blob for all the modules
> + */
> +static void __init lsm_early_task(struct task_struct *task)
> +{
> + int rc = lsm_task_alloc(task);
> +
> + if (rc)
> + panic("%s: Early task alloc failed.\n", __func__);
> +}
> +
> +static void __init ordered_lsm_init(void)
> +{
> + struct lsm_info **lsm;
> +
> + if (chosen_lsm_order) {
> + if (chosen_major_lsm) {
> + pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> + chosen_major_lsm, chosen_lsm_order);
> + chosen_major_lsm = NULL;
> + }
> + ordered_lsm_parse(chosen_lsm_order, "cmdline");
> + } else
> + ordered_lsm_parse(builtin_lsm_order, "builtin");
> +
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + prepare_lsm(*lsm);
> +
> + report_lsm_order();
> +
> + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> + init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> + init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> +#ifdef CONFIG_KEYS
> + init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> +#endif /* CONFIG_KEYS */
> + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> + init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> + init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> + init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> + init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> + init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> + init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> + init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> +
> + /*
> + * Create any kmem_caches needed for blobs
> + */
> + if (blob_sizes.lbs_file)
> + lsm_file_cache = kmem_cache_create("lsm_file_cache",
> + blob_sizes.lbs_file, 0,
> + SLAB_PANIC, NULL);
> + if (blob_sizes.lbs_inode)
> + lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> + blob_sizes.lbs_inode, 0,
> + SLAB_PANIC, NULL);
> +
> + lsm_early_cred((struct cred *) current->cred);
> + lsm_early_task(current);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + initialize_lsm(*lsm);
> +}
> +
> +static bool match_last_lsm(const char *list, const char *lsm)
> +{
> + const char *last;
> +
> + if (WARN_ON(!list || !lsm))
> + return false;
> + last = strrchr(list, ',');
> + if (last)
> + /* Pass the comma, strcmp() will check for '\0' */
> + last++;
> + else
> + last = list;
> + return !strcmp(last, lsm);
> +}
> +
> +static int lsm_append(const char *new, char **result)
> +{
> + char *cp;
> +
> + if (*result == NULL) {
> + *result = kstrdup(new, GFP_KERNEL);
> + if (*result == NULL)
> + return -ENOMEM;
> + } else {
> + /* Check if it is the last registered name */
> + if (match_last_lsm(*result, new))
> + return 0;
> + cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> + if (cp == NULL)
> + return -ENOMEM;
> + kfree(*result);
> + *result = cp;
> + }
> + return 0;
> +}
> +
> +static void __init lsm_static_call_init(struct security_hook_list *hl)
> +{
> + struct lsm_static_call *scall = hl->scalls;
> + int i;
> +
> + for (i = 0; i < MAX_LSM_COUNT; i++) {
> + /* Update the first static call that is not used yet */
> + if (!scall->hl) {
> + __static_call_update(scall->key, scall->trampoline,
> + hl->hook.lsm_func_addr);
> + scall->hl = hl;
> + static_branch_enable(scall->active);
> + return;
> + }
> + scall++;
> + }
> + panic("%s - Ran out of static slots.\n", __func__);
> +}
> +
> +/**
> + * security_add_hooks - Add a modules hooks to the hook lists.
> + * @hooks: the hooks to add
> + * @count: the number of hooks to add
> + * @lsmid: the identification information for the security module
> + *
> + * Each LSM has to register its hooks with the infrastructure.
> + */
> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
> + const struct lsm_id *lsmid)
> +{
> + int i;
> +
> + /*
> + * A security module may call security_add_hooks() more
> + * than once during initialization, and LSM initialization
> + * is serialized. Landlock is one such case.
> + * Look at the previous entry, if there is one, for duplication.
> + */
> + if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> + if (lsm_active_cnt >= MAX_LSM_COUNT)
> + panic("%s Too many LSMs registered.\n", __func__);
> + lsm_idlist[lsm_active_cnt++] = lsmid;
> + }
> +
> + for (i = 0; i < count; i++) {
> + hooks[i].lsmid = lsmid;
> + lsm_static_call_init(&hooks[i]);
> + }
> +
> + /*
> + * Don't try to append during early_security_init(), we'll come back
> + * and fix this up afterwards.
> + */
> + if (slab_is_available()) {
> + if (lsm_append(lsmid->name, &lsm_names) < 0)
> + panic("%s - Cannot get early memory.\n", __func__);
> + }
> +}
> +
> +int __init early_security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + prepare_lsm(lsm);
> + initialize_lsm(lsm);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * security_init - initializes the security framework
> + *
> + * This should be called early in the kernel initialization sequence.
> + */
> +int __init security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> + init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> + init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> +
> + /*
> + * Append the names of the early LSM modules now that kmalloc() is
> + * available
> + */
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + init_debug(" early started: %s (%s)\n", lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> + if (lsm->enabled)
> + lsm_append(lsm->name, &lsm_names);
> + }
> +
> + /* Load LSMs in specified order. */
> + ordered_lsm_init();
> +
> + return 0;
> +}
> diff --git a/security/security.c b/security/security.c
> index ea09a71d9767..e77791058086 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -32,24 +32,7 @@
> #include <net/flow.h>
> #include <net/sock.h>
>
> -#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
> -
> -/*
> - * Identifier for the LSM static calls.
> - * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> - * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> - */
> -#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
> -
> -/*
> - * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> - */
> -#define LSM_LOOP_UNROLL(M, ...) \
> -do { \
> - UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> -} while (0)
> -
> -#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
> +#include "lsm.h"
>
> /*
> * These are descriptions of the reasons that can be passed to the
> @@ -90,21 +73,29 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
> [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
> };
>
> -static struct kmem_cache *lsm_file_cache;
> -static struct kmem_cache *lsm_inode_cache;
> +struct lsm_blob_sizes blob_sizes;
>
> -char *lsm_names;
> -static struct lsm_blob_sizes blob_sizes __ro_after_init;
> +struct kmem_cache *lsm_file_cache;
> +struct kmem_cache *lsm_inode_cache;
>
> -/* Boot-time LSM user choice */
> -static __initdata const char *chosen_lsm_order;
> -static __initdata const char *chosen_major_lsm;
> +#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
>
> -static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +/*
> + * Identifier for the LSM static calls.
> + * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> + * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> + */
> +#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
>
> -/* Ordered list of LSMs to initialize. */
> -static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> -static __initdata struct lsm_info *exclusive;
> +/*
> + * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> + */
> +#define LSM_LOOP_UNROLL(M, ...) \
> +do { \
> + UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> +} while (0)
> +
> +#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
>
> #ifdef CONFIG_HAVE_STATIC_CALL
> #define LSM_HOOK_TRAMP(NAME, NUM) \
> @@ -155,490 +146,25 @@ struct lsm_static_calls_table
> #undef INIT_LSM_STATIC_CALL
> };
>
> -static __initdata bool debug;
> -#define init_debug(...) \
> - do { \
> - if (debug) \
> - pr_info(__VA_ARGS__); \
> - } while (0)
> -
> -static bool __init is_enabled(struct lsm_info *lsm)
> -{
> - if (!lsm->enabled)
> - return false;
> -
> - return *lsm->enabled;
> -}
> -
> -/* Mark an LSM's enabled flag. */
> -static int lsm_enabled_true __initdata = 1;
> -static int lsm_enabled_false __initdata = 0;
> -static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> -{
> - /*
> - * When an LSM hasn't configured an enable variable, we can use
> - * a hard-coded location for storing the default enabled state.
> - */
> - if (!lsm->enabled) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - else
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_true) {
> - if (!enabled)
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_false) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - } else {
> - *lsm->enabled = enabled;
> - }
> -}
> -
> -/* Is an LSM already listed in the ordered LSMs list? */
> -static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> -{
> - struct lsm_info **check;
> -
> - for (check = ordered_lsms; *check; check++)
> - if (*check == lsm)
> - return true;
> -
> - return false;
> -}
> -
> -/* Append an LSM to the list of ordered LSMs to initialize. */
> -static int last_lsm __initdata;
> -static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> -{
> - /* Ignore duplicate selections. */
> - if (exists_ordered_lsm(lsm))
> - return;
> -
> - if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> - return;
> -
> - /* Enable this LSM, if it is not already set. */
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - ordered_lsms[last_lsm++] = lsm;
> -
> - init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> -}
> -
> -/* Is an LSM allowed to be initialized? */
> -static bool __init lsm_allowed(struct lsm_info *lsm)
> -{
> - /* Skip if the LSM is disabled. */
> - if (!is_enabled(lsm))
> - return false;
> -
> - /* Not allowed if another exclusive LSM already initialized. */
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> - init_debug("exclusive disabled: %s\n", lsm->name);
> - return false;
> - }
> -
> - return true;
> -}
> -
> -static void __init lsm_set_blob_size(int *need, int *lbs)
> -{
> - int offset;
> -
> - if (*need <= 0)
> - return;
> -
> - offset = ALIGN(*lbs, sizeof(void *));
> - *lbs = offset + *need;
> - *need = offset;
> -}
> -
> -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> -{
> - if (!needed)
> - return;
> -
> - lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> - lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> - /*
> - * The inode blob gets an rcu_head in addition to
> - * what the modules might need.
> - */
> - if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> - blob_sizes.lbs_inode = sizeof(struct rcu_head);
> - lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> - lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> - lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> - lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> - lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_set_blob_size(&needed->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> -}
> -
> -/* Prepare LSM for initialization. */
> -static void __init prepare_lsm(struct lsm_info *lsm)
> -{
> - int enabled = lsm_allowed(lsm);
> -
> - /* Record enablement (to handle any following exclusive LSMs). */
> - set_enabled(lsm, enabled);
> -
> - /* If enabled, do pre-initialization work. */
> - if (enabled) {
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> - exclusive = lsm;
> - init_debug("exclusive chosen: %s\n", lsm->name);
> - }
> -
> - lsm_set_blob_sizes(lsm->blobs);
> - }
> -}
> -
> -/* Initialize a given LSM, if it is enabled. */
> -static void __init initialize_lsm(struct lsm_info *lsm)
> -{
> - if (is_enabled(lsm)) {
> - int ret;
> -
> - init_debug("initializing %s\n", lsm->name);
> - ret = lsm->init();
> - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> - }
> -}
> -
> -/*
> - * Current index to use while initializing the lsm id list.
> - */
> -u32 lsm_active_cnt __ro_after_init;
> -const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> -
> -/* Populate ordered LSMs list from comma-separated LSM name list. */
> -static void __init ordered_lsm_parse(const char *order, const char *origin)
> -{
> - struct lsm_info *lsm;
> - char *sep, *name, *next;
> -
> - /* LSM_ORDER_FIRST is always first. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_FIRST)
> - append_ordered_lsm(lsm, " first");
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - struct lsm_info *major;
> -
> - /*
> - * To match the original "security=" behavior, this
> - * explicitly does NOT fallback to another Legacy Major
> - * if the selected one was separately disabled: disable
> - * all non-matching Legacy Major LSMs.
> - */
> - for (major = __start_lsm_info; major < __end_lsm_info;
> - major++) {
> - if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> - strcmp(major->name, chosen_major_lsm) != 0) {
> - set_enabled(major, false);
> - init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> - chosen_major_lsm, major->name);
> - }
> - }
> - }
> -
> - sep = kstrdup(order, GFP_KERNEL);
> - next = sep;
> - /* Walk the list, looking for matching LSMs. */
> - while ((name = strsep(&next, ",")) != NULL) {
> - bool found = false;
> -
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (strcmp(lsm->name, name) == 0) {
> - if (lsm->order == LSM_ORDER_MUTABLE)
> - append_ordered_lsm(lsm, origin);
> - found = true;
> - }
> - }
> -
> - if (!found)
> - init_debug("%s ignored: %s (not built into kernel)\n",
> - origin, name);
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - if (strcmp(lsm->name, chosen_major_lsm) == 0)
> - append_ordered_lsm(lsm, "security=");
> - }
> - }
> -
> - /* LSM_ORDER_LAST is always last. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_LAST)
> - append_ordered_lsm(lsm, " last");
> - }
> -
> - /* Disable all LSMs not in the ordered list. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - set_enabled(lsm, false);
> - init_debug("%s skipped: %s (not in requested order)\n",
> - origin, lsm->name);
> - }
> -
> - kfree(sep);
> -}
> -
> -static void __init lsm_static_call_init(struct security_hook_list *hl)
> -{
> - struct lsm_static_call *scall = hl->scalls;
> - int i;
> -
> - for (i = 0; i < MAX_LSM_COUNT; i++) {
> - /* Update the first static call that is not used yet */
> - if (!scall->hl) {
> - __static_call_update(scall->key, scall->trampoline,
> - hl->hook.lsm_func_addr);
> - scall->hl = hl;
> - static_branch_enable(scall->active);
> - return;
> - }
> - scall++;
> - }
> - panic("%s - Ran out of static slots.\n", __func__);
> -}
> -
> -static void __init lsm_early_cred(struct cred *cred);
> -static void __init lsm_early_task(struct task_struct *task);
> -
> -static int lsm_append(const char *new, char **result);
> -
> -static void __init report_lsm_order(void)
> -{
> - struct lsm_info **lsm, *early;
> - int first = 0;
> -
> - pr_info("initializing lsm=");
> -
> - /* Report each enabled LSM name, comma separated. */
> - for (early = __start_early_lsm_info;
> - early < __end_early_lsm_info; early++)
> - if (is_enabled(early))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - if (is_enabled(*lsm))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> -
> - pr_cont("\n");
> -}
> -
> -static void __init ordered_lsm_init(void)
> -{
> - struct lsm_info **lsm;
> -
> - if (chosen_lsm_order) {
> - if (chosen_major_lsm) {
> - pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> - chosen_major_lsm, chosen_lsm_order);
> - chosen_major_lsm = NULL;
> - }
> - ordered_lsm_parse(chosen_lsm_order, "cmdline");
> - } else
> - ordered_lsm_parse(builtin_lsm_order, "builtin");
> -
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - prepare_lsm(*lsm);
> -
> - report_lsm_order();
> -
> - init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> - init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> - init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> - init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> - init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> -#ifdef CONFIG_KEYS
> - init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> -#endif /* CONFIG_KEYS */
> - init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> - init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> - init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> - init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> - init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> - init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> - init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> - init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> -
> - /*
> - * Create any kmem_caches needed for blobs
> - */
> - if (blob_sizes.lbs_file)
> - lsm_file_cache = kmem_cache_create("lsm_file_cache",
> - blob_sizes.lbs_file, 0,
> - SLAB_PANIC, NULL);
> - if (blob_sizes.lbs_inode)
> - lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> - blob_sizes.lbs_inode, 0,
> - SLAB_PANIC, NULL);
> -
> - lsm_early_cred((struct cred *) current->cred);
> - lsm_early_task(current);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - initialize_lsm(*lsm);
> -}
> -
> -int __init early_security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - prepare_lsm(lsm);
> - initialize_lsm(lsm);
> - }
> -
> - return 0;
> -}
> -
> /**
> - * security_init - initializes the security framework
> + * lsm_file_alloc - allocate a composite file blob
> + * @file: the file that needs a blob
> *
> - * This should be called early in the kernel initialization sequence.
> - */
> -int __init security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> - init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> - init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> -
> - /*
> - * Append the names of the early LSM modules now that kmalloc() is
> - * available
> - */
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - init_debug(" early started: %s (%s)\n", lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> - if (lsm->enabled)
> - lsm_append(lsm->name, &lsm_names);
> - }
> -
> - /* Load LSMs in specified order. */
> - ordered_lsm_init();
> -
> - return 0;
> -}
> -
> -/* Save user chosen LSM */
> -static int __init choose_major_lsm(char *str)
> -{
> - chosen_major_lsm = str;
> - return 1;
> -}
> -__setup("security=", choose_major_lsm);
> -
> -/* Explicitly choose LSM initialization order. */
> -static int __init choose_lsm_order(char *str)
> -{
> - chosen_lsm_order = str;
> - return 1;
> -}
> -__setup("lsm=", choose_lsm_order);
> -
> -/* Enable LSM order debugging. */
> -static int __init enable_debug(char *str)
> -{
> - debug = true;
> - return 1;
> -}
> -__setup("lsm.debug", enable_debug);
> -
> -static bool match_last_lsm(const char *list, const char *lsm)
> -{
> - const char *last;
> -
> - if (WARN_ON(!list || !lsm))
> - return false;
> - last = strrchr(list, ',');
> - if (last)
> - /* Pass the comma, strcmp() will check for '\0' */
> - last++;
> - else
> - last = list;
> - return !strcmp(last, lsm);
> -}
> -
> -static int lsm_append(const char *new, char **result)
> -{
> - char *cp;
> -
> - if (*result == NULL) {
> - *result = kstrdup(new, GFP_KERNEL);
> - if (*result == NULL)
> - return -ENOMEM;
> - } else {
> - /* Check if it is the last registered name */
> - if (match_last_lsm(*result, new))
> - return 0;
> - cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> - if (cp == NULL)
> - return -ENOMEM;
> - kfree(*result);
> - *result = cp;
> - }
> - return 0;
> -}
> -
> -/**
> - * security_add_hooks - Add a modules hooks to the hook lists.
> - * @hooks: the hooks to add
> - * @count: the number of hooks to add
> - * @lsmid: the identification information for the security module
> + * Allocate the file blob for all the modules
> *
> - * Each LSM has to register its hooks with the infrastructure.
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -void __init security_add_hooks(struct security_hook_list *hooks, int count,
> - const struct lsm_id *lsmid)
> +static int lsm_file_alloc(struct file *file)
> {
> - int i;
> -
> - /*
> - * A security module may call security_add_hooks() more
> - * than once during initialization, and LSM initialization
> - * is serialized. Landlock is one such case.
> - * Look at the previous entry, if there is one, for duplication.
> - */
> - if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> - if (lsm_active_cnt >= MAX_LSM_COUNT)
> - panic("%s Too many LSMs registered.\n", __func__);
> - lsm_idlist[lsm_active_cnt++] = lsmid;
> + if (!lsm_file_cache) {
> + file->f_security = NULL;
> + return 0;
> }
>
> - for (i = 0; i < count; i++) {
> - hooks[i].lsmid = lsmid;
> - lsm_static_call_init(&hooks[i]);
> - }
> -
> - /*
> - * Don't try to append during early_security_init(), we'll come back
> - * and fix this up afterwards.
> - */
> - if (slab_is_available()) {
> - if (lsm_append(lsmid->name, &lsm_names) < 0)
> - panic("%s - Cannot get early memory.\n", __func__);
> - }
> + file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> + if (file->f_security == NULL)
> + return -ENOMEM;
> + return 0;
> }
>
> /**
> @@ -673,46 +199,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> {
> return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
> }
>
> -/**
> - * lsm_early_cred - during initialization allocate a composite cred blob
> - * @cred: the cred that needs a blob
> - *
> - * Allocate the cred blob for all the modules
> - */
> -static void __init lsm_early_cred(struct cred *cred)
> -{
> - int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> -
> - if (rc)
> - panic("%s: Early cred alloc failed.\n", __func__);
> -}
> -
> -/**
> - * lsm_file_alloc - allocate a composite file blob
> - * @file: the file that needs a blob
> - *
> - * Allocate the file blob for all the modules
> - *
> - * Returns 0, or -ENOMEM if memory can't be allocated.
> - */
> -static int lsm_file_alloc(struct file *file)
> -{
> - if (!lsm_file_cache) {
> - file->f_security = NULL;
> - return 0;
> - }
> -
> - file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> - if (file->f_security == NULL)
> - return -ENOMEM;
> - return 0;
> -}
> -
> /**
> * lsm_inode_alloc - allocate a composite inode blob
> * @inode: the inode that needs a blob
> @@ -743,7 +234,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_task_alloc(struct task_struct *task)
> +int lsm_task_alloc(struct task_struct *task)
> {
> return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
> }
> @@ -812,20 +303,6 @@ static int lsm_bdev_alloc(struct block_device *bdev)
> return 0;
> }
>
> -/**
> - * lsm_early_task - during initialization allocate a composite task blob
> - * @task: the task that needs a blob
> - *
> - * Allocate the task blob for all the modules
> - */
> -static void __init lsm_early_task(struct task_struct *task)
> -{
> - int rc = lsm_task_alloc(task);
> -
> - if (rc)
> - panic("%s: Early task alloc failed.\n", __func__);
> -}
> -
> /**
> * lsm_superblock_alloc - allocate a composite superblock blob
> * @sb: the superblock that needs a blob
^ permalink raw reply
* Re: [PATCH v3 2/4] landlock: Fix handling of disconnected directories
From: Günther Noack @ 2025-07-24 14:49 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Tingmao Wang, Jann Horn, John Johansen, Al Viro, Ben Scarlato,
Christian Brauner, Daniel Burgener, Jeff Xu, NeilBrown,
Paul Moore, Ryan Sullivan, Song Liu, linux-fsdevel,
linux-security-module
In-Reply-To: <20250723.vouso1Kievao@digikod.net>
On Wed, Jul 23, 2025 at 11:01:42PM +0200, Mickaël Salaün wrote:
> On Tue, Jul 22, 2025 at 07:04:02PM +0100, Tingmao Wang wrote:
> > On the other hand, I'm still a bit uncertain about the domain check
> > semantics. While it would not cause a rename to be allowed if it is
> > otherwise not allowed by any rules on or above the mountpoint, this gets a
> > bit weird if we have a situation where renames are allowed on the
> > mountpoint or everywhere, but not read/writes, however read/writes are
> > allowed directly on a file, but the dir containing that file gets
> > disconnected so the sandboxed application can't read or write to it.
> > (Maybe someone would set up such a policy where renames are allowed,
> > expecting Landlock to always prevent renames where additional permissions
> > would be exposed?)
> >
> > In the above situation, if the file is then moved to a connected
> > directory, it will become readable/writable again.
>
> We can generalize this issue to not only the end file but any component
> of the path: disconnected directories. In fact, the main issue is the
> potential inconsistency of access checks over time (e.g. between two
> renames). This could be exploited to bypass the security checks done
> for FS_REFER.
>
> I see two solutions:
>
> 1. *Always* walk down to the IS_ROOT directory, and then jump to the
> mount point. This makes it possible to have consistent access checks
> for renames and open/use. The first downside is that that would
> change the current behavior for bind mounts that could get more
> access rights (if the policy explicitly sets rights for the hidden
> directories). The second downside is that we'll do more walk.
>
> 2. Return -EACCES (or -ENOENT) for actions involving disconnected
> directories, or renames of disconnected opened files. This second
> solution is simpler and safer but completely disables the use of
> disconnected directories and the rename of disconnected files for
> sandboxed processes.
>
> It would be much better to be able to handle opened directories as
> (object) capabilities, but that is not currently possible because of the
> way paths are handled by the VFS and LSM hooks.
>
> Tingmao, Günther, Jann, what do you think?
I have to admit that so far, I still failed to wrap my head around the
full patch set and its possible corner cases. I hope I did not
misunderstand things all too badly below:
As far as I understand the proposed patch, we are "checkpointing" the
intermediate results of the path walk at every mount point boundary,
and in the case where we run into a disconnected directory in one of
the nested mount points, we restore from the intermediate result at
the previous mount point directory and skip to the next mount point.
Visually speaking, if the layout is this (where ":" denotes a
mountpoint boundary between the mountpoints MP1, MP2, MP3):
dirfd
|
: V :
: ham <--- spam <--- eggs <--- x.txt
: (disconn.) :
: :
/ <--- foo <--- bar <--- baz :
: :
MP1 MP2 MP3
When a process holds a reference to the "spam" directory, which is now
disconnected, and invokes openat(dirfd, "eggs/x.txt", ...), then we
would:
* traverse x.txt
* traverse eggs (checkpointing the intermediate result) <-.
* traverse spam |
* traverse ham |
* discover that ham is disconnected: |
* restore the intermediate result from "eggs" ---------'
* continue the walk at foo
* end up at the root
So effectively, since the results from "spam" and "ham" are discarded,
we would traverse only the inodes in the outer and inner mountpoints
MP1 and MP3, but effectively return a result that looks like we did
not traverse MP2?
Maybe (likely) I misread the code. :) It's not clear to me what the
thinking behind this is. Also, if there was another directory in
between "spam" and "eggs" in MP2, wouldn't we be missing the access
rights attached to this directory?
Regarding the capability approach:
I agree that a "capability" approach would be the better solution, but
it seems infeasible with the existing LSM hooks at the moment. I
would be in favor of it though.
To spell it out a bit more explicitly what that would mean in my mind:
When a path is looked up relative to a dirfd, the path walk upwards
would terminate at the dirfd and use previously calculated access
rights stored in the associated struct file. These access rights
would be determined at the time of opening the dirfd, similar to how we
are already storing the "truncate" access right today for regular
files.
(Remark: There might still be corner cases where we have to solve it
the hard way, if someone uses ".." together with a dirfd-relative
lookup.)
I also looked at what it would take to change the LSM hooks to pass
the directory that the lookup was done relative to, but it seems that
this would have to be passed through a bunch of VFS callbacks as well,
which seems like a larger change. I would be curious whether that
would be deemed an acceptable change.
—Günther
P.S. Related to relative directory lookups, there is some movement in
the BSDs as well to use dirfds as capabilities, by adding a flag to
open directories that enforces O_BENEATH on subsequent opens:
* https://undeadly.org/cgi?action=article;sid=20250529080623
* https://reviews.freebsd.org/D50371
(both found via https://news.ycombinator.com/item?id=44575361)
If a dirfd had such a flag, that would get rid of the corner case
above.
^ permalink raw reply
* Re: [PATCH 0/2] Secure Boot lock down
From: sergeh @ 2025-07-24 14:13 UTC (permalink / raw)
To: Nicolas Bouchinet
Cc: Paul Moore, Hamza Mahfooz, Xiu Jianfeng, linux-kernel,
Ard Biesheuvel, James Morris, Serge E. Hallyn, Yue Haibing,
Tanya Agarwal, Kees Cook, linux-efi, linux-security-module
In-Reply-To: <xfabe3wvdsfkch3yhxmswhootf5vj6suyow5s3ffumcnjkojjz@e7ojgu3s7ion>
On Thu, Jul 24, 2025 at 02:59:39PM +0200, Nicolas Bouchinet wrote:
> Hi Hamza, thanks for your patch.
>
> Thanks, Paul, for the forward.
>
> Sorry for the delay, we took a bit of time to do some lore archaeology
> and discuss it with Xiu.
>
> As you might know, this has already been through debates in 2017 [1]. At
> that time, the decision was not to merge this behavior.
>
> Distros have indeed carried downstream patches reflecting this behavior
> for a long time and have been affected by vulnerabilities like
> CVE-2025-1272 [2], which is caused by the magic sprinkled in
> setup_arch().
>
> While your implementation looks cleaner to me. One of the points in
> previous debates was to have a Lockdown side Kconfig knob to enable or
> not this behavior. It would gate the registration of the Lockdown LSM to
> the security_lock_kernel_down() hook.
Well, but there is a default-n kconfig. What do you mean by "Lockdown
side Kconfig knob"? I'm sure I'm missing something, but not sure
what...
thanks,
-serge
^ permalink raw reply
* [PATCH 03/19] smack: fix bug: setting label-containing xattrs silently ignores input garbage
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
The following command:
# setfattr -n security.SMACK64 -v foo/bar FILE
sets the label of FILE to 'foo' w/o indication
that label does not match input.
This happens due to the use of an unsuitable parsing
function: smk_import_entry(), which acquires only
that part from the beginning of the input
that looks like a label.
This is exactly the same issue as described in [1],
but it occurs with Smack label-containing xattrs
instead of /proc/PID/attr/smack/current.
Curiously,
# setfattr -n security.SMACK64 -v foo/bar FILE
# setfattr -n security.SMACK64EXEC -v fo2/ba2 FILE
# setfattr -n security.SMACK64MMAP -v fo3/ba3 FILE
# getfattr -hd -m - FILE
security.SMACK64="foo" // garbage ignored
security.SMACK64EXEC="fo2/ba2" // garbage taken?!
security.SMACK64MMAP="fo3/ba3" // garbage taken?!
It looks like label-containing xattrs SMACK64EXEC
and SMACK64MMAP can acquire invalid Smack label.
In fact, inode contains the labels `fo2' and `fo3',
but, due to another Smack bug [2] we do not see them -
instead we see the on-disk content of the SMACK64EXEC
and SMACK64MMAP xattrs, that is, indeed, `fo2/ba2'
and `fo3/ba3'
This change detects input garbage by adding a check:
taken label length == input length
and indicates -EINVAL to the caller if they do not match
(2008-02-04 Casey Schaufler)
Fixes: e114e473771c ("Smack: Simplified Mandatory Access Control Kernel")
[1] 2025-06-17 andreev
commit 674e2b24791c ("smack: fix bug: setting task label
silently ignores input garbage")
Link: https://lore.kernel.org/linux-security-module/20250315015723.1357541-3-andreev@swemel.ru/
[2] 2025-07 andreev (forward reference)
commit ("smack: fix bug: getxattr() returns invalid SMACK64EXEC/MMAP")
Link: https://lore.kernel.org/linux-security-module/ba2af0356940d61d932af15d4b1a265a61e7d16c.1753356770.git.andreev@swemel.ru/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 81 +++++++++++++++++++++++++++-----------
1 file changed, 57 insertions(+), 24 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 5a159a2653a6..4ef6355c84c0 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -263,6 +263,49 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
#define smk_bu_credfile(cred, file, mode, RC) (RC)
#endif
+/**
+ * smk_parse_label - check @string (not \0-terminated) is a valid Smack label
+ * @string: a string to check, not \0-terminated
+ * @string_len: the length of the @string
+ *
+ * Return: 0 or an error code
+ */
+static int
+smk_parse_label(const char *string, size_t string_len)
+{
+ int label_len;
+
+ if (unlikely(string == NULL || string_len == 0 ||
+ string_len >= SMK_LONGLABEL))
+ return -EINVAL;
+
+ label_len = smk_parse_label_len(string, string_len);
+ if (label_len < 0 || /* invalid label */
+ label_len != string_len) /* garbage after label */
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * smk_import_label - import a label, return the list entry
+ * @string: a text string that may be a valid Smack label, not \0-terminated
+ * @string_len: the length of the text string in the @string
+ *
+ * Contrast to smk_import_entry(), the full @string_len of the @string
+ * must constitute a valid Smack label to be imported.
+ *
+ * Return: see description of smk_import_entry()
+ */
+static struct smack_known *
+smk_import_label(const char *string, int string_len)
+{
+ if (smk_parse_label(string, string_len))
+ return ERR_PTR(-EINVAL);
+
+ return smk_import_valid_label(string, string_len, GFP_NOFS);
+}
+
/**
* smk_fetch - Fetch the smack label from a file.
* @name: type of the label (attribute)
@@ -1343,14 +1386,11 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- int check_import = 0;
+ bool label_inside = true;
int check_star = 0;
struct inode * const inode = d_backing_inode(dentry);
umode_t const i_mode = inode->i_mode;
- /*
- * Check label validity here so import won't fail in post_setxattr
- */
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
/*
* inode of socket file descriptor (sockfs inode) and
@@ -1358,19 +1398,18 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
*/
if (S_ISSOCK(i_mode))
return -EINVAL;
- check_import = 1;
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
- check_import = 1;
+ ;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
- check_import = 1;
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
if (!S_ISDIR(i_mode) ||
size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
return -EINVAL;
+ label_inside = false;
} else {
/*
* treat other xattrs as labeled data
@@ -1387,13 +1426,13 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- if (check_import) {
- const struct smack_known *skp;
+ /*
+ * Import label now, so import won't fail in smack_inode_post_setxattr
+ */
+ if (label_inside) {
+ const struct smack_known * const skp =
+ smk_import_label(value, size);
- if (size == 0)
- return -EINVAL;
-
- skp = smk_import_entry(value, size);
if (IS_ERR(skp))
return PTR_ERR(skp);
@@ -3781,23 +3820,17 @@ static int do_setattr(unsigned int attr, void *value, size_t size)
struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
- int label_len;
/*
* let unprivileged user validate input, check permissions later
*/
- if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
+ if (smk_parse_label(value, size))
return -EINVAL;
-
- label_len = smk_parse_label_len(value, size);
- if (label_len < 0 || label_len != size)
- return -EINVAL;
-
/*
* No process is ever allowed the web ("@") label
* and the star ("*") label.
*/
- if (label_len == 1 /* '@', '*' */) {
+ if (size == 1 /* '@', '*' */) {
const char c = *(const char *)value;
if (c == *smack_known_web.smk_known ||
@@ -3810,8 +3843,8 @@ static int do_setattr(unsigned int attr, void *value, size_t size)
list_for_each_entry(sklep, &tsp->smk_relabel, list) {
const char *cp = sklep->smk_label->smk_known;
- if (strlen(cp) == label_len &&
- strncmp(cp, value, label_len) == 0)
+ if (strlen(cp) == size &&
+ strncmp(cp, value, size) == 0)
goto in_relabel;
}
return -EPERM;
@@ -3819,7 +3852,7 @@ static int do_setattr(unsigned int attr, void *value, size_t size)
;
}
- skp = smk_import_valid_label(value, label_len, GFP_KERNEL);
+ skp = smk_import_valid_label(value, size, GFP_KERNEL);
if (IS_ERR(skp))
return PTR_ERR(skp);
--
2.43.0
^ permalink raw reply related
* [PATCH 02/19] smack: fix bug: changing Smack xattrs requires cap_mac_override
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
The xattr-changing hooks (smack_inode_setxattr,
smack_inode_removexattr) treat Smack xattrs
(security.SMACK64*) like any other xattrs,
but additionally require the subject process
to possess cap_mac_admin.
These hooks treat "any other xattr" as if it is
just labeled data, so changing a Smack xattr,
like changing any other xattr,
requires the subject process to either possess
cap_mac_override or have a rule allowing it
to write to the object.
A curious effect arises here: the rule 'foo bar w'
allows subject 'foo' to relabel object 'bar'
to any label 8-O - clearly not what the rules
are designed for.
According to the Smack documentation [1,2],
possessing cap_mac_admin is necessary and sufficient
to change Smack xattrs (security.SMACK64*).
Treating Smack xattrs as labeled data
appears to be incorrect.
This change excludes the "labeled data check" for
Smack xattrs from the aforementioned hooks, making
cap_mac_admin sufficient to change Smack xattrs.
(2008-02-04 Casey Schaufler)
Fixes: e114e473771c ("Smack: Simplified Mandatory Access Control Kernel")
[1] Documentation/admin-guide/LSM/Smack.rst
[2] 2012-06-05 Casey Schaufler
commit 1880eff77e7a ("Smack: onlycap limits on CAP_MAC_ADMIN")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 105 +++++++++++++++++++------------------
1 file changed, 54 insertions(+), 51 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 42fdac05d32d..5a159a2653a6 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -175,7 +175,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
#endif
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
-static int smk_bu_inode(struct inode *inode, int mode, int rc)
+static int smk_bu_inode(const struct inode * const inode, int mode, int rc)
{
struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack *isp = smack_inode(inode);
@@ -1343,65 +1343,67 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- struct smk_audit_info ad;
- struct smack_known *skp;
- int check_priv = 0;
int check_import = 0;
int check_star = 0;
- int rc = 0;
- umode_t const i_mode = d_backing_inode(dentry)->i_mode;
+ struct inode * const inode = d_backing_inode(dentry);
+ umode_t const i_mode = inode->i_mode;
/*
* Check label validity here so import won't fail in post_setxattr
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
/*
- * UDS inode has fixed label
+ * inode of socket file descriptor (sockfs inode) and
+ * UDS inode have fixed label
*/
- if (S_ISSOCK(i_mode)) {
- rc = -EINVAL;
- } else {
- check_priv = 1;
- check_import = 1;
- }
+ if (S_ISSOCK(i_mode))
+ return -EINVAL;
+ check_import = 1;
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
- check_priv = 1;
check_import = 1;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
- check_priv = 1;
check_import = 1;
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
- check_priv = 1;
if (!S_ISDIR(i_mode) ||
size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
- rc = -EINVAL;
+ return -EINVAL;
+ } else {
+ /*
+ * treat other xattrs as labeled data
+ */
+ struct smk_audit_info ad;
+
+ smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
+ smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+ return smk_bu_inode(inode, MAY_WRITE,
+ smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad));
}
- if (check_priv && !smack_privileged(CAP_MAC_ADMIN))
- rc = -EPERM;
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
- if (rc == 0 && check_import) {
- skp = size ? smk_import_entry(value, size) : NULL;
+ if (check_import) {
+ const struct smack_known *skp;
+
+ if (size == 0)
+ return -EINVAL;
+
+ skp = smk_import_entry(value, size);
if (IS_ERR(skp))
- rc = PTR_ERR(skp);
- else if (skp == NULL || (check_star &&
- (skp == &smack_known_star || skp == &smack_known_web)))
- rc = -EINVAL;
+ return PTR_ERR(skp);
+
+ if (check_star &&
+ (skp == &smack_known_star ||
+ skp == &smack_known_web))
+ return -EINVAL;
}
- smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
- smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
-
- if (rc == 0) {
- rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
- rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
- }
-
- return rc;
+ return 0;
}
/**
@@ -1452,6 +1454,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
*/
static int smack_inode_getxattr(struct dentry *dentry, const char *name)
{
+ /*
+ * treat all xattrs as labeled data
+ */
struct smk_audit_info ad;
int rc;
@@ -1476,9 +1481,8 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
static int smack_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
{
- struct inode_smack *isp;
- struct smk_audit_info ad;
- int rc = 0;
+ const struct inode * const inode = d_backing_inode(dentry);
+ struct inode_smack * const isp = smack_inode(inode);
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
@@ -1487,21 +1491,20 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
if (!smack_privileged(CAP_MAC_ADMIN))
- rc = -EPERM;
+ return -EPERM;
+ } else {
+ /*
+ * treat other xattrs as labeled data
+ */
+ struct smk_audit_info ad;
+
+ smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
+ smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+ return smk_bu_inode(inode, MAY_WRITE,
+ smk_curacc(isp->smk_inode, MAY_WRITE, &ad));
}
- if (rc != 0)
- return rc;
-
- smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
- smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
-
- rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
- rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
- if (rc != 0)
- return rc;
-
- isp = smack_inode(d_backing_inode(dentry));
/*
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
@@ -1509,7 +1512,7 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
* XATTR_NAME_SMACK if S_ISSOCK (UDS inode has fixed label)
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
- if (!S_ISSOCK(d_backing_inode(dentry)->i_mode)) {
+ if (!S_ISSOCK(inode->i_mode)) {
struct super_block *sbp = dentry->d_sb;
struct superblock_smack *sbsp = smack_superblock(sbp);
--
2.43.0
^ permalink raw reply related
* [PATCH 01/19] smack: fix bug: changing Smack xattrs requires cap_sys_admin
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
[1] introduced a new LSM hook, inode_xattr_skipcap.
This hook is intended to identify security xattrs
for which the LSM takes responsibility for access control.
However, the Smack implementation, smack_inode_xattr_skipcap(),
has never worked as intended. It mistakenly does not recognize
the security.SMACK64* xattrs it owns.
As a result, Smack does not inform the common security layer
(security/security.c) that Smack is responsible for
security.SMACK64* xattrs. Consequently, the generic access
control functions (cap_inode_removexattr, cap_inode_setxattr)
are invoked, and they require cap_sys_admin to be effective.
This change corrects smack_inode_xattr_skipcap(),
allowing Smack xattrs to skip cap_inode_*xattr() calls.
[1] 2024-05-02, Paul Moore
Fixes: 61df7b828204 ("lsm: fixup the inode xattr capability handling")
Link: https://lore.kernel.org/linux-security-module/20240503005850.466144-2-paul@paul-moore.com/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index adf1c542d213..42fdac05d32d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1312,7 +1312,7 @@ static int smack_inode_getattr(const struct path *path)
*/
static int smack_inode_xattr_skipcap(const char *name)
{
- if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX)))
+ if (strncmp(name, XATTR_NAME_SMACK, sizeof(XATTR_NAME_SMACK) - 1))
return 0;
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
--
2.43.0
^ permalink raw reply related
* [PATCH 04/19] smack: stop polling other LSMs & VFS to getxattr() unsupported SMACK64IPIN/OUT
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
smack_inode_getsecurity() returns -EOPNOTSUPP
for the SMACK64IPIN and SMACK64IPOUT xattrs
if the inode is not a socket.
Since [1], the -EOPNOTSUPP return value
from the inode_getsecurity LSM hook
has been interpreted by security_inode_getsecurity()
as a signal to "continue polling other LSMs".
As a result, security_inode_getsecurity() queries
other LSMs for these Smack xattrs.
Furthermore, the VFS layer is also aware of the convention
and attempts to read SMACK64IPIN and SMACK64IPOUT from disk
via __vfs_getxattr() if all LSMs return -EOPNOTSUPP.
Looking for Smack propertу in these places is incorrect,
as Smack does own these xattrs - even if
they are irrelevant for a particular inode.
Returning -ENODATA (no such attribute) instead of
-EOPNOTSUPP is more appropriate, as it stops further
fallback to other LSMs and the filesystem.
This appears safe, since __vfs_getxattr() also returns
-ENODATA when the attribute does not exist.
[1] 2016-05-31 Casey Schaufler
commit 2885c1e3e0c2 ("LSM: Fix for security_inode_getsecurity
and -EOPNOTSUPP")
Link: https://lore.kernel.org/lkml/d8a4d26e-46c8-975d-d075-a3848130981c@schaufler-ca.com/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 4ef6355c84c0..7bd47baac481 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1649,10 +1649,6 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
struct inode *inode, const char *name,
void **buffer, bool alloc)
{
- struct socket_smack *ssp;
- struct socket *sock;
- struct super_block *sbp;
- struct inode *ip = inode;
struct smack_known *isp;
struct inode_smack *ispp;
size_t label_len;
@@ -1666,27 +1662,29 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
label = TRANS_TRUE;
else
label = "";
- } else {
+ } else if (strcmp(name, XATTR_SMACK_IPIN) == 0 ||
+ strcmp(name, XATTR_SMACK_IPOUT) == 0) {
/*
- * The rest of the Smack xattrs are only on sockets.
+ * These Smack xattrs are only on sockets.
*/
- sbp = ip->i_sb;
- if (sbp->s_magic != SOCKFS_MAGIC)
- return -EOPNOTSUPP;
+ const struct socket_smack *ssp;
+ const struct sock *sk;
- sock = SOCKET_I(ip);
- if (sock == NULL || sock->sk == NULL)
- return -EOPNOTSUPP;
+ if (inode->i_sb->s_magic != SOCKFS_MAGIC)
+ return -ENODATA;
- ssp = smack_sock(sock->sk);
+ sk = SOCKET_I(inode)->sk;
+ if (sk == NULL)
+ return -ENODATA;
+
+ ssp = smack_sock(sk);
if (strcmp(name, XATTR_SMACK_IPIN) == 0)
isp = ssp->smk_in;
- else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
- isp = ssp->smk_out;
else
- return -EOPNOTSUPP;
- }
+ isp = ssp->smk_out;
+ } else
+ return -EOPNOTSUPP;
if (!label)
label = isp->smk_known;
--
2.43.0
^ permalink raw reply related
* [PATCH 00/19] smack: clean up xattr handling
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
A set of minor bug fixes and optimizations in Smack xattr handling.
Logically independent, but with the code dependencies.
The patch set applies on top of:
https://github.com/cschaufler/smack-next/commits/next
commit 6ddd169d0288
Konstantin Andreev (19):
smack: fix bug: changing Smack xattrs requires cap_sys_admin
smack: fix bug: changing Smack xattrs requires cap_mac_override
smack: fix bug: setting label-containing xattrs silently ignores input garbage
smack: stop polling other LSMs & VFS to getxattr() unsupported SMACK64IPIN/OUT
smack: restrict getxattr() SMACK64TRANSMUTE to directories
smack: fix bug: getxattr() returns invalid SMACK64EXEC/MMAP
smack: deduplicate task label validation
smack: smack_inode_setsecurity: prevent setting SMACK64EXEC/MMAP in other LSMs
smack: smack_inode_setsecurity: prevent setting SMACK64IPIN/OUT in other LSMs
smack: fix bug: smack_inode_setsecurity() imports alien xattrs as labels
smack: fix bug: smack_inode_setsecurity() false EINVAL for alien xattrs
smack: restrict setxattr() SMACK64IPIN/IPOUT to sockets
smack: restrict setxattr() SMACK64EXEC/MMAP to regular files
smack: return EOPNOTSUPP for setxattr() unsupported SMACK64(TRANSMUTE)
smack: smack_inode_setsecurity(): skip checks for SMACK64TRANSMUTE
smack: smack_inode_notifysecctx(): reject invalid labels
smack: smack_inode_post_setxattr(): find label instead of import
smack: smack_inode_setsecurity(): find label instead of import
smack: deduplicate strcmp(name, XATTR_{,NAME_}SMACK*)
Documentation/admin-guide/LSM/Smack.rst | 3 +-
security/smack/smack.h | 2 +
security/smack/smack_access.c | 22 +-
security/smack/smack_lsm.c | 492 +++++++++++++++---------
4 files changed, 324 insertions(+), 195 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v4 4/4] kstack_erase: Support Clang stack depth tracking
From: Nicolas Schier @ 2025-07-24 13:08 UTC (permalink / raw)
To: Kees Cook
Cc: Arnd Bergmann, Masahiro Yamada, Nathan Chancellor, Marco Elver,
Andrey Konovalov, Andrey Ryabinin, Ard Biesheuvel,
Gustavo A. R. Silva, linux-kbuild, kasan-dev, linux-hardening,
Will Deacon, Catalin Marinas, Jonathan Cameron, Gavin Shan,
Russell King (Oracle), James Morse, Oza Pawandeep,
Anshuman Khandual, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Paolo Bonzini, Mike Rapoport,
Vitaly Kuznetsov, Henrique de Moraes Holschuh, Hans de Goede,
Ilpo Järvinen, Rafael J. Wysocki, Len Brown,
Masami Hiramatsu, Michal Wilczynski, Juergen Gross,
Andy Shevchenko, Kirill A. Shutemov, Roger Pau Monne,
David Woodhouse, Usama Arif, Guilherme G. Piccoli, Thomas Huth,
Brian Gerst, Hou Wenlong, Andrew Morton, Peter Zijlstra (Intel),
Luis Chamberlain, Sami Tolvanen, Christophe Leroy,
Andy Lutomirski, Baoquan He, Alexander Graf, Changyuan Lyu,
Paul Moore, James Morris, Serge E. Hallyn, Nick Desaulniers,
Bill Wendling, Justin Stitt, Jan Beulich, Boqun Feng,
Viresh Kumar, Paul E. McKenney, Bibo Mao, linux-kernel,
linux-arm-kernel, x86, kvm, ibm-acpi-devel, platform-driver-x86,
linux-acpi, linux-trace-kernel, linux-efi, linux-mm, kexec,
linux-security-module, llvm
In-Reply-To: <20250724055029.3623499-4-kees@kernel.org>
On Wed, Jul 23, 2025 at 10:50:28PM -0700, Kees Cook wrote:
> Wire up CONFIG_KSTACK_ERASE to Clang 21's new stack depth tracking
> callback[1] option.
>
> Link: https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-stack-depth [1]
> Signed-off-by: Kees Cook <kees@kernel.org>
> ---
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Masahiro Yamada <masahiroy@kernel.org>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Nicolas Schier <nicolas.schier@linux.dev>
> Cc: Marco Elver <elver@google.com>
> Cc: Andrey Konovalov <andreyknvl@gmail.com>
> Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
> Cc: Ard Biesheuvel <ardb@kernel.org>
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: <linux-kbuild@vger.kernel.org>
> Cc: <kasan-dev@googlegroups.com>
> Cc: <linux-hardening@vger.kernel.org>
> ---
Acked-by: Nicolas Schier <n.schier@avm.de>
^ permalink raw reply
* [PATCH 19/19] smack: deduplicate strcmp(name, XATTR_{,NAME_}SMACK*)
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 170 ++++++++++++++++++++++++-------------
1 file changed, 109 insertions(+), 61 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6c529de00584..81c0f69202ea 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -96,6 +96,49 @@ static int match_opt_prefix(char *s, int l, char **arg)
return Opt_error;
}
+enum smack_xa {
+ SMK_XA_OBJECT, // security.SMACK64
+ SMK_XA_IPIN, // security.SMACK64IPIN
+ SMK_XA_IPOUT, // security.SMACK64IPOUT
+ SMK_XA_EXEC, // security.SMACK64EXEC
+ SMK_XA_MMAP, // security.SMACK64MMAP
+ SMK_XA_TRANSMUTE, // security.SMACK64TRANSMUTE
+};
+
+static const char * const
+smk_xa_suffix[] = {
+ [SMK_XA_OBJECT] = "",
+ [SMK_XA_IPIN] = "IPIN",
+ [SMK_XA_IPOUT] = "IPOUT",
+ [SMK_XA_EXEC] = "EXEC",
+ [SMK_XA_MMAP] = "MMAP",
+ [SMK_XA_TRANSMUTE] = "TRANSMUTE",
+};
+
+static int
+smk_xa_suffix_to_id(const char *suffix)
+{
+ return match_string(smk_xa_suffix, ARRAY_SIZE(smk_xa_suffix), suffix);
+}
+
+static int
+smk_xa_name_to_id(const char *name)
+{
+ if (strncmp(name, XATTR_NAME_SMACK, sizeof(XATTR_NAME_SMACK) - 1))
+ return -EINVAL;
+
+ return smk_xa_suffix_to_id(name + (sizeof(XATTR_NAME_SMACK) - 1));
+}
+
+static int
+smk_xa_secname_to_id(const char *name)
+{
+ if (strncmp(name, XATTR_SMACK_SUFFIX, sizeof(XATTR_SMACK_SUFFIX) - 1))
+ return -EINVAL;
+
+ return smk_xa_suffix_to_id(name + (sizeof(XATTR_SMACK_SUFFIX) - 1));
+}
+
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static char *smk_bu_mess[] = {
"Bringup Error", /* Unused */
@@ -1355,18 +1398,7 @@ static int smack_inode_getattr(const struct path *path)
*/
static int smack_inode_xattr_skipcap(const char *name)
{
- if (strncmp(name, XATTR_NAME_SMACK, sizeof(XATTR_NAME_SMACK) - 1))
- return 0;
-
- if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
- strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
- strcmp(name, XATTR_NAME_SMACKMMAP) == 0 ||
- strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
- return 1;
-
- return 0;
+ return (smk_xa_name_to_id(name) >= 0);
}
/*
@@ -1406,15 +1438,17 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
struct inode * const inode = d_backing_inode(dentry);
umode_t const i_mode = inode->i_mode;
- if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+ switch (smk_xa_name_to_id(name)) {
+ case SMK_XA_OBJECT:
/*
* inode of socket file descriptor (sockfs inode) and
* UDS inode have fixed label
*/
if (S_ISSOCK(i_mode))
return -EOPNOTSUPP;
- } else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ break;
+ case SMK_XA_IPIN:
+ case SMK_XA_IPOUT:
/*
* inode of socket file descriptor (sockfs inode) only
*/
@@ -1423,19 +1457,22 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
if (SOCKET_I(inode)->sk == NULL)
return -EOPNOTSUPP;
- } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
- strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
+ break;
+ case SMK_XA_EXEC:
+ case SMK_XA_MMAP:
if (!S_ISREG(i_mode))
return -EOPNOTSUPP;
task_label = true;
- } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
+ break;
+ case SMK_XA_TRANSMUTE:
if (!S_ISDIR(i_mode))
return -EOPNOTSUPP;
if (size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
return -EINVAL;
label_inside = false;
- } else {
+ break;
+ default: {
/*
* treat other xattrs as labeled data
*/
@@ -1447,6 +1484,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
return smk_bu_inode(inode, MAY_WRITE,
smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad));
}
+ }
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
@@ -1487,17 +1525,21 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
struct smack_known **skpp = NULL;
struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
- if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
+ switch (smk_xa_name_to_id(name)) {
+ case SMK_XA_TRANSMUTE:
isp->smk_flags |= SMK_INODE_TRANSMUTE;
return;
+ case SMK_XA_OBJECT:
+ skpp = &isp->smk_inode;
+ break;
+ case SMK_XA_EXEC:
+ skpp = &isp->smk_task;
+ break;
+ case SMK_XA_MMAP:
+ skpp = &isp->smk_mmap;
+ break;
}
- if (strcmp(name, XATTR_NAME_SMACK) == 0)
- skpp = &isp->smk_inode;
- else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
- skpp = &isp->smk_task;
- else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
- skpp = &isp->smk_mmap;
/*
* Label has been imported by smack_inode_setxattr, just find it
*/
@@ -1545,16 +1587,9 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
{
const struct inode * const inode = d_backing_inode(dentry);
struct inode_smack * const isp = smack_inode(inode);
+ int const xa_id = smk_xa_name_to_id(name);
- if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
- strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
- strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 ||
- strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
- if (!smack_privileged(CAP_MAC_ADMIN))
- return -EPERM;
- } else {
+ if (xa_id < 0) {
/*
* treat other xattrs as labeled data
*/
@@ -1567,25 +1602,34 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
smk_curacc(isp->smk_inode, MAY_WRITE, &ad));
}
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+
/*
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
* XATTR_NAME_SMACKIPOUT
* XATTR_NAME_SMACK if S_ISSOCK (UDS inode has fixed label)
*/
- if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+ switch (xa_id) {
+ case SMK_XA_OBJECT:
if (!S_ISSOCK(inode->i_mode)) {
struct super_block *sbp = dentry->d_sb;
struct superblock_smack *sbsp = smack_superblock(sbp);
isp->smk_inode = sbsp->smk_default;
}
- } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
+ break;
+ case SMK_XA_EXEC:
isp->smk_task = NULL;
- else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
+ break;
+ case SMK_XA_MMAP:
isp->smk_mmap = NULL;
- else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
+ break;
+ case SMK_XA_TRANSMUTE:
isp->smk_flags &= ~SMK_INODE_TRANSMUTE;
+ break;
+ }
return 0;
}
@@ -1676,20 +1720,26 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
const struct inode_smack * const isp = smack_inode(inode);
const char *value = NULL;
int value_len;
+ int const xa_id = smk_xa_secname_to_id(name);
- if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ switch (xa_id) {
+ case SMK_XA_OBJECT:
skp = isp->smk_inode;
- } else if (strcmp(name, XATTR_SMACK_EXEC) == 0) {
+ break;
+ case SMK_XA_EXEC:
skp = isp->smk_task;
- } else if (strcmp(name, XATTR_SMACK_MMAP) == 0) {
+ break;
+ case SMK_XA_MMAP:
skp = isp->smk_mmap;
- } else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
+ break;
+ case SMK_XA_TRANSMUTE:
if (isp->smk_flags & SMK_INODE_TRANSMUTE)
value = TRANS_TRUE;
else
return -ENODATA;
- } else if (strcmp(name, XATTR_SMACK_IPIN) == 0 ||
- strcmp(name, XATTR_SMACK_IPOUT) == 0) {
+ break;
+ case SMK_XA_IPIN:
+ case SMK_XA_IPOUT: {
/*
* These Smack xattrs are only on sockets.
*/
@@ -1705,12 +1755,15 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
ssp = smack_sock(sk);
- if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ if (xa_id == SMK_XA_IPIN)
skp = ssp->smk_in;
else
skp = ssp->smk_out;
- } else
+ break;
+ }
+ default:
return -EOPNOTSUPP;
+ }
if (!value) {
if (!skp)
@@ -2986,28 +3039,23 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
struct socket_smack *ssp;
struct socket *sock;
int rc = 0;
+ int const xa_id = smk_xa_secname_to_id(name);
- if (!(strcmp(name, XATTR_SMACK_SUFFIX) == 0 ||
- strcmp(name, XATTR_SMACK_TRANSMUTE) == 0 ||
- strcmp(name, XATTR_SMACK_EXEC) == 0 ||
- strcmp(name, XATTR_SMACK_MMAP) == 0 ||
- strcmp(name, XATTR_SMACK_IPIN) == 0 ||
- strcmp(name, XATTR_SMACK_IPOUT) == 0
- ))
+ if (xa_id < 0)
return -EOPNOTSUPP;
- if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
+ switch (xa_id) {
+ case SMK_XA_TRANSMUTE:
nsp->smk_flags |= SMK_INODE_TRANSMUTE;
return 0;
- }
-
- if (strcmp(name, XATTR_SMACK_EXEC) == 0 ||
- strcmp(name, XATTR_SMACK_MMAP) == 0)
+ case SMK_XA_EXEC:
+ case SMK_XA_MMAP:
return -ENODATA;
+ }
skp = smk_find_label(value, size);
- if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ if (xa_id == SMK_XA_OBJECT) {
nsp->smk_inode = skp;
nsp->smk_flags |= SMK_INODE_INSTANT;
return 0;
@@ -3020,7 +3068,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
sock = SOCKET_I(inode);
ssp = smack_sock(sock->sk);
- if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ if (xa_id == SMK_XA_IPIN)
ssp->smk_in = skp;
else {
ssp->smk_out = skp;
--
2.43.0
^ permalink raw reply related
* [PATCH 18/19] smack: smack_inode_setsecurity(): find label instead of import
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
smack_inode_setsecurity() is called
for label-containing xattrs either
after smack_inode_setxattr():
fs/xattr.c:
...
` __vfs_setxattr_locked
` security_inode_setxattr
` __vfs_setxattr_noperm
` security_inode_setsecurity
or
...
` smack_inode_notifysecctx
` smack_inode_setsecurity
In both cases - via security_inode_setxattr()
or smack_inode_notifysecctx() -
the label is imported in advance.
There is no need to validate and import
the input value again; looking it up is sufficient.
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 5d3d72162444..6c529de00584 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2996,17 +2996,16 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
))
return -EOPNOTSUPP;
- if (value == NULL || size > SMK_LONGLABEL || size == 0)
- return -EINVAL;
-
if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
nsp->smk_flags |= SMK_INODE_TRANSMUTE;
return 0;
}
- skp = smk_import_entry(value, size);
- if (IS_ERR(skp))
- return PTR_ERR(skp);
+ if (strcmp(name, XATTR_SMACK_EXEC) == 0 ||
+ strcmp(name, XATTR_SMACK_MMAP) == 0)
+ return -ENODATA;
+
+ skp = smk_find_label(value, size);
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
nsp->smk_inode = skp;
@@ -3014,9 +3013,6 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return 0;
}
- if (strcmp(name, XATTR_SMACK_EXEC) == 0 ||
- strcmp(name, XATTR_SMACK_MMAP) == 0)
- return -ENODATA;
/*
* The rest of the Smack xattrs are only on sockets.
* smack_inode_setxattr() has checked that inode is sockfs
--
2.43.0
^ permalink raw reply related
* [PATCH 16/19] smack: smack_inode_notifysecctx(): reject invalid labels
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
Exactly the same issue as described in [1,2].
smack_inode_notifysecctx()
` smack_inode_setsecurity
` smk_import_entry
uses an unsuitable parsing
function: smk_import_entry(), which acquires only
that part from the beginning of the input
that looks like a label.
[1] 2025-06-17 andreev
commit 674e2b24791c ("smack: fix bug: setting task label
silently ignores input garbage")
Link: https://lore.kernel.org/linux-security-module/20250315015723.1357541-3-andreev@swemel.ru/
[2] 2025-07 andreev
commit ("smack: fix bug: setting label-containing xattrs
silently ignores input garbage")
Link: https://lore.kernel.org/linux-security-module/ae1100894499a1f6ce8e783727635388b3ac3af8.1753356770.git.andreev@swemel.ru/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 5175dfb3d448..9271cd54bc43 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -5003,11 +5003,17 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
+ const struct smack_known *skp;
/*
* UDS inode has fixed label. Ignore nfs label.
*/
if (S_ISSOCK(inode->i_mode))
return 0;
+
+ skp = smk_import_label(ctx, ctxlen);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx,
ctxlen, 0);
}
--
2.43.0
^ permalink raw reply related
* [PATCH 17/19] smack: smack_inode_post_setxattr(): find label instead of import
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
Efforts were made in [1]
to ensure that smk_import_entry() can not fail
when called by smack_inode_post_setxattr():
label is imported in advance by smack_inode_setxattr().
However,
- smk_import_entry() can still fail
due to memory allocation
- its use is misleading here,
as no actual import is needed
Using smk_find_entry() instead of smk_import_entry()
should be sufficient for smack_inode_post_setxattr().
However, smk_find_entry() takes a \0-terminated string,
while we have a (non-\0-terminated string, length) tuple.
To resolve this, I added smk_find_label_rcu(),
which accepts such a tuple and
is otherwise identical to smk_find_entry().
It is now used in smack_inode_post_setxattr().
smack_inode_post_setxattr() can no longer fail.
[1] 2009-04-16 etienne.basset
commit defc433ba3bc ("Smack: check for SMACK xattr validity
in smack_inode_setxattr")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack.h | 2 ++
security/smack/smack_access.c | 22 ++++++++++++++++++++--
security/smack/smack_lsm.c | 26 ++++++++++++--------------
3 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 759343a6bbae..9abb11947fe9 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -294,6 +294,8 @@ struct smack_known *smk_import_valid_label(const char *label, int label_len,
gfp_t gfp);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
+struct smack_known *smk_find_label_rcu(const char *string, size_t string_len);
+struct smack_known *smk_find_label(const char *string, size_t string_len);
bool smack_privileged(int cap);
bool smack_privileged_cred(int cap, const struct cred *cred);
void smk_destroy_label_list(struct list_head *list);
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 09167be79122..390dc9642f9b 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -427,21 +427,39 @@ void smk_insert_entry(struct smack_known *skp)
* matches the passed string or NULL if not found.
*/
struct smack_known *smk_find_entry(const char *string)
+{
+ return smk_find_label_rcu(string, strlen(string));
+}
+
+struct smack_known *
+smk_find_label_rcu(const char *string, size_t string_len)
{
unsigned int hash;
struct hlist_head *head;
struct smack_known *skp;
- hash = full_name_hash(NULL, string, strlen(string));
+ hash = full_name_hash(NULL, string, string_len);
head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
hlist_for_each_entry_rcu(skp, head, smk_hashed)
- if (strcmp(skp->smk_known, string) == 0)
+ if (strlen(skp->smk_known) == string_len &&
+ strncmp(skp->smk_known, string, string_len) == 0)
return skp;
return NULL;
}
+struct smack_known *
+smk_find_label(const char *string, size_t string_len)
+{
+ struct smack_known *skp;
+
+ rcu_read_lock();
+ skp = smk_find_label_rcu(string, string_len);
+ rcu_read_unlock();
+ return skp;
+}
+
/**
* smk_parse_label_len - calculate the length of the starting segment
* in the string that constitutes a valid smack label
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 9271cd54bc43..5d3d72162444 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1484,7 +1484,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- struct smack_known *skp;
+ struct smack_known **skpp = NULL;
struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
@@ -1492,19 +1492,17 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
return;
}
- if (strcmp(name, XATTR_NAME_SMACK) == 0) {
- skp = smk_import_entry(value, size);
- if (!IS_ERR(skp))
- isp->smk_inode = skp;
- } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
- skp = smk_import_entry(value, size);
- if (!IS_ERR(skp))
- isp->smk_task = skp;
- } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
- skp = smk_import_entry(value, size);
- if (!IS_ERR(skp))
- isp->smk_mmap = skp;
- }
+ if (strcmp(name, XATTR_NAME_SMACK) == 0)
+ skpp = &isp->smk_inode;
+ else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
+ skpp = &isp->smk_task;
+ else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
+ skpp = &isp->smk_mmap;
+ /*
+ * Label has been imported by smack_inode_setxattr, just find it
+ */
+ if (skpp)
+ *skpp = smk_find_label(value, size);
return;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 15/19] smack: smack_inode_setsecurity(): skip checks for SMACK64TRANSMUTE
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
Like smack_inode_post_setxattr(), smack_inode_setsecurity()
is called after smack_inode_setxattr()
for the SMACK64TRANSMUTE xattr:
fs/xattr.c:
...
` __vfs_setxattr_locked
` security_inode_setxattr
` __vfs_setxattr_noperm
|
` security_inode_post_setxattr
| ^
| }- one of them is called
| v
` security_inode_setsecurity
Like smack_inode_post_setxattr(), smack_inode_setsecurity()
does not need to check
the applicability of the SMACK64TRANSMUTE or its value,
as they have already been checked by smack_inode_setxattr()
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 113371887b4d..5175dfb3d448 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3002,10 +3002,6 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return -EINVAL;
if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
- if (!S_ISDIR(inode->i_mode) || size != TRANS_TRUE_SIZE ||
- strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
- return -EINVAL;
-
nsp->smk_flags |= SMK_INODE_TRANSMUTE;
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 14/19] smack: return EOPNOTSUPP for setxattr() unsupported SMACK64(TRANSMUTE)
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
The SMACK64TRANSMUTE is supported only for directories.
"The standard return value for unsupported attribute names is
-EOPNOTSUPP, as opposed to undefined but supported attributes
(-ENODATA)" [3]
Smack follows [4] the convention
for get/setxattr() SMACK64IPIN/IPOUT
It is more appropriate to return -EOPNOTSUPP
instead of -EINVAL when attempting to set SMACK64TRANSMUTE
on a non-directory object.
A Unix domain socket (UDS) with a BSD address, and
the inode of a socket file descriptor (sockfs inode)
have fixed (*) nominal SMACK64 label [2]
Likewise, it is more appropriate to return -EOPNOTSUPP
instead of -EINVAL when attempting to set
the SMACK64 xattr on either type of socket inode.
The commits being fixed [1,2] are recent enough
so this change should not break userspace.
[1] 2023-11-16 roberto.sassu
Fixes: 9c82169208dd ("smack: Set SMACK64TRANSMUTE only for dirs in smack_inode_setxattr()")
Link: https://lore.kernel.org/linux-security-module/20231116090125.187209-2-roberto.sassu@huaweicloud.com/
[2] 2025-06-16 andreev
Fixes: 78fc6a94be25 ("smack: fix bug: invalid label of unix socket file")
Link: https://lore.kernel.org/linux-security-module/20250616010745.800386-6-andreev@swemel.ru/
[3] 2016-09-29 agruenba
commit 971df15bd54a ("sockfs: getxattr: Fail with -EOPNOTSUPP
for invalid attribute names")
[4] 2008-02-04 casey
commit e114e473771c ("Smack: Simplified Mandatory Access Control Kernel")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6712fa047722..113371887b4d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1412,7 +1412,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
* UDS inode have fixed label
*/
if (S_ISSOCK(i_mode))
- return -EINVAL;
+ return -EOPNOTSUPP;
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
/*
@@ -1429,8 +1429,9 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
return -EOPNOTSUPP;
task_label = true;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
- if (!S_ISDIR(i_mode) ||
- size != TRANS_TRUE_SIZE ||
+ if (!S_ISDIR(i_mode))
+ return -EOPNOTSUPP;
+ if (size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
return -EINVAL;
label_inside = false;
--
2.43.0
^ permalink raw reply related
* [PATCH 12/19] smack: restrict setxattr() SMACK64IPIN/IPOUT to sockets
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
The SMACK64IPIN and SMACK64IPOUT xattrs apply
only to sockets. However, smack_inode_setxattr()
currently allows setting them on any filesystem
object, including regular files, FIFOs, and others.
These xattrs are even written to disk by the
underlying filesystem. E.g. you can
# setfattr -n security.SMACK64IPIN -v foo /etc/passwd
# # no error
and have SMACK64IPIN on disk.
This change restricts setting SMACK64IPIN/IPOUT
in smack_inode_setxattr() to socket inodes only.
Given that, the corresponding check in
smack_inode_setsecurity() may be omitted,
as it called after smack_inode_setxattr()
for SMACK64IPIN/IPOUT:
fs/xattr.c:
...
` __vfs_setxattr_locked
` security_inode_setxattr
` __vfs_setxattr_noperm
` security_inode_setsecurity
Additionally, with this change the error code returned by setxattr()
for unsupported SMACK64IPIN/OUT xattrs
changes from -ENODATA [1]:
# setfattr -n security.SMACK64IPIN -v foo /sys/kernel/debug/sleep_time
setfattr: /sys/kernel/debug/sleep_time: No such attribute
back to -EOPNOTSUPP:
# setfattr -n security.SMACK64IPIN -v foo /sys/kernel/debug/sleep_time
setfattr: /sys/kernel/debug/sleep_time: Operation not supported
[1] 2025-07 andreev
commit ("smack: smack_inode_setsecurity:
prevent setting SMACK64IPIN/OUT in other LSMs")
Link: https://lore.kernel.org/linux-security-module/a0d039a407a8164a2025847f5b00fd5f3c2e5def.1753356770.git.andreev@swemel.ru/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
Documentation/admin-guide/LSM/Smack.rst | 3 ++-
security/smack/smack_lsm.c | 16 +++++++++-------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/admin-guide/LSM/Smack.rst b/Documentation/admin-guide/LSM/Smack.rst
index c5ed775f2d10..ce8be25333a7 100644
--- a/Documentation/admin-guide/LSM/Smack.rst
+++ b/Documentation/admin-guide/LSM/Smack.rst
@@ -693,7 +693,8 @@ can only be set by privileged tasks, but any task can read them for their own
sockets.
SMACK64IPIN:
- The Smack label of the task object. A privileged
+ The Smack label of incoming packets must have write access to the
+ Smack label, specified in the SMACK64IPIN attribute. A privileged
program that will enforce policy may set this to the star label.
SMACK64IPOUT:
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 672be8b47821..a66fa2c16dc2 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1415,7 +1415,14 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
return -EINVAL;
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
- ;
+ /*
+ * inode of socket file descriptor (sockfs inode) only
+ */
+ if (inode->i_sb->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ if (SOCKET_I(inode)->sk == NULL)
+ return -EOPNOTSUPP;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
task_label = true;
@@ -3015,14 +3022,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return -ENODATA;
/*
* The rest of the Smack xattrs are only on sockets.
+ * smack_inode_setxattr() has checked that inode is sockfs
*/
- if (inode->i_sb->s_magic != SOCKFS_MAGIC)
- return -ENODATA;
-
sock = SOCKET_I(inode);
- if (sock->sk == NULL)
- return -ENODATA;
-
ssp = smack_sock(sock->sk);
if (strcmp(name, XATTR_SMACK_IPIN) == 0)
--
2.43.0
^ permalink raw reply related
* [PATCH 13/19] smack: restrict setxattr() SMACK64EXEC/MMAP to regular files
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
The SMACK64EXEC and SMACK64MMAP xattrs apply
only to regular files. However, setxattr() currently
allows setting them on any filesystem object,
including FIFOs, device nodes, and others. E.g.
root# setfattr -n security.SMACK64EXEC -v foo /dev/null
root# getfattr -hn security.SMACK64EXEC /dev/null
# file: dev/null
security.SMACK64EXEC="foo"
This change restricts setting SMACK64EXEC and
SMACK64MMAP to regular files only.
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index a66fa2c16dc2..6712fa047722 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1425,6 +1425,8 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
return -EOPNOTSUPP;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
+ if (!S_ISREG(i_mode))
+ return -EOPNOTSUPP;
task_label = true;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
if (!S_ISDIR(i_mode) ||
@@ -3754,15 +3756,17 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
/*
* Don't let the exec or mmap label be "*" or "@".
*/
- skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
- if (IS_ERR(skp) || smk_task_invalid_label(skp))
- skp = NULL;
- isp->smk_task = skp;
+ if (S_ISREG(inode->i_mode)) {
+ skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
+ if (IS_ERR(skp) || smk_task_invalid_label(skp))
+ skp = NULL;
+ isp->smk_task = skp;
- skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
- if (IS_ERR(skp) || smk_task_invalid_label(skp))
- skp = NULL;
- isp->smk_mmap = skp;
+ skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
+ if (IS_ERR(skp) || smk_task_invalid_label(skp))
+ skp = NULL;
+ isp->smk_mmap = skp;
+ }
dput(dp);
break;
--
2.43.0
^ permalink raw reply related
* [PATCH 11/19] smack: fix bug: smack_inode_setsecurity() false EINVAL for alien xattrs
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
Currently, smack_inode_setsecurity() checks
the validity of the xattr value before checking
whether the xattr is actually a Smack xattr.
This was correct when only one LSM
could be active in the system.
Since [1] this is no longer incorrect.
When Smack mistakenly EINVALidates a non-Smack xattr,
Smack may prevent owner LSM from seeing the xattr.
The change ensures that the xattr is recognized
as a Smack xattr before looking into the value.
[1] 2015-05-02 Casey Schaufler
Fixes: b1d9e6b0646d ("LSM: Switch to lists of hooks")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6f74be82ae45..672be8b47821 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2979,6 +2979,15 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
struct socket *sock;
int rc = 0;
+ if (!(strcmp(name, XATTR_SMACK_SUFFIX) == 0 ||
+ strcmp(name, XATTR_SMACK_TRANSMUTE) == 0 ||
+ strcmp(name, XATTR_SMACK_EXEC) == 0 ||
+ strcmp(name, XATTR_SMACK_MMAP) == 0 ||
+ strcmp(name, XATTR_SMACK_IPIN) == 0 ||
+ strcmp(name, XATTR_SMACK_IPOUT) == 0
+ ))
+ return -EOPNOTSUPP;
+
if (value == NULL || size > SMK_LONGLABEL || size == 0)
return -EINVAL;
@@ -2991,14 +3000,6 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return 0;
}
- if (!(strcmp(name, XATTR_SMACK_SUFFIX) == 0 ||
- strcmp(name, XATTR_SMACK_EXEC) == 0 ||
- strcmp(name, XATTR_SMACK_MMAP) == 0 ||
- strcmp(name, XATTR_SMACK_IPIN) == 0 ||
- strcmp(name, XATTR_SMACK_IPOUT) == 0
- ))
- return -EOPNOTSUPP;
-
skp = smk_import_entry(value, size);
if (IS_ERR(skp))
return PTR_ERR(skp);
--
2.43.0
^ permalink raw reply related
* [PATCH 09/19] smack: smack_inode_setsecurity: prevent setting SMACK64IPIN/OUT in other LSMs
From: Konstantin Andreev @ 2025-07-24 13:09 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <cover.1753356770.git.andreev@swemel.ru>
smack_inode_setsecurity() equally returns -EOPNOTSUPP
either the inode does not come from socket (sockfs
inode) or the xattr is a security.NOT-SMACK-XATTR
This did make no difference until [1]. Since [1]
-EOPNOTSUPP is reserved by security_inode_setsecurity()
as a signal to "continue polling other LSMs".
When xattr is SMACK64IPIN or SMACK64IPOUT and inode
is not from socket then return code is -EOPNOTSUPP,
and the security_inode_setsecurity() proceeds to query
other LSMs and attempts to store the xattr there.
Passing a Smack property to other LSMs is incorrect,
as Smack owns these xattrs.
This change returns -ENODATA if inode does not come
from sockfs and the xattr is SMACK64IPIN/OUT.
This causes change from
# setfattr -n security.SMACK64IPIN -v foo /sys/kernel/debug/sleep_time
setfattr: /sys/kernel/debug/sleep_time: Operation not supported
to
# setfattr -n security.SMACK64IPIN -v foo /sys/kernel/debug/sleep_time
setfattr: /sys/kernel/debug/sleep_time: No such attribute
not ideal, but it makes sense and prevents fallback to other LSMs.
[1] 2016-05-31 Casey Schaufler
commit 2885c1e3e0c2 ("LSM: Fix for security_inode_getsecurity
and -EOPNOTSUPP")
Link: https://lore.kernel.org/lkml/d8a4d26e-46c8-975d-d075-a3848130981c@schaufler-ca.com/
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack_lsm.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 00d4b5bf1056..7108696083d8 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3004,21 +3004,25 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
if (strcmp(name, XATTR_SMACK_EXEC) == 0 ||
strcmp(name, XATTR_SMACK_MMAP) == 0)
return -ENODATA;
+
+ if (!(strcmp(name, XATTR_SMACK_IPIN) == 0 ||
+ strcmp(name, XATTR_SMACK_IPOUT) == 0))
+ return -EOPNOTSUPP;
/*
* The rest of the Smack xattrs are only on sockets.
*/
if (inode->i_sb->s_magic != SOCKFS_MAGIC)
- return -EOPNOTSUPP;
+ return -ENODATA;
sock = SOCKET_I(inode);
- if (sock == NULL || sock->sk == NULL)
- return -EOPNOTSUPP;
+ if (sock->sk == NULL)
+ return -ENODATA;
ssp = smack_sock(sock->sk);
if (strcmp(name, XATTR_SMACK_IPIN) == 0)
ssp->smk_in = skp;
- else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
+ else {
ssp->smk_out = skp;
if (sock->sk->sk_family == PF_INET) {
rc = smack_netlbl_add(sock->sk);
@@ -3027,8 +3031,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
"Smack: \"%s\" netlbl error %d.\n",
__func__, -rc);
}
- } else
- return -EOPNOTSUPP;
+ }
#ifdef SMACK_IPV6_PORT_LABELING
if (sock->sk->sk_family == PF_INET6)
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox