* [PATCH v4 10/34] lsm: rework lsm_active_cnt and lsm_idlist[]
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
Move the LSM active count and lsm_id list declarations out of a header
that is visible across the kernel and into a header that is limited to
the LSM framework. This not only helps keep the include/linux headers
smaller and cleaner, it helps prevent misuse of these variables.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
include/linux/security.h | 2 --
security/lsm.h | 5 +++++
security/lsm_init.c | 6 ------
security/lsm_syscalls.c | 2 ++
security/security.c | 3 +++
5 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index 521bcb5b9717..8560c50edd2e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -167,8 +167,6 @@ struct lsm_prop {
};
extern const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1];
-extern u32 lsm_active_cnt;
-extern const struct lsm_id *lsm_idlist[];
/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
diff --git a/security/lsm.h b/security/lsm.h
index 0e1731bad4a7..dbe755c45e57 100644
--- a/security/lsm.h
+++ b/security/lsm.h
@@ -7,6 +7,11 @@
#define _LSM_H_
#include <linux/lsm_hooks.h>
+#include <linux/lsm_count.h>
+
+/* List of configured LSMs */
+extern unsigned int lsm_active_cnt;
+extern const struct lsm_id *lsm_idlist[];
/* LSM blob configuration */
extern struct lsm_blob_sizes blob_sizes;
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 2e76cefb1585..9e495a36a332 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -217,12 +217,6 @@ static void __init initialize_lsm(struct lsm_info *lsm)
}
}
-/*
- * 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)
{
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index 8440948a690c..5648b1f0ce9c 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -17,6 +17,8 @@
#include <linux/lsm_hooks.h>
#include <uapi/linux/lsm.h>
+#include "lsm.h"
+
/**
* lsm_name_to_attr - map an LSM attribute name to its ID
* @name: name of the attribute
diff --git a/security/security.c b/security/security.c
index ff6da6735e2a..add46073af0c 100644
--- a/security/security.c
+++ b/security/security.c
@@ -73,6 +73,9 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};
+unsigned int lsm_active_cnt __ro_after_init;
+const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
+
struct lsm_blob_sizes blob_sizes;
struct kmem_cache *lsm_file_cache;
--
2.51.0
^ permalink raw reply related
* [PATCH v4 09/34] lsm: rename the lsm order variables for consistency
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
Rename the builtin_lsm_order variable to lsm_order_builtin,
chosen_lsm_order to lsm_order_cmdline, chosen_major_lsm to
lsm_order_legacy, ordered_lsms[] to lsm_order[], and exclusive
to lsm_exclusive.
This patch also renames the associated kernel command line parsing
functions and adds some basic function comment blocks. The parsing
function choose_major_lsm() was renamed to lsm_choose_security(),
choose_lsm_order() to lsm_choose_lsm(), and enable_debug() to
lsm_debug_enable().
Reviewed-by: Kees Cook <kees@kernel.org>
Reviewed-by: John Johansen <john.johansen@canonical.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
security/lsm_init.c | 86 +++++++++++++++++++++++++--------------------
1 file changed, 48 insertions(+), 38 deletions(-)
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 628f54790360..2e76cefb1585 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -16,14 +16,14 @@ char *lsm_names;
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;
+/* Build and boot-time LSM ordering. */
+static __initconst const char *const lsm_order_builtin = CONFIG_LSM;
+static __initdata const char *lsm_order_cmdline;
+static __initdata const char *lsm_order_legacy;
/* 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 struct lsm_info *lsm_order[MAX_LSM_COUNT + 1];
+static __initdata struct lsm_info *lsm_exclusive;
static __initdata bool debug;
#define init_debug(...) \
@@ -33,7 +33,7 @@ static __initdata bool debug;
} while (0)
#define lsm_order_for_each(iter) \
- for ((iter) = ordered_lsms; *(iter); (iter)++)
+ for ((iter) = lsm_order; *(iter); (iter)++)
#define lsm_for_each_raw(iter) \
for ((iter) = __start_lsm_info; \
(iter) < __end_lsm_info; (iter)++)
@@ -41,31 +41,41 @@ static __initdata bool debug;
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 */
-static int __init choose_major_lsm(char *str)
+/**
+ * lsm_choose_security - Legacy "major" LSM selection
+ * @str: kernel command line parameter
+ */
+static int __init lsm_choose_security(char *str)
{
- chosen_major_lsm = str;
+ lsm_order_legacy = str;
return 1;
}
-__setup("security=", choose_major_lsm);
+__setup("security=", lsm_choose_security);
-/* Explicitly choose LSM initialization order. */
-static int __init choose_lsm_order(char *str)
+/**
+ * lsm_choose_lsm - Modern LSM selection
+ * @str: kernel command line parameter
+ */
+static int __init lsm_choose_lsm(char *str)
{
- chosen_lsm_order = str;
+ lsm_order_cmdline = str;
return 1;
}
-__setup("lsm=", choose_lsm_order);
+__setup("lsm=", lsm_choose_lsm);
-/* Enable LSM order debugging. */
-static int __init enable_debug(char *str)
+/**
+ * lsm_debug_enable - Enable LSM framework debugging
+ * @str: kernel command line parameter
+ *
+ * Currently we only provide debug info during LSM initialization, but we may
+ * want to expand this in the future.
+ */
+static int __init lsm_debug_enable(char *str)
{
debug = true;
return 1;
}
-__setup("lsm.debug", enable_debug);
+__setup("lsm.debug", lsm_debug_enable);
/* Mark an LSM's enabled flag. */
static int lsm_enabled_true __initdata = 1;
@@ -127,7 +137,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
/* Enable this LSM, if it is not already set. */
if (!lsm->enabled)
lsm->enabled = &lsm_enabled_true;
- ordered_lsms[last_lsm] = lsm;
+ lsm_order[last_lsm] = lsm;
lsm_idlist[last_lsm++] = lsm->id;
init_debug("%s ordered: %s (%s)\n", from, lsm->id->name,
@@ -157,7 +167,7 @@ static void __init lsm_prepare(struct lsm_info *lsm)
if (!is_enabled(lsm)) {
set_enabled(lsm, false);
return;
- } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
+ } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) {
init_debug("exclusive disabled: %s\n", lsm->id->name);
set_enabled(lsm, false);
return;
@@ -165,9 +175,9 @@ static void __init lsm_prepare(struct lsm_info *lsm)
/* Mark the LSM as enabled. */
set_enabled(lsm, true);
- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
+ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) {
init_debug("exclusive chosen: %s\n", lsm->id->name);
- exclusive = lsm;
+ lsm_exclusive = lsm;
}
/* Register the LSM blob sizes. */
@@ -226,7 +236,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
}
/* Process "security=", if given. */
- if (chosen_major_lsm) {
+ if (lsm_order_legacy) {
struct lsm_info *major;
/*
@@ -237,10 +247,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
*/
lsm_for_each_raw(major) {
if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
- strcmp(major->id->name, chosen_major_lsm) != 0) {
+ strcmp(major->id->name, lsm_order_legacy) != 0) {
set_enabled(major, false);
init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
- chosen_major_lsm, major->id->name);
+ lsm_order_legacy, major->id->name);
}
}
}
@@ -265,11 +275,11 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
}
/* Process "security=", if given. */
- if (chosen_major_lsm) {
+ if (lsm_order_legacy) {
lsm_for_each_raw(lsm) {
if (exists_ordered_lsm(lsm))
continue;
- if (strcmp(lsm->id->name, chosen_major_lsm) == 0)
+ if (strcmp(lsm->id->name, lsm_order_legacy) == 0)
append_ordered_lsm(lsm, "security=");
}
}
@@ -301,15 +311,15 @@ static void __init lsm_init_ordered(void)
struct lsm_info **lsm;
struct lsm_info *early;
- if (chosen_lsm_order) {
- if (chosen_major_lsm) {
+ if (lsm_order_cmdline) {
+ if (lsm_order_legacy) {
pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
- chosen_major_lsm, chosen_lsm_order);
- chosen_major_lsm = NULL;
+ lsm_order_legacy, lsm_order_cmdline);
+ lsm_order_legacy = NULL;
}
- ordered_lsm_parse(chosen_lsm_order, "cmdline");
+ ordered_lsm_parse(lsm_order_cmdline, "cmdline");
} else
- ordered_lsm_parse(builtin_lsm_order, "builtin");
+ ordered_lsm_parse(lsm_order_builtin, "builtin");
lsm_order_for_each(lsm) {
lsm_prepare(*lsm);
@@ -473,9 +483,9 @@ 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*");
+ init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*");
+ init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin);
+ init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*");
/*
* Append the names of the early LSM modules now that kmalloc() is
--
2.51.0
^ permalink raw reply related
* [PATCH v4 08/34] lsm: replace the name field with a pointer to the lsm_id struct
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
Reduce the duplication between the lsm_id struct and the DEFINE_LSM()
definition by linking the lsm_id struct directly into the individual
LSM's DEFINE_LSM() instance.
Linking the lsm_id into the LSM definition also allows us to simplify
the security_add_hooks() function by removing the code which populates
the lsm_idlist[] array and moving it into the normal LSM startup code
where the LSM list is parsed and the individual LSMs are enabled,
making for a cleaner implementation with less overhead at boot.
Reviewed-by: Kees Cook <kees@kernel.org>
Reviewed-by: John Johansen <john.johansen@canonical.com>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
include/linux/lsm_hooks.h | 2 +-
security/apparmor/lsm.c | 2 +-
security/bpf/hooks.c | 2 +-
security/commoncap.c | 2 +-
security/integrity/evm/evm_main.c | 2 +-
security/integrity/ima/ima_main.c | 2 +-
security/ipe/ipe.c | 2 +-
security/landlock/setup.c | 2 +-
security/loadpin/loadpin.c | 2 +-
security/lockdown/lockdown.c | 2 +-
security/lsm_init.c | 45 +++++++++++++------------------
security/safesetid/lsm.c | 2 +-
security/selinux/hooks.c | 2 +-
security/smack/smack_lsm.c | 2 +-
security/tomoyo/tomoyo.c | 2 +-
security/yama/yama_lsm.c | 2 +-
16 files changed, 33 insertions(+), 42 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 0112926ed923..7343dd60b1d5 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -152,7 +152,7 @@ enum lsm_order {
};
struct lsm_info {
- const char *name; /* Required. */
+ const struct lsm_id *id;
enum lsm_order order; /* Optional: default is LSM_ORDER_MUTABLE */
unsigned long flags; /* Optional: flags describing LSM */
int *enabled; /* Optional: controlled by CONFIG_LSM */
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 8e1cc229b41b..45b3a304d525 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -2552,7 +2552,7 @@ static int __init apparmor_init(void)
}
DEFINE_LSM(apparmor) = {
- .name = "apparmor",
+ .id = &apparmor_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.enabled = &apparmor_enabled,
.blobs = &apparmor_blob_sizes,
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index db759025abe1..40efde233f3a 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -33,7 +33,7 @@ struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
};
DEFINE_LSM(bpf) = {
- .name = "bpf",
+ .id = &bpf_lsmid,
.init = bpf_lsm_init,
.blobs = &bpf_lsm_blob_sizes
};
diff --git a/security/commoncap.c b/security/commoncap.c
index 6bd4adeb4795..b50479bd0286 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1505,7 +1505,7 @@ static int __init capability_init(void)
}
DEFINE_LSM(capability) = {
- .name = "capability",
+ .id = &capability_lsmid,
.order = LSM_ORDER_FIRST,
.init = capability_init,
};
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0add782e73ba..db8e324ed4e6 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1175,7 +1175,7 @@ struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
};
DEFINE_LSM(evm) = {
- .name = "evm",
+ .id = &evm_lsmid,
.init = init_evm_lsm,
.order = LSM_ORDER_LAST,
.blobs = &evm_blob_sizes,
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index cdd225f65a62..eade8e1e3cb1 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1279,7 +1279,7 @@ struct lsm_blob_sizes ima_blob_sizes __ro_after_init = {
};
DEFINE_LSM(ima) = {
- .name = "ima",
+ .id = &ima_lsmid,
.init = init_ima_lsm,
.order = LSM_ORDER_LAST,
.blobs = &ima_blob_sizes,
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 4317134cb0da..2426441181dc 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -92,7 +92,7 @@ static int __init ipe_init(void)
}
DEFINE_LSM(ipe) = {
- .name = "ipe",
+ .id = &ipe_lsmid,
.init = ipe_init,
.blobs = &ipe_blobs,
};
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index bd53c7a56ab9..47dac1736f10 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -75,7 +75,7 @@ static int __init landlock_init(void)
}
DEFINE_LSM(LANDLOCK_NAME) = {
- .name = LANDLOCK_NAME,
+ .id = &landlock_lsmid,
.init = landlock_init,
.blobs = &landlock_blob_sizes,
};
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 68252452b66c..b9ddf05c5c16 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -271,7 +271,7 @@ static int __init loadpin_init(void)
}
DEFINE_LSM(loadpin) = {
- .name = "loadpin",
+ .id = &loadpin_lsmid,
.init = loadpin_init,
};
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index cf83afa1d879..4813f168ff93 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -168,6 +168,6 @@ DEFINE_EARLY_LSM(lockdown) = {
#else
DEFINE_LSM(lockdown) = {
#endif
- .name = "lockdown",
+ .id = &lockdown_lsmid,
.init = lockdown_lsm_init,
};
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 4a108b03c23d..628f54790360 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -127,9 +127,10 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
/* Enable this LSM, if it is not already set. */
if (!lsm->enabled)
lsm->enabled = &lsm_enabled_true;
- ordered_lsms[last_lsm++] = lsm;
+ ordered_lsms[last_lsm] = lsm;
+ lsm_idlist[last_lsm++] = lsm->id;
- init_debug("%s ordered: %s (%s)\n", from, lsm->name,
+ init_debug("%s ordered: %s (%s)\n", from, lsm->id->name,
is_enabled(lsm) ? "enabled" : "disabled");
}
@@ -157,7 +158,7 @@ static void __init lsm_prepare(struct lsm_info *lsm)
set_enabled(lsm, false);
return;
} else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
- init_debug("exclusive disabled: %s\n", lsm->name);
+ init_debug("exclusive disabled: %s\n", lsm->id->name);
set_enabled(lsm, false);
return;
}
@@ -165,7 +166,7 @@ static void __init lsm_prepare(struct lsm_info *lsm)
/* Mark the LSM as enabled. */
set_enabled(lsm, true);
if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
- init_debug("exclusive chosen: %s\n", lsm->name);
+ init_debug("exclusive chosen: %s\n", lsm->id->name);
exclusive = lsm;
}
@@ -200,9 +201,9 @@ static void __init initialize_lsm(struct lsm_info *lsm)
if (is_enabled(lsm)) {
int ret;
- init_debug("initializing %s\n", lsm->name);
+ init_debug("initializing %s\n", lsm->id->name);
ret = lsm->init();
- WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
+ WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret);
}
}
@@ -236,10 +237,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
*/
lsm_for_each_raw(major) {
if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
- strcmp(major->name, chosen_major_lsm) != 0) {
+ strcmp(major->id->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);
+ chosen_major_lsm, major->id->name);
}
}
}
@@ -251,7 +252,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
bool found = false;
lsm_for_each_raw(lsm) {
- if (strcmp(lsm->name, name) == 0) {
+ if (strcmp(lsm->id->name, name) == 0) {
if (lsm->order == LSM_ORDER_MUTABLE)
append_ordered_lsm(lsm, origin);
found = true;
@@ -268,7 +269,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
lsm_for_each_raw(lsm) {
if (exists_ordered_lsm(lsm))
continue;
- if (strcmp(lsm->name, chosen_major_lsm) == 0)
+ if (strcmp(lsm->id->name, chosen_major_lsm) == 0)
append_ordered_lsm(lsm, "security=");
}
}
@@ -285,7 +286,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
continue;
set_enabled(lsm, false);
init_debug("%s skipped: %s (not in requested order)\n",
- origin, lsm->name);
+ origin, lsm->id->name);
}
kfree(sep);
@@ -317,11 +318,13 @@ static void __init lsm_init_ordered(void)
pr_info("initializing lsm=");
lsm_early_for_each_raw(early) {
if (is_enabled(early))
- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
+ pr_cont("%s%s",
+ first++ == 0 ? "" : ",", early->id->name);
}
lsm_order_for_each(lsm) {
if (is_enabled(*lsm))
- pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
+ pr_cont("%s%s",
+ first++ == 0 ? "" : ",", (*lsm)->id->name);
}
pr_cont("\n");
@@ -432,18 +435,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
{
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]);
@@ -491,10 +482,10 @@ int __init security_init(void)
* available
*/
lsm_early_for_each_raw(lsm) {
- init_debug(" early started: %s (%s)\n", lsm->name,
+ init_debug(" early started: %s (%s)\n", lsm->id->name,
is_enabled(lsm) ? "enabled" : "disabled");
if (lsm->enabled)
- lsm_append(lsm->name, &lsm_names);
+ lsm_append(lsm->id->name, &lsm_names);
}
/* Load LSMs in specified order. */
diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c
index 1ba564f097f5..9a7c68d4e642 100644
--- a/security/safesetid/lsm.c
+++ b/security/safesetid/lsm.c
@@ -287,6 +287,6 @@ static int __init safesetid_security_init(void)
}
DEFINE_LSM(safesetid_security_init) = {
+ .id = &safesetid_lsmid,
.init = safesetid_security_init,
- .name = "safesetid",
};
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4da5e792b42e..d94b1ff316ba 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7630,7 +7630,7 @@ void selinux_complete_init(void)
/* SELinux requires early initialization in order to label
all processes and objects when they are created. */
DEFINE_LSM(selinux) = {
- .name = "selinux",
+ .id = &selinux_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.enabled = &selinux_enabled_boot,
.blobs = &selinux_blob_sizes,
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fc340a6f0dde..e09490c75f59 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -5275,7 +5275,7 @@ static __init int smack_init(void)
* all processes and objects when they are created.
*/
DEFINE_LSM(smack) = {
- .name = "smack",
+ .id = &smack_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.blobs = &smack_blob_sizes,
.init = smack_init,
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index d6ebcd9db80a..ed0f7b052a85 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -612,7 +612,7 @@ static int __init tomoyo_init(void)
}
DEFINE_LSM(tomoyo) = {
- .name = "tomoyo",
+ .id = &tomoyo_lsmid,
.enabled = &tomoyo_enabled,
.flags = LSM_FLAG_LEGACY_MAJOR,
.blobs = &tomoyo_blob_sizes,
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 3d064dd4e03f..38b21ee0c560 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -476,6 +476,6 @@ static int __init yama_init(void)
}
DEFINE_LSM(yama) = {
- .name = "yama",
+ .id = &yama_lsmid,
.init = yama_init,
};
--
2.51.0
^ permalink raw reply related
* [PATCH v4 07/34] lsm: rename ordered_lsm_init() to lsm_init_ordered()
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
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.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.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 a8b82329c76a..4a108b03c23d 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -291,7 +291,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;
@@ -342,9 +345,6 @@ static void __init ordered_lsm_init(void)
init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
- /*
- * 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,
@@ -498,7 +498,7 @@ int __init security_init(void)
}
/* Load LSMs in specified order. */
- ordered_lsm_init();
+ lsm_init_ordered();
return 0;
}
--
2.51.0
^ permalink raw reply related
* [PATCH v4 06/34] lsm: integrate lsm_early_cred() and lsm_early_task() into caller
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
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.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.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 09afa7ad719e..a8b82329c76a 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -291,34 +291,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;
@@ -382,8 +354,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);
}
--
2.51.0
^ permalink raw reply related
* [PATCH v4 05/34] lsm: integrate report_lsm_order() code into caller
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
With only one caller of report_lsm_order(), insert the function's code
directly into the caller and ger rid of report_lsm_order().
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
security/lsm_init.c | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 18828a65c364..09afa7ad719e 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -291,26 +291,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
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. */
- lsm_early_for_each_raw(early) {
- if (is_enabled(early))
- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
- }
- lsm_order_for_each(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
@@ -341,7 +321,9 @@ static void __init lsm_early_task(struct task_struct *task)
static void __init ordered_lsm_init(void)
{
+ unsigned int first = 0;
struct lsm_info **lsm;
+ struct lsm_info *early;
if (chosen_lsm_order) {
if (chosen_major_lsm) {
@@ -357,7 +339,16 @@ static void __init ordered_lsm_init(void)
lsm_prepare(*lsm);
}
- report_lsm_order();
+ pr_info("initializing lsm=");
+ lsm_early_for_each_raw(early) {
+ if (is_enabled(early))
+ pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
+ }
+ lsm_order_for_each(lsm) {
+ if (is_enabled(*lsm))
+ pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
+ }
+ pr_cont("\n");
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
--
2.51.0
^ permalink raw reply related
* [PATCH v4 04/34] lsm: introduce looping macros for the initialization code
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
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 chance for errors.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.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 6f40ab1d2f54..18828a65c364 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;
}
@@ -209,7 +219,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");
}
@@ -224,8 +234,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);
@@ -241,7 +250,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);
@@ -256,7 +265,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)
@@ -265,13 +274,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);
@@ -290,13 +299,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");
}
@@ -343,8 +353,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();
@@ -382,8 +393,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)
@@ -485,7 +497,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);
@@ -512,7 +524,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)
--
2.51.0
^ permalink raw reply related
* [PATCH v4 03/34] lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare()
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
Simplify and consolidate the lsm_allowed() and prepare_lsm() functions
into a new function, lsm_prepare().
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: John Johansen <john.johhansen@canonical.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
security/lsm_init.c | 109 +++++++++++++++++++-------------------------
1 file changed, 46 insertions(+), 63 deletions(-)
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 124213b906af..6f40ab1d2f54 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,54 +135,53 @@ 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);
- lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
- lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
- lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
-}
-
-/* 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);
+ lsm_set_blob_size(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
+ lsm_set_blob_size(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
+ lsm_set_blob_size(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
}
/* Initialize a given LSM, if it is enabled. */
@@ -361,7 +344,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();
@@ -505,7 +488,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);
}
--
2.51.0
^ permalink raw reply related
* [PATCH v4 02/34] lsm: split the init code out into lsm_init.c
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
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>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
include/linux/lsm_hooks.h | 3 +-
security/Makefile | 2 +-
security/lsm.h | 22 ++
security/lsm_init.c | 543 ++++++++++++++++++++++++++++++++++
security/security.c | 597 +++-----------------------------------
5 files changed, 601 insertions(+), 566 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 79ec5a2bdcca..0112926ed923 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -170,11 +170,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..124213b906af
--- /dev/null
+++ b/security/lsm_init.c
@@ -0,0 +1,543 @@
+// 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);
+ lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
+ lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
+ lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
+}
+
+/* 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);
+ init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map);
+ init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
+ init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
+
+ /*
+ * 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 8cb049bebc57..ff6da6735e2a 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,496 +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);
- lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
- lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
- lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
-}
-
-/* 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);
- init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map);
- init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
- init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
-
- /*
- * 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;
}
/**
@@ -679,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
@@ -749,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);
}
@@ -851,20 +336,6 @@ static int lsm_bpf_token_alloc(struct bpf_token *token)
}
#endif /* CONFIG_BPF_SYSCALL */
-/**
- * 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
--
2.51.0
^ permalink raw reply related
* [PATCH v4 01/34] lsm: split the notifier code out into lsm_notifier.c
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
In-Reply-To: <20250916220355.252592-36-paul@paul-moore.com>
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>
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.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 ca126b02d2fe..8cb049bebc57 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;
@@ -649,27 +647,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
--
2.51.0
^ permalink raw reply related
* [PATCH v4 0/34] Rework the LSM initialization
From: Paul Moore @ 2025-09-16 22:03 UTC (permalink / raw)
To: 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, Casey Schaufler, Tetsuo Handa, Nicolas Bouchinet,
Xiu Jianfeng
This is the fourth revision of the LSM initialization rework patchset.
The number of changes between this revision and the last are limited to
a rework of the lsm_read() function and the replacement of my IMA/EVM
patch with one from Roberto; relatively minor things given the scope of
the patchset.
I've run the v4 patchset through some basic testing today and everything
looks okay to me; I'll continue to play with it, but I wanted to get this
out onto the list sooner rather than later. Assuming Mimi is okay with
Roberto's patch, and no other issues are found, I would expect this to go
into the LSM tree after the upcoming merge window closes.
For those of you having problems pulling this patchset into your own
local trees for review/testing, this patchset can be found in the
working-lsm_init_rework branch of the main LSM tree:
https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm.git/log/?h=working-lsm_init_rework
The RFC/v3 patchset:
https://lore.kernel.org/linux-security-module/20250814225159.275901-36-paul@paul-moore.com/
The RFC/v2 patchset:
https://lore.kernel.org/linux-security-module/20250721232142.77224-36-paul@paul-moore.com/
The RFC/v1 patchset is below, the cover letter provides some background
and motivation for this series which still applies:
https://lore.kernel.org/linux-security-module/20250409185019.238841-31-paul@paul-moore.com/
CHANGELOG
v4:
- reworked the lsm_read() function (John, Roberto, Tetsuo)
- replaced the IMA/EVM patch with one from Roberto
RFC/v3:
- rebased to lsm/dev branch
- fixed IMA/EVM initcall comment (Roberto)
- fixed CONFIG_IMA and CONFIG_EVM problems (Nicolas, Roberto)
- fixed CONFIG_SECURITY_SMACK_NETFILTER problems (Roberto)
- fixed the IMA/EVM header file include macro protections
- fixed an off-by-one string length issue in lsm_read() (Casey)
RFC/v2:
- rename lsm_prep_single() to lsm_prepare()
- drop the lsm_prop counting patch
- drop the platform_certs changes from the IMA/EVM patch (Mimi)
- split/reorder anough patches in the patchset that I lost track
- added missing function comment blocks in the SELinux patches
- split patch 04/29 into smaller patches (Kees)
- fix an LSM list output problem in an intermediate patch (Kees)
- preserve the "lsm_active_cnt" variable name (Casey)
- cache the lsm_read() string (Kees)
- squashed, split, and reordered the enabled/ordering patches
- reworked the Smack patch (Casey)
- conditionalized the SELinux IB init code (Stephen)
- fixed missing Smack "__init" annotation (Fan)
- fixed a potential unused variable warning in IMA/EVM (John)
- fixed the placeholder commit descriptions (various)
RFC/v1:
- initial version
--
Paul Moore (33):
lsm: split the notifier code out into lsm_notifier.c
lsm: split the init code out into lsm_init.c
lsm: consolidate lsm_allowed() and prepare_lsm() into
lsm_prepare()
lsm: introduce looping macros for the initialization code
lsm: integrate report_lsm_order() code into caller
lsm: integrate lsm_early_cred() and lsm_early_task() into caller
lsm: rename ordered_lsm_init() to lsm_init_ordered()
lsm: replace the name field with a pointer to the lsm_id struct
lsm: rename the lsm order variables for consistency
lsm: rework lsm_active_cnt and lsm_idlist[]
lsm: get rid of the lsm_names list and do some cleanup
lsm: rework the LSM enable/disable setter/getter functions
lsm: rename exists_ordered_lsm() to lsm_order_exists()
lsm: rename/rework append_ordered_lsm() into lsm_order_append()
lsm: rename/rework ordered_lsm_parse() to lsm_order_parse()
lsm: cleanup the LSM blob size code
lsm: cleanup initialize_lsm() and rename to lsm_init_single()
lsm: fold lsm_init_ordered() into security_init()
lsm: add/tweak function header comment blocks in lsm_init.c
lsm: cleanup the debug and console output in lsm_init.c
lsm: output available LSMs when debugging
lsm: group lsm_order_parse() with the other lsm_order_*()
functions
lsm: introduce an initcall mechanism into the LSM framework
loadpin: move initcalls to the LSM framework
ipe: move initcalls to the LSM framework
smack: move initcalls to the LSM framework
tomoyo: move initcalls to the LSM framework
safesetid: move initcalls to the LSM framework
apparmor: move initcalls to the LSM framework
lockdown: move initcalls to the LSM framework
selinux: move initcalls to the LSM framework
lsm: consolidate all of the LSM framework initcalls
lsm: add a LSM_STARTED_ALL notification event
Roberto Sassu (1):
ima,evm: move initcalls to the LSM framework
include/linux/lsm_hooks.h | 73 +-
include/linux/security.h | 3
security/Makefile | 2
security/apparmor/apparmorfs.c | 4
security/apparmor/crypto.c | 3
security/apparmor/include/apparmorfs.h | 2
security/apparmor/include/crypto.h | 1
security/apparmor/lsm.c | 11
security/bpf/hooks.c | 2
security/commoncap.c | 2
security/inode.c | 46 +
security/integrity/evm/evm_main.c | 5
security/integrity/evm/evm_secfs.c | 11
security/integrity/iint.c | 14
security/integrity/ima/ima_fs.c | 11
security/integrity/ima/ima_main.c | 6
security/integrity/integrity.h | 2
security/ipe/fs.c | 4
security/ipe/ipe.c | 3
security/ipe/ipe.h | 2
security/landlock/setup.c | 2
security/loadpin/loadpin.c | 15
security/lockdown/lockdown.c | 5
security/lsm.h | 42 +
security/lsm_init.c | 563 ++++++++++++++++++++++
security/lsm_notifier.c | 31 +
security/lsm_syscalls.c | 2
security/min_addr.c | 5
security/safesetid/lsm.c | 3
security/safesetid/lsm.h | 2
security/safesetid/securityfs.c | 3
security/security.c | 623 +------------------------
security/selinux/Makefile | 2
security/selinux/hooks.c | 11
security/selinux/ibpkey.c | 5
security/selinux/include/audit.h | 9
security/selinux/include/initcalls.h | 19
security/selinux/initcalls.c | 52 ++
security/selinux/netif.c | 5
security/selinux/netlink.c | 5
security/selinux/netnode.c | 5
security/selinux/netport.c | 5
security/selinux/selinuxfs.c | 5
security/selinux/ss/services.c | 26 -
security/smack/smack.h | 14
security/smack/smack_lsm.c | 11
security/smack/smack_netfilter.c | 4
security/smack/smackfs.c | 4
security/tomoyo/common.h | 2
security/tomoyo/securityfs_if.c | 4
security/tomoyo/tomoyo.c | 3
security/yama/yama_lsm.c | 2
52 files changed, 984 insertions(+), 712 deletions(-)
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Christian Schoenebeck @ 2025-09-16 19:22 UTC (permalink / raw)
To: Dominique Martinet, Tingmao Wang
Cc: Mickaël Salaün, Eric Van Hensbergen, Latchesar Ionkov,
v9fs, Günther Noack, linux-security-module, Jan Kara,
Amir Goldstein, Matthew Bobrowski, Al Viro, Christian Brauner,
linux-fsdevel
In-Reply-To: <a98c14f5-4b28-4f7b-86a2-94e3d66bbf26@maowtm.org>
On Tuesday, September 16, 2025 4:01:40 PM CEST Tingmao Wang wrote:
> On 9/16/25 14:35, Dominique Martinet wrote:
> > Tingmao Wang wrote on Tue, Sep 16, 2025 at 01:44:27PM +0100:
> >> [...]
> >>
> >> Note that in discussion with Mickaël (maintainer of Landlock) he
> >> indicated
> >> that he would be comfortable for Landlock to track a qid, instead of
> >> holding a inode, specifically for 9pfs.
> >
> > Yes, I saw that, but what you pointed out about qid reuse make me
> > somewhat uncomfortable with that direction -- you could allow a
> > directory, delete it, create a new one somewhere else and if the
> > underlying fs reuse the same inode number the rule would allow an
> > intended directory instead so I'd rather not rely on qid for this
> > either.
> > But if you think that's not a problem in practice (because e.g. landlock
> > would somehow detect the dir got deleted or another good reason it's not
> > a problem) then I agree it's probably the simplest way forward
> > implementation-wise.
>
> Sorry, I forgot to add that this idea would also involve Landlock holding
> a reference to the fid (or dentry, but that's problematic due to breaking
> unmount unless we can have a new hook) to keep the file open on the host
> side so that the qid won't be reused (ignoring collisions caused by
> different filesystems mounted under one 9pfs export when multidev mapping
> is not enabled)
I see that you are proposing an option for your proposed qid based re-using of
dentries. I don't think it should be on by default though, considering what we
already discussed (e.g. inodes recycled by ext4, but also not all 9p servers
handling inode collisions).
> (There's the separate issue of QEMU not seemingly keeping a directory open
> on the host when the guest has a fid to it tho. I checked that if the dir
> is renamed on the host side, any process in the guest that has a fd to it
> (checked via cd in a shell) will not be able to use that fd to read it
> anymore. This also means that another directory might be created with the
> same qid.path)
For all open FIDs QEMU retains a descriptor to the file/directory.
Which 9p message do you see sent to server, Trename or Trenameat?
Does this always happen to you or just sometimes, i.e. under heavy load?
Because even though QEMU retains descriptors of open FIDs; when the QEMU
process approaches host system's max. allowed number of open file descriptors
then v9fs_reclaim_fd() [hw/9pfs/9p.c] is called, which closes some descriptors
of older FIDs to (at least) keep the QEMU process alive.
BTW: to prevent these descriptor reclaims to happen too often, I plan to do
what many other files servers do: asking the host system on process start to
increase the max. number of file descriptors.
/Christian
^ permalink raw reply
* Re: [PATCH v3 09/14] cgroup/cpuset: Remove redundant rcu_read_lock/unlock() in spin_lock
From: Tejun Heo @ 2025-09-16 18:37 UTC (permalink / raw)
To: pengdonglin
Cc: tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees, bigeasy, hdanton, paulmck, linux-kernel,
linux-rt-devel, linux-nfs, linux-aio, linux-fsdevel,
linux-security-module, netdev, intel-gfx, linux-wireless,
linux-acpi, linux-s390, cgroups, Johannes Weiner, pengdonglin
In-Reply-To: <20250916044735.2316171-10-dolinux.peng@gmail.com>
On Tue, Sep 16, 2025 at 12:47:30PM +0800, pengdonglin wrote:
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
> there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
> rcu_read_lock_sched() in terms of RCU read section and the relevant grace
> period. That means that spin_lock(), which implies rcu_read_lock_sched(),
> also implies rcu_read_lock().
>
> There is no need no explicitly start a RCU read section if one has already
> been started implicitly by spin_lock().
>
> Simplify the code and remove the inner rcu_read_lock() invocation.
>
> Cc: Waiman Long <longman@redhat.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Acked-by: Waiman Long <longman@redhat.com>
> Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
Applied to cgroup/for-6.18.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH v3 08/14] cgroup: Remove redundant rcu_read_lock/unlock() in spin_lock
From: Tejun Heo @ 2025-09-16 18:37 UTC (permalink / raw)
To: pengdonglin
Cc: tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees, bigeasy, hdanton, paulmck, linux-kernel,
linux-rt-devel, linux-nfs, linux-aio, linux-fsdevel,
linux-security-module, netdev, intel-gfx, linux-wireless,
linux-acpi, linux-s390, cgroups, Johannes Weiner, pengdonglin
In-Reply-To: <20250916044735.2316171-9-dolinux.peng@gmail.com>
On Tue, Sep 16, 2025 at 12:47:29PM +0800, pengdonglin wrote:
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
> there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
> rcu_read_lock_sched() in terms of RCU read section and the relevant grace
> period. That means that spin_lock(), which implies rcu_read_lock_sched(),
> also implies rcu_read_lock().
>
> There is no need no explicitly start a RCU read section if one has already
> been started implicitly by spin_lock().
>
> Simplify the code and remove the inner rcu_read_lock() invocation.
>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Waiman Long <longman@redhat.com>
> Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
Applied to cgroup/for-6.18.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH] memfd,selinux: call security_inode_init_security_anon
From: Paul Moore @ 2025-09-16 15:26 UTC (permalink / raw)
To: Hugh Dickins, Thiébaud Weksteen
Cc: James Morris, Stephen Smalley, Jeff Vander Stoep, Nick Kralevich,
Jeff Xu, Baolin Wang, linux-kernel, linux-security-module,
selinux, linux-mm
In-Reply-To: <92255d5e-7e0a-6ca3-3169-114ae7f6247f@google.com>
On Tue, Sep 16, 2025 at 1:07 AM Hugh Dickins <hughd@google.com> wrote:
> On Wed, 3 Sep 2025, Paul Moore wrote:
> > On Aug 25, 2025 "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" <tweek@google.com> wrote:
> > >
> > > Prior to this change, no security hooks were called at the creation of a
> > > memfd file. It means that, for SELinux as an example, it will receive
> > > the default type of the filesystem that backs the in-memory inode. In
> > > most cases, that would be tmpfs, but if MFD_HUGETLB is passed, it will
> > > be hugetlbfs. Both can be considered implementation details of memfd.
> > >
> > > It also means that it is not possible to differentiate between a file
> > > coming from memfd_create and a file coming from a standard tmpfs mount
> > > point.
> > >
> > > Additionally, no permission is validated at creation, which differs from
> > > the similar memfd_secret syscall.
> > >
> > > Call security_inode_init_security_anon during creation. This ensures
> > > that the file is setup similarly to other anonymous inodes. On SELinux,
> > > it means that the file will receive the security context of its task.
> > >
> > > The ability to limit fexecve on memfd has been of interest to avoid
> > > potential pitfalls where /proc/self/exe or similar would be executed
> > > [1][2]. Reuse the "execute_no_trans" and "entrypoint" access vectors,
> > > similarly to the file class. These access vectors may not make sense for
> > > the existing "anon_inode" class. Therefore, define and assign a new
> > > class "memfd_file" to support such access vectors.
> > >
> > > Guard these changes behind a new policy capability named "memfd_class".
> > >
> > > [1] https://crbug.com/1305267
> > > [2] https://lore.kernel.org/lkml/20221215001205.51969-1-jeffxu@google.com/
> > >
> > > Signed-off-by: Thiébaud Weksteen <tweek@google.com>
> > > Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> > > Tested-by: Stephen Smalley <stephen.smalley.work@gmail.com>
...
> > Hugh, Baolin, and shmem/mm folks, are you okay with the changes above? If
> > so it would be nice to get an ACK from one of you.
>
> So far as I can tell, seems okay to me:
> Acked-by: Hugh Dickins <hughd@google.com>
>
> If I'd responded earlier (sorry), I would have asked for it just to use
> &QSTR("[memfd]") directly in the call, rather than indirecting through
> unnecessary #define MEMFD_ANON_NAME "[memfd]"; never mind, that's all.
>
> Please do take this, along with the rest, through your security tree:
> mm.git contains no conflicting change to mm/memfd.c at present.
Thanks Hugh, it turns out we ended up having a discussion on the
SELinux side (proper return values for error conditions) and I'm going
to hold off on this until after the upcoming merge window to give time
for that discussion to run its course. The good news is that gives
Thiébaud an opportunity to do the qstr fixup you wanted.
Thiébaud, are you okay with making the change Hugh has requested?
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Tingmao Wang @ 2025-09-16 14:01 UTC (permalink / raw)
To: Dominique Martinet, Christian Schoenebeck
Cc: Mickaël Salaün, Eric Van Hensbergen, Latchesar Ionkov,
v9fs, Günther Noack, linux-security-module, Jan Kara,
Amir Goldstein, Matthew Bobrowski, Al Viro, Christian Brauner,
linux-fsdevel
In-Reply-To: <aMlnpz7TrbXuL0mc@codewreck.org>
On 9/16/25 14:35, Dominique Martinet wrote:
> Tingmao Wang wrote on Tue, Sep 16, 2025 at 01:44:27PM +0100:
>> [...]
>>
>> Note that in discussion with Mickaël (maintainer of Landlock) he indicated
>> that he would be comfortable for Landlock to track a qid, instead of
>> holding a inode, specifically for 9pfs.
>
> Yes, I saw that, but what you pointed out about qid reuse make me
> somewhat uncomfortable with that direction -- you could allow a
> directory, delete it, create a new one somewhere else and if the
> underlying fs reuse the same inode number the rule would allow an
> intended directory instead so I'd rather not rely on qid for this
> either.
> But if you think that's not a problem in practice (because e.g. landlock
> would somehow detect the dir got deleted or another good reason it's not
> a problem) then I agree it's probably the simplest way forward
> implementation-wise.
>
Sorry, I forgot to add that this idea would also involve Landlock holding
a reference to the fid (or dentry, but that's problematic due to breaking
unmount unless we can have a new hook) to keep the file open on the host
side so that the qid won't be reused (ignoring collisions caused by
different filesystems mounted under one 9pfs export when multidev mapping
is not enabled)
(There's the separate issue of QEMU not seemingly keeping a directory open
on the host when the guest has a fid to it tho. I checked that if the dir
is renamed on the host side, any process in the guest that has a fd to it
(checked via cd in a shell) will not be able to use that fd to read it
anymore. This also means that another directory might be created with the
same qid.path)
(I've not looked at the code yet but Christian, feel free to point out if
I missed anything or if you disagree :) didn't realize earlier you're also
the recent author of the 9pfs server in QEMU)
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Christian Schoenebeck @ 2025-09-16 13:43 UTC (permalink / raw)
To: Dominique Martinet, Tingmao Wang
Cc: Mickaël Salaün, Eric Van Hensbergen, Latchesar Ionkov,
v9fs, Günther Noack, linux-security-module, Jan Kara,
Amir Goldstein, Matthew Bobrowski, Al Viro, Christian Brauner,
linux-fsdevel
In-Reply-To: <6502db0c-17ed-4644-a744-bb0553174541@maowtm.org>
On Tuesday, September 16, 2025 2:44:27 PM CEST Tingmao Wang wrote:
> On 9/16/25 00:31, Dominique Martinet wrote:
[...]
> >> I tried mounting a qemu-exported 9pfs backed on ext4, with
> >> multidevs=remap, and created a file, used stat to note its inode number,
> >> deleted the file, created another file (of the same OR different name),
> >> and that new file will have the same inode number.
> >>
> >> (If I don't delete the file, then a newly created file would of course
> >> have a different ext4 inode number, and in that case QEMU exposes a
> >> different qid)
> >
> > Ok so from Christian's reply this is just ext4 reusing the same inode..
> > I briefly hinted at this above, but in this case ext4 will give the
> > inode a different generation number (so the ext4 file handle will be
> > different, and accessing the old one will get ESTALE); but that's not
> > something qemu currently tracks and it'd be a bit of an overhaul...
> > In theory qemu could hash mount_id + file handle to get a properly
> > unique qid, if we need to improve that, but that'd be limited to root
> > users (and to filesystems that support name_to_handle_at) so I don't
> > think it's really appropriate either... hmm..
>
> Actually I think I forgot that there is also qid.version, which in the
> case of a QEMU-exported 9pfs might just be the file modification time? In
> 9pfs currently we do reject a inode match if that version changed server
> side in cached mode:
>
> v9fs_test_inode_dotl:
> /* compare qid details */
> if (memcmp(&v9inode->qid.version,
> &st->qid.version, sizeof(v9inode->qid.version)))
> return 0;
>
> (not tested whether QEMU correctly sets this version yet)
Define "correctly". ;-) QEMU sets it like this since 2010:
qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
https://github.com/qemu/qemu/blob/190d5d7fd725ff754f94e8e0cbfb69f279c82b5d/hw/9pfs/9p.c#L1020
/Christian
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Dominique Martinet @ 2025-09-16 13:35 UTC (permalink / raw)
To: Tingmao Wang
Cc: Christian Schoenebeck, Mickaël Salaün,
Eric Van Hensbergen, Latchesar Ionkov, v9fs, Günther Noack,
linux-security-module, Jan Kara, Amir Goldstein,
Matthew Bobrowski, Al Viro, Christian Brauner, linux-fsdevel
In-Reply-To: <6502db0c-17ed-4644-a744-bb0553174541@maowtm.org>
Tingmao Wang wrote on Tue, Sep 16, 2025 at 01:44:27PM +0100:
> > iirc even in cacheless mode 9p should keep inode arounds if there's an
> > open fd somewhere
>
> Yes, because there is a dentry that has a reference to it. Similarly if
> there is a Landlock rule referencing it, the inode will also be kept
> around (but not the dentry, Landlock only references the inode). The
> problem is that when another application (that is being Landlocked)
> accesses the file, 9pfs will create a new inode in uncached mode,
> regardless of whether an existing inode exists.
> [...]
> Based on my understanding, I think this isn't really to do with whether
> the dentry is around or not. In cached mode, 9pfs will use iget5_locked
> to look up an existing inode based on the qid, if one exists, and use
> that, even if no cached dentry points to it. However, in uncached mode,
> currently if vfs asks 9pfs to find an inode (e.g. because the dentry is no
> longer in cache), it always get a new one:
> [...]
> v9fs_qid_iget_dotl:
> ...
> if (new)
> test = v9fs_test_new_inode_dotl;
> else
> test = v9fs_test_inode_dotl;
Right, if we get all the way to iget uncached mode will get a new inode,
but if the file is opened (I tried `cat > foo` and `tail -f foo`) then
re-opening cat will not issue a lookup at all -- v9fs_vfs_lookup() is
not called in the first place.
Likewise, in cached mode, just having the file in cache makes new open
not call v9fs_vfs_lookup for that file (even if it's not currently
open), so that `if (new)` is not actually what matters here afaiu.
What's the condition to make it happen? Can we make that happen with
landlock?
In practice that'd make landlock partially negate cacheless mode, as
we'd "pin" landlocked paths, but as long as readdirs aren't cached and
other metadata is refreshed on e.g. stat() calls I think that's fine
if we can make it happen.
(That's a big if)
> > So in cacheless mode, if you have a tree like this:
> > a
> > └── b
> > ├── c
> > └── d
> > with c 'open' (or a reference held by landlock), then dentries for a/b/c
> > would be kept, but d could be droppable?
>
> I think, based on my understanding, a child dentry does always have a
> reference to its parent, and so parent won't be dropped before child, if
> child dentry is alive.
I'd be tempted to agree here
> However holding a proper dentry reference in an
> inode might be tricky as dentry holds the reference to its inode.
Hmm, yeah, that's problematic.
Could it be held in landlock and not by the inode?
Just thinking out loud.
> > My understanding is that in cacheless mode we're dropping dentries
> > aggressively so that things like readdir() are refreshed, but I'm
> > thinking this should work even if we keep some dentries alive when their
> > inode is held up.
>
> If we have some way of keeping the dentry alive (without introducing
> circular reference problems) then I guess that would work and we don't
> have to track paths ourselves.
Yes, that's the idea - the dentry basically already contain the path, so
we wouldn't be reinventing the wheel...
> > - if that doesn't work (or is too complicated), I'm thinking tracking
> > path is probably better than qid-based filtering based on what we
> > discussed as it only affects uncached mode.. I'll need to spend some
> > time testing but I think we can move forward with the current patchset
> > rather than try something new.
>
> Note that in discussion with Mickaël (maintainer of Landlock) he indicated
> that he would be comfortable for Landlock to track a qid, instead of
> holding a inode, specifically for 9pfs.
Yes, I saw that, but what you pointed out about qid reuse make me
somewhat uncomfortable with that direction -- you could allow a
directory, delete it, create a new one somewhere else and if the
underlying fs reuse the same inode number the rule would allow an
intended directory instead so I'd rather not rely on qid for this
either.
But if you think that's not a problem in practice (because e.g. landlock
would somehow detect the dir got deleted or another good reason it's not
a problem) then I agree it's probably the simplest way forward
implementation-wise.
--
Dominique Martinet | Asmadeus
^ permalink raw reply
* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Tingmao Wang @ 2025-09-16 12:44 UTC (permalink / raw)
To: Dominique Martinet
Cc: Christian Schoenebeck, Mickaël Salaün,
Eric Van Hensbergen, Latchesar Ionkov, v9fs, Günther Noack,
linux-security-module, Jan Kara, Amir Goldstein,
Matthew Bobrowski, Al Viro, Christian Brauner, linux-fsdevel
In-Reply-To: <aMih5XYYrpP559de@codewreck.org>
On 9/16/25 00:31, Dominique Martinet wrote:
> (Thanks Christian, replying just once but your reply was helpful)
>
> Tingmao Wang wrote on Mon, Sep 15, 2025 at 02:44:44PM +0100:
>>> I'm fuzzy on the details but I don't see how inode pointers would be
>>> stable for other filesystems as well, what prevents
>>> e.g. vm.drop_caches=3 to drop these inodes on ext4?
>>
>> Landlock holds a reference to the inode in the ruleset, so they shouldn't
>> be dropped. On security_sb_delete Landlock will iput those inodes so they
>> won't cause issue with unmounting. There is some special mechanism
>> ("landlock objects") to decouple the ruleset themselves from the actual
>> inodes, so that previously Landlocked things can keep running even after
>> the inode has disappeared as a result of unmounting.
>
> Thank you for the explanation, that makes more sense.
> iirc even in cacheless mode 9p should keep inode arounds if there's an
> open fd somewhere
Yes, because there is a dentry that has a reference to it. Similarly if
there is a Landlock rule referencing it, the inode will also be kept
around (but not the dentry, Landlock only references the inode). The
problem is that when another application (that is being Landlocked)
accesses the file, 9pfs will create a new inode in uncached mode,
regardless of whether an existing inode exists.
>
>> [...]
>>
>> I tried mounting a qemu-exported 9pfs backed on ext4, with
>> multidevs=remap, and created a file, used stat to note its inode number,
>> deleted the file, created another file (of the same OR different name),
>> and that new file will have the same inode number.
>>
>> (If I don't delete the file, then a newly created file would of course
>> have a different ext4 inode number, and in that case QEMU exposes a
>> different qid)
>
> Ok so from Christian's reply this is just ext4 reusing the same inode..
> I briefly hinted at this above, but in this case ext4 will give the
> inode a different generation number (so the ext4 file handle will be
> different, and accessing the old one will get ESTALE); but that's not
> something qemu currently tracks and it'd be a bit of an overhaul...
> In theory qemu could hash mount_id + file handle to get a properly
> unique qid, if we need to improve that, but that'd be limited to root
> users (and to filesystems that support name_to_handle_at) so I don't
> think it's really appropriate either... hmm..
Actually I think I forgot that there is also qid.version, which in the
case of a QEMU-exported 9pfs might just be the file modification time? In
9pfs currently we do reject a inode match if that version changed server
side in cached mode:
v9fs_test_inode_dotl:
/* compare qid details */
if (memcmp(&v9inode->qid.version,
&st->qid.version, sizeof(v9inode->qid.version)))
return 0;
(not tested whether QEMU correctly sets this version yet)
>
> (I also thought of checking if nlink is 0 when getting a new inode, but
> that's technically legimitate from /proc/x/fd opens so I don't think we
> can do that either)
>
> And then there's also all the servers that don't give unique qids at
> all, so we'll just get weird landlock/fsnotify behaviours for them if we
> go that way...
>
> -----------------
>
> Okay, you've convinced me something like path tracking seems more
> appropriate; I'll just struggle one last time first with a few more open
> questions:
> - afaiu this (reusing inodes) work in cached mode because the dentry is
> kept around;
Based on my understanding, I think this isn't really to do with whether
the dentry is around or not. In cached mode, 9pfs will use iget5_locked
to look up an existing inode based on the qid, if one exists, and use
that, even if no cached dentry points to it. However, in uncached mode,
currently if vfs asks 9pfs to find an inode (e.g. because the dentry is no
longer in cache), it always get a new one:
v9fs_vfs_lookup:
...
else if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
else
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
...
v9fs_qid_iget_dotl:
...
if (new)
test = v9fs_test_new_inode_dotl;
else
test = v9fs_test_inode_dotl;
...
v9fs_test_new_inode_dotl:
static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
{
return 0;
}
> I don't understand the vfs well enough but could the inodes
> hold its dentry and dentries hold their parent dentry alive somehow?
> So in cacheless mode, if you have a tree like this:
> a
> └── b
> ├── c
> └── d
> with c 'open' (or a reference held by landlock), then dentries for a/b/c
> would be kept, but d could be droppable?
I think, based on my understanding, a child dentry does always have a
reference to its parent, and so parent won't be dropped before child, if
child dentry is alive. However holding a proper dentry reference in an
inode might be tricky as dentry holds the reference to its inode.
>
> My understanding is that in cacheless mode we're dropping dentries
> aggressively so that things like readdir() are refreshed, but I'm
> thinking this should work even if we keep some dentries alive when their
> inode is held up.
If we have some way of keeping the dentry alive (without introducing
circular reference problems) then I guess that would work and we don't
have to track paths ourselves.
>
> - if that doesn't work (or is too complicated), I'm thinking tracking
> path is probably better than qid-based filtering based on what we
> discussed as it only affects uncached mode.. I'll need to spend some
> time testing but I think we can move forward with the current patchset
> rather than try something new.
>
> Thanks!
Note that in discussion with Mickaël (maintainer of Landlock) he indicated
that he would be comfortable for Landlock to track a qid, instead of
holding a inode, specifically for 9pfs.
(This doesn't solve the problem for fsnotify though)
Kind regards,
Tingmao
^ permalink raw reply
* Re: [PATCH v3 05/14] s390/pkey: Remove redundant rcu_read_lock/unlock() in spin_lock
From: Harald Freudenberger @ 2025-09-16 10:51 UTC (permalink / raw)
To: pengdonglin
Cc: tj, tony.luck, jani.nikula, ap420073, jv, bcrl, trondmy, longman,
kees, bigeasy, hdanton, paulmck, linux-kernel, linux-rt-devel,
linux-nfs, linux-aio, linux-fsdevel, linux-security-module,
netdev, intel-gfx, linux-wireless, linux-acpi, linux-s390,
cgroups, Holger Dengler, Vasily Gorbik, Alexander Gordeev,
pengdonglin
In-Reply-To: <20250916044735.2316171-6-dolinux.peng@gmail.com>
On 2025-09-16 06:47, pengdonglin wrote:
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side
> function definitions")
> there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
> rcu_read_lock_sched() in terms of RCU read section and the relevant
> grace
> period. That means that spin_lock(), which implies
> rcu_read_lock_sched(),
> also implies rcu_read_lock().
>
> There is no need no explicitly start a RCU read section if one has
> already
> been started implicitly by spin_lock().
>
> Simplify the code and remove the inner rcu_read_lock() invocation.
>
> Cc: Harald Freudenberger <freude@linux.ibm.com>
> Cc: Holger Dengler <dengler@linux.ibm.com>
> Cc: Vasily Gorbik <gor@linux.ibm.com>
> Cc: Alexander Gordeev <agordeev@linux.ibm.com>
> Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
> ---
> drivers/s390/crypto/pkey_base.c | 3 ---
> 1 file changed, 3 deletions(-)
>
> diff --git a/drivers/s390/crypto/pkey_base.c
> b/drivers/s390/crypto/pkey_base.c
> index b15741461a63..4c4a9feecccc 100644
> --- a/drivers/s390/crypto/pkey_base.c
> +++ b/drivers/s390/crypto/pkey_base.c
> @@ -48,16 +48,13 @@ int pkey_handler_register(struct pkey_handler
> *handler)
>
> spin_lock(&handler_list_write_lock);
>
> - rcu_read_lock();
> list_for_each_entry_rcu(h, &handler_list, list) {
> if (h == handler) {
> - rcu_read_unlock();
> spin_unlock(&handler_list_write_lock);
> module_put(handler->module);
> return -EEXIST;
> }
> }
> - rcu_read_unlock();
>
> list_add_rcu(&handler->list, &handler_list);
> spin_unlock(&handler_list_write_lock);
Acked-by: Harald Freudenberger <freude@linux.ibm.com>
^ permalink raw reply
* Re: [PATCH v3 11/14] net: ncsi: Remove redundant rcu_read_lock/unlock() in spin_lock
From: Paul Fertser @ 2025-09-16 9:41 UTC (permalink / raw)
To: pengdonglin
Cc: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees, bigeasy, hdanton, paulmck, linux-kernel,
linux-rt-devel, linux-nfs, linux-aio, linux-fsdevel,
linux-security-module, netdev, intel-gfx, linux-wireless,
linux-acpi, linux-s390, cgroups, Samuel Mendoza-Jonas,
pengdonglin
In-Reply-To: <20250916044735.2316171-12-dolinux.peng@gmail.com>
Hello pengdonglin,
Thank you for the patch, looks reasonable and justified.
On Tue, Sep 16, 2025 at 12:47:32PM +0800, pengdonglin wrote:
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
> there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
> rcu_read_lock_sched() in terms of RCU read section and the relevant grace
> period. That means that spin_lock(), which implies rcu_read_lock_sched(),
> also implies rcu_read_lock().
>
> There is no need no explicitly start a RCU read section if one has already
> been started implicitly by spin_lock().
>
> Simplify the code and remove the inner rcu_read_lock() invocation.
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
^ permalink raw reply
* Re: [PATCH] memfd,selinux: call security_inode_init_security_anon
From: Hugh Dickins @ 2025-09-16 5:06 UTC (permalink / raw)
To: Paul Moore
Cc: Thiébaud Weksteen, James Morris, Stephen Smalley,
Hugh Dickins, Jeff Vander Stoep, Nick Kralevich, Jeff Xu,
Baolin Wang, linux-kernel, linux-security-module, selinux,
linux-mm
In-Reply-To: <6afc91a9f5caef96b2ca335b6d143670@paul-moore.com>
[-- Attachment #1: Type: text/plain, Size: 8087 bytes --]
On Wed, 3 Sep 2025, Paul Moore wrote:
> On Aug 25, 2025 "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" <tweek@google.com> wrote:
> >
> > Prior to this change, no security hooks were called at the creation of a
> > memfd file. It means that, for SELinux as an example, it will receive
> > the default type of the filesystem that backs the in-memory inode. In
> > most cases, that would be tmpfs, but if MFD_HUGETLB is passed, it will
> > be hugetlbfs. Both can be considered implementation details of memfd.
> >
> > It also means that it is not possible to differentiate between a file
> > coming from memfd_create and a file coming from a standard tmpfs mount
> > point.
> >
> > Additionally, no permission is validated at creation, which differs from
> > the similar memfd_secret syscall.
> >
> > Call security_inode_init_security_anon during creation. This ensures
> > that the file is setup similarly to other anonymous inodes. On SELinux,
> > it means that the file will receive the security context of its task.
> >
> > The ability to limit fexecve on memfd has been of interest to avoid
> > potential pitfalls where /proc/self/exe or similar would be executed
> > [1][2]. Reuse the "execute_no_trans" and "entrypoint" access vectors,
> > similarly to the file class. These access vectors may not make sense for
> > the existing "anon_inode" class. Therefore, define and assign a new
> > class "memfd_file" to support such access vectors.
> >
> > Guard these changes behind a new policy capability named "memfd_class".
> >
> > [1] https://crbug.com/1305267
> > [2] https://lore.kernel.org/lkml/20221215001205.51969-1-jeffxu@google.com/
> >
> > Signed-off-by: Thiébaud Weksteen <tweek@google.com>
> > Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> > Tested-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> > ---
> > Changes since RFC:
> > - Remove enum argument, simply compare the anon inode name
> > - Introduce a policy capability for compatility
> > - Add validation of class in selinux_bprm_creds_for_exec
> >
> > include/linux/memfd.h | 2 ++
> > mm/memfd.c | 14 +++++++++--
> > security/selinux/hooks.c | 27 ++++++++++++++++++----
> > security/selinux/include/classmap.h | 2 ++
> > security/selinux/include/policycap.h | 1 +
> > security/selinux/include/policycap_names.h | 1 +
> > security/selinux/include/security.h | 5 ++++
> > 7 files changed, 46 insertions(+), 6 deletions(-)
> >
> > diff --git a/include/linux/memfd.h b/include/linux/memfd.h
> > index 6f606d9573c3..cc74de3dbcfe 100644
> > --- a/include/linux/memfd.h
> > +++ b/include/linux/memfd.h
> > @@ -4,6 +4,8 @@
> >
> > #include <linux/file.h>
> >
> > +#define MEMFD_ANON_NAME "[memfd]"
> > +
> > #ifdef CONFIG_MEMFD_CREATE
> > extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
> > struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
> > diff --git a/mm/memfd.c b/mm/memfd.c
> > index bbe679895ef6..63b439eb402a 100644
> > --- a/mm/memfd.c
> > +++ b/mm/memfd.c
> > @@ -433,6 +433,8 @@ static struct file *alloc_file(const char *name, unsigned int flags)
> > {
> > unsigned int *file_seals;
> > struct file *file;
> > + struct inode *inode;
> > + int err = 0;
> >
> > if (flags & MFD_HUGETLB) {
> > file = hugetlb_file_setup(name, 0, VM_NORESERVE,
> > @@ -444,12 +446,20 @@ static struct file *alloc_file(const char *name, unsigned int flags)
> > }
> > if (IS_ERR(file))
> > return file;
> > +
> > + inode = file_inode(file);
> > + err = security_inode_init_security_anon(inode,
> > + &QSTR(MEMFD_ANON_NAME), NULL);
> > + if (err) {
> > + fput(file);
> > + file = ERR_PTR(err);
> > + return file;
> > + }
> > +
> > file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
> > file->f_flags |= O_LARGEFILE;
> >
> > if (flags & MFD_NOEXEC_SEAL) {
> > - struct inode *inode = file_inode(file);
> > -
> > inode->i_mode &= ~0111;
> > file_seals = memfd_file_seals_ptr(file);
> > if (file_seals) {
>
> Hugh, Baolin, and shmem/mm folks, are you okay with the changes above? If
> so it would be nice to get an ACK from one of you.
So far as I can tell, seems okay to me:
Acked-by: Hugh Dickins <hughd@google.com>
If I'd responded earlier (sorry), I would have asked for it just to use
&QSTR("[memfd]") directly in the call, rather than indirecting through
unnecessary #define MEMFD_ANON_NAME "[memfd]"; never mind, that's all.
Please do take this, along with the rest, through your security tree:
mm.git contains no conflicting change to mm/memfd.c at present.
Thanks,
Hugh
>
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index c95a5874bf7d..429b2269b35a 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -93,6 +93,7 @@
> > #include <linux/fanotify.h>
> > #include <linux/io_uring/cmd.h>
> > #include <uapi/linux/lsm.h>
> > +#include <linux/memfd.h>
> >
> > #include "avc.h"
> > #include "objsec.h"
> > @@ -2366,9 +2367,12 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
> > ad.type = LSM_AUDIT_DATA_FILE;
> > ad.u.file = bprm->file;
> >
> > + if (isec->sclass != SECCLASS_FILE && isec->sclass != SECCLASS_MEMFD_FILE)
> > + return -EPERM;
>
> In the interest of failing fast, this should probably be moved up in the
> function to just after where @isec is set. There are also a number of
> checks that happen prior to this placement, but after the isec assignment.
> While I don't think any of those checks should be an issue, I'd rather
> not to have to worry about those and just fail the non-FILE/MEMFD_FILE
> case as soon as we can in selinux_bprm_creds_for_exec().
>
> > if (new_tsec->sid == old_tsec->sid) {
> > - rc = avc_has_perm(old_tsec->sid, isec->sid,
> > - SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
> > + rc = avc_has_perm(old_tsec->sid, isec->sid, isec->sclass,
> > + FILE__EXECUTE_NO_TRANS, &ad);
> > if (rc)
> > return rc;
> > } else {
> > @@ -2378,8 +2382,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
> > if (rc)
> > return rc;
> >
> > - rc = avc_has_perm(new_tsec->sid, isec->sid,
> > - SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
> > + rc = avc_has_perm(new_tsec->sid, isec->sid, isec->sclass,
> > + FILE__ENTRYPOINT, &ad);
> > if (rc)
> > return rc;
> >
> > @@ -2974,10 +2978,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
> > struct common_audit_data ad;
> > struct inode_security_struct *isec;
> > int rc;
> > + bool is_memfd = false;
> >
> > if (unlikely(!selinux_initialized()))
> > return 0;
> >
> > + if (name != NULL && name->name != NULL &&
> > + !strcmp(name->name, MEMFD_ANON_NAME)) {
> > + if (!selinux_policycap_memfd_class())
> > + return 0;
> > + is_memfd = true;
> > + }
> > +
> > isec = selinux_inode(inode);
> >
> > /*
> > @@ -2996,6 +3008,13 @@ static int selinux_inode_init_security_anon(struct inode *inode,
> >
> > isec->sclass = context_isec->sclass;
> > isec->sid = context_isec->sid;
> > + } else if (is_memfd) {
> > + isec->sclass = SECCLASS_MEMFD_FILE;
> > + rc = security_transition_sid(
> > + sid, sid,
> > + isec->sclass, name, &isec->sid);
> > + if (rc)
> > + return rc;
> > } else {
> > isec->sclass = SECCLASS_ANON_INODE;
> > rc = security_transition_sid(
>
> We're duplicating the security_transition_sid() call which seems less
> than ideal, how about something like this?
>
> if (context_inode) {
> /* ... existing stuff ... */
> } else {
> if (is_memfd)
> isec->sclass = SECCLASS_MEMFD_FILE;
> else
> isec->sclass = SECCLASS_ANON_INODE;
> rc = security_transition_sid(...);
> if (rc)
> return rc;
> }
>
> --
> paul-moore.com
^ permalink raw reply
* [PATCH v3 14/14] wifi: ath9k: Remove redundant rcu_read_lock/unlock() in spin_lock
From: pengdonglin @ 2025-09-16 4:47 UTC (permalink / raw)
To: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees
Cc: bigeasy, hdanton, paulmck, linux-kernel, linux-rt-devel,
linux-nfs, linux-aio, linux-fsdevel, linux-security-module,
netdev, intel-gfx, linux-wireless, linux-acpi, linux-s390,
cgroups, pengdonglin, Toke, Jakub Kicinski, pengdonglin
In-Reply-To: <20250916044735.2316171-1-dolinux.peng@gmail.com>
From: pengdonglin <pengdonglin@xiaomi.com>
Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
rcu_read_lock_sched() in terms of RCU read section and the relevant grace
period. That means that spin_lock(), which implies rcu_read_lock_sched(),
also implies rcu_read_lock().
There is no need no explicitly start a RCU read section if one has already
been started implicitly by spin_lock().
Simplify the code and remove the inner rcu_read_lock() invocation.
Cc: "Toke" <toke@toke.dk>
Cc: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
---
drivers/net/wireless/ath/ath9k/xmit.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 0ac9212e42f7..4a0f465aa2fe 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1993,7 +1993,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_txq_schedule_start(hw, txq->mac80211_qnum);
spin_lock_bh(&sc->chan_lock);
- rcu_read_lock();
if (sc->cur_chan->stopped)
goto out;
@@ -2011,7 +2010,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
}
out:
- rcu_read_unlock();
spin_unlock_bh(&sc->chan_lock);
ieee80211_txq_schedule_end(hw, txq->mac80211_qnum);
}
--
2.34.1
^ permalink raw reply related
* [PATCH v3 13/14] net: bonding: Remove redundant rcu_read_lock/unlock() in spin_lock
From: pengdonglin @ 2025-09-16 4:47 UTC (permalink / raw)
To: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees
Cc: bigeasy, hdanton, paulmck, linux-kernel, linux-rt-devel,
linux-nfs, linux-aio, linux-fsdevel, linux-security-module,
netdev, intel-gfx, linux-wireless, linux-acpi, linux-s390,
cgroups, pengdonglin, Paolo Abeni, Jakub Kicinski, pengdonglin
In-Reply-To: <20250916044735.2316171-1-dolinux.peng@gmail.com>
From: pengdonglin <pengdonglin@xiaomi.com>
Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
rcu_read_lock_sched() in terms of RCU read section and the relevant grace
period. That means that spin_lock(), which implies rcu_read_lock_sched(),
also implies rcu_read_lock().
There is no need no explicitly start a RCU read section if one has already
been started implicitly by spin_lock().
Simplify the code and remove the inner rcu_read_lock() invocation.
Cc: Jay Vosburgh <jv@jvosburgh.net>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
---
drivers/net/bonding/bond_3ad.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 4edc8e6b6b64..c53ea73f103a 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2485,7 +2485,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
* concurrently due to incoming LACPDU as well.
*/
spin_lock_bh(&bond->mode_lock);
- rcu_read_lock();
/* check if there are any slaves */
if (!bond_has_slaves(bond))
@@ -2537,7 +2536,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
break;
}
}
- rcu_read_unlock();
spin_unlock_bh(&bond->mode_lock);
if (update_slave_arr)
--
2.34.1
^ permalink raw reply related
* [PATCH v3 12/14] net: amt: Remove redundant rcu_read_lock/unlock() in spin_lock
From: pengdonglin @ 2025-09-16 4:47 UTC (permalink / raw)
To: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees
Cc: bigeasy, hdanton, paulmck, linux-kernel, linux-rt-devel,
linux-nfs, linux-aio, linux-fsdevel, linux-security-module,
netdev, intel-gfx, linux-wireless, linux-acpi, linux-s390,
cgroups, pengdonglin, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, pengdonglin
In-Reply-To: <20250916044735.2316171-1-dolinux.peng@gmail.com>
From: pengdonglin <pengdonglin@xiaomi.com>
Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
rcu_read_lock_sched() in terms of RCU read section and the relevant grace
period. That means that spin_lock(), which implies rcu_read_lock_sched(),
also implies rcu_read_lock().
There is no need no explicitly start a RCU read section if one has already
been started implicitly by spin_lock().
Simplify the code and remove the inner rcu_read_lock() invocation.
Cc: Taehee Yoo <ap420073@gmail.com>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: pengdonglin <dolinux.peng@gmail.com>
---
drivers/net/amt.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index ed86537b2f61..aaed9fbc7526 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -295,7 +295,6 @@ static void amt_source_work(struct work_struct *work)
tunnel = gnode->tunnel_list;
spin_lock_bh(&tunnel->lock);
- rcu_read_lock();
if (gnode->filter_mode == MCAST_INCLUDE) {
amt_destroy_source(snode);
if (!gnode->nr_sources)
@@ -306,7 +305,6 @@ static void amt_source_work(struct work_struct *work)
*/
snode->status = AMT_SOURCE_STATUS_D_FWD;
}
- rcu_read_unlock();
spin_unlock_bh(&tunnel->lock);
}
@@ -426,7 +424,6 @@ static void amt_group_work(struct work_struct *work)
goto out;
}
- rcu_read_lock();
for (i = 0; i < buckets; i++) {
hlist_for_each_entry_safe(snode, t,
&gnode->sources[i], node) {
@@ -443,7 +440,6 @@ static void amt_group_work(struct work_struct *work)
amt_del_group(amt, gnode);
else
gnode->filter_mode = MCAST_INCLUDE;
- rcu_read_unlock();
spin_unlock_bh(&tunnel->lock);
out:
dev_put(amt->dev);
@@ -1327,11 +1323,9 @@ static void amt_clear_groups(struct amt_tunnel_list *tunnel)
int i;
spin_lock_bh(&tunnel->lock);
- rcu_read_lock();
for (i = 0; i < amt->hash_buckets; i++)
hlist_for_each_entry_safe(gnode, t, &tunnel->groups[i], node)
amt_del_group(amt, gnode);
- rcu_read_unlock();
spin_unlock_bh(&tunnel->lock);
}
@@ -1343,11 +1337,9 @@ static void amt_tunnel_expire(struct work_struct *work)
struct amt_dev *amt = tunnel->amt;
spin_lock_bh(&amt->lock);
- rcu_read_lock();
list_del_rcu(&tunnel->list);
amt->nr_tunnels--;
amt_clear_groups(tunnel);
- rcu_read_unlock();
spin_unlock_bh(&amt->lock);
kfree_rcu(tunnel, rcu);
}
--
2.34.1
^ 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