[PATCH] selinux: compute the IMA configuration settings string length once at boot

Ian Bridges posted 1 patch 1 week, 4 days ago
security/selinux/hooks.c       |  3 +++
security/selinux/ima.c         | 33 ++++++++++++++++++++++++---------
security/selinux/include/ima.h |  4 ++++
3 files changed, 31 insertions(+), 9 deletions(-)
[PATCH] selinux: compute the IMA configuration settings string length once at boot
Posted by Ian Bridges 1 week, 4 days ago
selinux_ima_collect_state() builds a string of the current SELinux
configuration settings. The string lists each setting as a name and
one digit. The length of the string therefore never changes, but is
still recomputed on every call.

Add selinux_ima_config_len_init() to compute the length once during
selinux_init(). Update selinux_ima_collect_state() to use the stored
length.

Suggested-by: Paul Moore <paul@paul-moore.com>
Link: https://lore.kernel.org/r/df755e0282dab3b932d19aceab71b7d7@paul-moore.com
Signed-off-by: Ian Bridges <icb@fastmail.org>
---
The produced string is unchanged. selinux_ima_collect_state() now
depends on selinux_ima_config_len_init() having run. The call sits in
selinux_init(), as suggested in the review of the seq_buf conversion.

The length computation itself moves verbatim. The strlen() + 1 on
the string literal could become sizeof(), which counts the
terminator. That rewrite was left out so the moved block stays
identical to the applied code.

The patch was tested as follows.

- W=1 build of security/selinux with CONFIG_IMA=y, zero warnings.
- A userspace differential harness compiled the old and the new
  functions side by side, covering every combination of the settings
  and the policy capabilities. The outputs were byte identical. The
  stored length was an exact fit for the produced string in every
  case, with no overflow and no slack.

 security/selinux/hooks.c       |  3 +++
 security/selinux/ima.c         | 33 ++++++++++++++++++++++++---------
 security/selinux/include/ima.h |  4 ++++
 3 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f4a4edbff2bd..1ead2eee1944 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -107,6 +107,7 @@
 #include "netlabel.h"
 #include "audit.h"
 #include "avc_ss.h"
+#include "ima.h"
 
 #define SELINUX_INODE_INIT_XATTRS 1
 
@@ -7856,6 +7857,8 @@ static __init int selinux_init(void)
 
 	hashtab_cache_init();
 
+	selinux_ima_config_len_init();
+
 	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks),
 			   &selinux_lsmid);
 
diff --git a/security/selinux/ima.c b/security/selinux/ima.c
index cb0efa2fc1ad..cda68122e032 100644
--- a/security/selinux/ima.c
+++ b/security/selinux/ima.c
@@ -13,6 +13,27 @@
 #include "security.h"
 #include "ima.h"
 
+static int selinux_ima_config_len __ro_after_init;
+
+/*
+ * selinux_ima_config_len_init - Compute the configuration settings string length
+ *
+ * The string is fixed text plus one digit per setting, so its length
+ * is known at boot.
+ */
+void __init selinux_ima_config_len_init(void)
+{
+	int buf_len, suffix_len, i;
+
+	buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1;
+	suffix_len = strlen("=0;");
+
+	for (i = 0; i < __POLICYDB_CAP_MAX; i++)
+		buf_len += strlen(selinux_policycap_names[i]) + suffix_len;
+
+	selinux_ima_config_len = buf_len;
+}
+
 /*
  * selinux_ima_collect_state - Read selinux configuration settings
  *
@@ -23,19 +44,13 @@ static char *selinux_ima_collect_state(void)
 {
 	struct seq_buf s;
 	char *buf;
-	int buf_len, suffix_len, i;
+	int i;
 
-	buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1;
-	suffix_len = strlen("=0;");
-
-	for (i = 0; i < __POLICYDB_CAP_MAX; i++)
-		buf_len += strlen(selinux_policycap_names[i]) + suffix_len;
-
-	buf = kzalloc(buf_len, GFP_KERNEL);
+	buf = kzalloc(selinux_ima_config_len, GFP_KERNEL);
 	if (!buf)
 		return NULL;
 
-	seq_buf_init(&s, buf, buf_len);
+	seq_buf_init(&s, buf, selinux_ima_config_len);
 
 	seq_buf_printf(&s, "initialized=%d;enforcing=%d;checkreqprot=%d;",
 		       selinux_initialized(), enforcing_enabled(),
diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h
index 38ab302f5946..d7d18f030d3d 100644
--- a/security/selinux/include/ima.h
+++ b/security/selinux/include/ima.h
@@ -14,9 +14,13 @@
 #include "security.h"
 
 #ifdef CONFIG_IMA
+void __init selinux_ima_config_len_init(void);
 extern void selinux_ima_measure_state(void);
 extern void selinux_ima_measure_state_locked(void);
 #else
+static inline void selinux_ima_config_len_init(void)
+{
+}
 static inline void selinux_ima_measure_state(void)
 {
 }
-- 
2.47.3
Re: [PATCH] selinux: compute the IMA configuration settings string length once at boot
Posted by Paul Moore 1 week, 2 days ago
On Jul 14, 2026 Ian Bridges <icb@fastmail.org> wrote:
> 
> selinux_ima_collect_state() builds a string of the current SELinux
> configuration settings. The string lists each setting as a name and
> one digit. The length of the string therefore never changes, but is
> still recomputed on every call.
> 
> Add selinux_ima_config_len_init() to compute the length once during
> selinux_init(). Update selinux_ima_collect_state() to use the stored
> length.
> 
> Suggested-by: Paul Moore <paul@paul-moore.com>
> Link: https://lore.kernel.org/r/df755e0282dab3b932d19aceab71b7d7@paul-moore.com
> Signed-off-by: Ian Bridges <icb@fastmail.org>
> Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> ---
> The produced string is unchanged. selinux_ima_collect_state() now
> depends on selinux_ima_config_len_init() having run. The call sits in
> selinux_init(), as suggested in the review of the seq_buf conversion.
> 
> The length computation itself moves verbatim. The strlen() + 1 on
> the string literal could become sizeof(), which counts the
> terminator. That rewrite was left out so the moved block stays
> identical to the applied code.
> 
> The patch was tested as follows.
> 
> - W=1 build of security/selinux with CONFIG_IMA=y, zero warnings.
> - A userspace differential harness compiled the old and the new
>   functions side by side, covering every combination of the settings
>   and the policy capabilities. The outputs were byte identical. The
>   stored length was an exact fit for the produced string in every
>   case, with no overflow and no slack.
> 
>  security/selinux/hooks.c       |  3 +++
>  security/selinux/ima.c         | 33 ++++++++++++++++++++++++---------
>  security/selinux/include/ima.h |  4 ++++
>  3 files changed, 31 insertions(+), 9 deletions(-)

Merged into selinux/dev, thanks for doing this.

--
paul-moore.com
Re: [PATCH] selinux: compute the IMA configuration settings string length once at boot
Posted by Stephen Smalley 1 week, 4 days ago
On Tue, Jul 14, 2026 at 12:09 AM Ian Bridges <icb@fastmail.org> wrote:
>
> selinux_ima_collect_state() builds a string of the current SELinux
> configuration settings. The string lists each setting as a name and
> one digit. The length of the string therefore never changes, but is
> still recomputed on every call.
>
> Add selinux_ima_config_len_init() to compute the length once during
> selinux_init(). Update selinux_ima_collect_state() to use the stored
> length.
>
> Suggested-by: Paul Moore <paul@paul-moore.com>
> Link: https://lore.kernel.org/r/df755e0282dab3b932d19aceab71b7d7@paul-moore.com
> Signed-off-by: Ian Bridges <icb@fastmail.org>

Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>

> ---
> The produced string is unchanged. selinux_ima_collect_state() now
> depends on selinux_ima_config_len_init() having run. The call sits in
> selinux_init(), as suggested in the review of the seq_buf conversion.
>
> The length computation itself moves verbatim. The strlen() + 1 on
> the string literal could become sizeof(), which counts the
> terminator. That rewrite was left out so the moved block stays
> identical to the applied code.
>
> The patch was tested as follows.
>
> - W=1 build of security/selinux with CONFIG_IMA=y, zero warnings.
> - A userspace differential harness compiled the old and the new
>   functions side by side, covering every combination of the settings
>   and the policy capabilities. The outputs were byte identical. The
>   stored length was an exact fit for the produced string in every
>   case, with no overflow and no slack.
>
>  security/selinux/hooks.c       |  3 +++
>  security/selinux/ima.c         | 33 ++++++++++++++++++++++++---------
>  security/selinux/include/ima.h |  4 ++++
>  3 files changed, 31 insertions(+), 9 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index f4a4edbff2bd..1ead2eee1944 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -107,6 +107,7 @@
>  #include "netlabel.h"
>  #include "audit.h"
>  #include "avc_ss.h"
> +#include "ima.h"
>
>  #define SELINUX_INODE_INIT_XATTRS 1
>
> @@ -7856,6 +7857,8 @@ static __init int selinux_init(void)
>
>         hashtab_cache_init();
>
> +       selinux_ima_config_len_init();
> +
>         security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks),
>                            &selinux_lsmid);
>
> diff --git a/security/selinux/ima.c b/security/selinux/ima.c
> index cb0efa2fc1ad..cda68122e032 100644
> --- a/security/selinux/ima.c
> +++ b/security/selinux/ima.c
> @@ -13,6 +13,27 @@
>  #include "security.h"
>  #include "ima.h"
>
> +static int selinux_ima_config_len __ro_after_init;
> +
> +/*
> + * selinux_ima_config_len_init - Compute the configuration settings string length
> + *
> + * The string is fixed text plus one digit per setting, so its length
> + * is known at boot.
> + */
> +void __init selinux_ima_config_len_init(void)
> +{
> +       int buf_len, suffix_len, i;
> +
> +       buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1;
> +       suffix_len = strlen("=0;");
> +
> +       for (i = 0; i < __POLICYDB_CAP_MAX; i++)
> +               buf_len += strlen(selinux_policycap_names[i]) + suffix_len;
> +
> +       selinux_ima_config_len = buf_len;
> +}
> +
>  /*
>   * selinux_ima_collect_state - Read selinux configuration settings
>   *
> @@ -23,19 +44,13 @@ static char *selinux_ima_collect_state(void)
>  {
>         struct seq_buf s;
>         char *buf;
> -       int buf_len, suffix_len, i;
> +       int i;
>
> -       buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1;
> -       suffix_len = strlen("=0;");
> -
> -       for (i = 0; i < __POLICYDB_CAP_MAX; i++)
> -               buf_len += strlen(selinux_policycap_names[i]) + suffix_len;
> -
> -       buf = kzalloc(buf_len, GFP_KERNEL);
> +       buf = kzalloc(selinux_ima_config_len, GFP_KERNEL);
>         if (!buf)
>                 return NULL;
>
> -       seq_buf_init(&s, buf, buf_len);
> +       seq_buf_init(&s, buf, selinux_ima_config_len);
>
>         seq_buf_printf(&s, "initialized=%d;enforcing=%d;checkreqprot=%d;",
>                        selinux_initialized(), enforcing_enabled(),
> diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h
> index 38ab302f5946..d7d18f030d3d 100644
> --- a/security/selinux/include/ima.h
> +++ b/security/selinux/include/ima.h
> @@ -14,9 +14,13 @@
>  #include "security.h"
>
>  #ifdef CONFIG_IMA
> +void __init selinux_ima_config_len_init(void);
>  extern void selinux_ima_measure_state(void);
>  extern void selinux_ima_measure_state_locked(void);
>  #else
> +static inline void selinux_ima_config_len_init(void)
> +{
> +}
>  static inline void selinux_ima_measure_state(void)
>  {
>  }
> --
> 2.47.3
>