Merge tag 'selinux-pr-20260814' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux updates from Paul Moore:

 - Convert a __get_free_page() call into a kmalloc() call

   We had some very old code that called out to __get_free_page() for
   allocating a pathname. There is no reason this couldn't be done with
   a call to kmalloc() so we've done the conversion and now there is one
   less __get_free_page() caller in the kernel.

 - Limit the number of retired/unknown DCCP netlink messages

   While DCCP is gone from the kernel, there are still userspace tools
   which try to talk to the kernel about DCCP sockets which were
   generating SELinux related log noise (unrecognized netlink message).
   This pull request both limits the log messages to just the first
   instance and also explains to the user that DCCP support has been
   removed.

 - Convert the SELinux strlcat() calls to seq_buf_XXX() calls

   As part of the effort to drop the strlcat() API from the kernel, the
   SELinux/IMA code was converted over to using seq_buf_XXX() calls.

 - Only calculate the SELinux IMA configuration string length once

   Previously each call to generate a SELinux configuration string for
   IMA would have to calculate the length of the string. While the
   contents of the string will likely change over the lifetime of the
   system, the length of the string will not. Calculate the string
   length once at boot and reuse the length value throughout the
   lifetime of the system.

 - Further validation of the SELinux policy at policy load time

   Perform additional sanity checks on the policy constraints and types.

 - Proper cleanup and error handling for selinuxfs init failures

   We were not properly cleaning up some state in the case where
   selinuxfs fails to initialize properly. It's somewhat of an academic
   exercise as a failure to initialize selinuxfs will cause the system
   to fail on boot, but it's arguably better to make sure we do things
   the proper way.

 - Various code cleanups

   Convert integer flags to boolean types and drop an uncessary goto
   from the SELinux code.

* tag 'selinux-pr-20260814' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: validate constraint expression attr and op at load time
  selinux: compute the IMA configuration settings string length once at boot
  selinux: replace strlcat() with seq_buf in selinux_ima_collect_state()
  selinux: suppress warning flood for retired DCCP netlink messages
  selinux: tighten type validation during policy load
  selinux: drop unnecessary goto and label from avc_alloc_node()
  selinux: convert int flags to bool flags in ss/services.c
  selinux: clean up selinuxfs resources on init failure
  selinux: hooks: use kmalloc() to allocate path buffer
This commit is contained in:
Linus Torvalds
2026-08-19 16:24:46 -07:00
7 changed files with 121 additions and 61 deletions
+1 -2
View File
@@ -497,7 +497,7 @@ static struct avc_node *avc_alloc_node(void)
node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
if (!node)
goto out;
return NULL;
INIT_HLIST_NODE(&node->list);
avc_cache_stats_incr(allocations);
@@ -506,7 +506,6 @@ static struct avc_node *avc_alloc_node(void)
selinux_avc.avc_cache_threshold)
avc_reclaim_node();
out:
return node;
}
+18 -9
View File
@@ -94,6 +94,7 @@
#include <linux/io_uring/cmd.h>
#include <uapi/linux/lsm.h>
#include <linux/memfd.h>
#include <uapi/linux/inet_diag.h>
#include "initcalls.h"
#include "avc.h"
@@ -106,6 +107,7 @@
#include "netlabel.h"
#include "audit.h"
#include "avc_ss.h"
#include "ima.h"
#define SELINUX_INODE_INIT_XATTRS 1
@@ -1336,11 +1338,11 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
struct super_block *sb = dentry->d_sb;
char *buffer, *path;
buffer = (char *)__get_free_page(GFP_KERNEL);
buffer = kmalloc(PATH_MAX, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
path = dentry_path_raw(dentry, buffer, PAGE_SIZE);
path = dentry_path_raw(dentry, buffer, PATH_MAX);
if (IS_ERR(path))
rc = PTR_ERR(path);
else {
@@ -1361,7 +1363,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
rc = 0;
}
}
free_page((unsigned long)buffer);
kfree(buffer);
return rc;
}
@@ -6296,12 +6298,17 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
return rc;
} else if (rc == -EINVAL) {
/* -EINVAL is a missing msg/perm mapping */
pr_warn_ratelimited("SELinux: unrecognized netlink"
" message: protocol=%hu nlmsg_type=%hu sclass=%s"
" pid=%d comm=%s\n",
sk->sk_protocol, nlh->nlmsg_type,
secclass_map[sclass - 1].name,
task_pid_nr(current), current->comm);
if (sclass == SECCLASS_NETLINK_TCPDIAG_SOCKET &&
nlh->nlmsg_type == DCCPDIAG_GETSOCK)
pr_warn_once("SELinux: DCCP has been removed, pid=%d comm=%s\n",
task_pid_nr(current), current->comm);
else
pr_warn_ratelimited("SELinux: unrecognized netlink"
" message: protocol=%hu nlmsg_type=%hu sclass=%s"
" pid=%d comm=%s\n",
sk->sk_protocol, nlh->nlmsg_type,
secclass_map[sclass - 1].name,
task_pid_nr(current), current->comm);
if (enforcing_enabled() &&
!security_get_allow_unknown())
return rc;
@@ -7874,6 +7881,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);
+33 -32
View File
@@ -9,9 +9,31 @@
*/
#include <linux/vmalloc.h>
#include <linux/ima.h>
#include <linux/seq_buf.h>
#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
*
@@ -20,46 +42,25 @@
*/
static char *selinux_ima_collect_state(void)
{
const char *on = "=1;", *off = "=0;";
struct seq_buf s;
char *buf;
int buf_len, len, i, rc;
int i;
buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1;
len = strlen(on);
for (i = 0; i < __POLICYDB_CAP_MAX; i++)
buf_len += strlen(selinux_policycap_names[i]) + len;
buf = kzalloc(buf_len, GFP_KERNEL);
buf = kzalloc(selinux_ima_config_len, GFP_KERNEL);
if (!buf)
return NULL;
rc = strscpy(buf, "initialized", buf_len);
WARN_ON(rc < 0);
seq_buf_init(&s, buf, selinux_ima_config_len);
rc = strlcat(buf, selinux_initialized() ? on : off, buf_len);
WARN_ON(rc >= buf_len);
seq_buf_printf(&s, "initialized=%d;enforcing=%d;checkreqprot=%d;",
selinux_initialized(), enforcing_enabled(),
checkreqprot_get());
rc = strlcat(buf, "enforcing", buf_len);
WARN_ON(rc >= buf_len);
for (i = 0; i < __POLICYDB_CAP_MAX; i++)
seq_buf_printf(&s, "%s=%d;", selinux_policycap_names[i],
selinux_state.policycap[i]);
rc = strlcat(buf, enforcing_enabled() ? on : off, buf_len);
WARN_ON(rc >= buf_len);
rc = strlcat(buf, "checkreqprot", buf_len);
WARN_ON(rc >= buf_len);
rc = strlcat(buf, checkreqprot_get() ? on : off, buf_len);
WARN_ON(rc >= buf_len);
for (i = 0; i < __POLICYDB_CAP_MAX; i++) {
rc = strlcat(buf, selinux_policycap_names[i], buf_len);
WARN_ON(rc >= buf_len);
rc = strlcat(buf, selinux_state.policycap[i] ? on : off,
buf_len);
WARN_ON(rc >= buf_len);
}
WARN_ON(seq_buf_has_overflowed(&s));
return buf;
}
+4
View File
@@ -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)
{
}
+13 -6
View File
@@ -1984,17 +1984,15 @@ int __init init_sel_fs(void)
return err;
err = register_filesystem(&sel_fs_type);
if (err) {
sysfs_remove_mount_point(fs_kobj, "selinux");
return err;
}
if (err)
goto err_remove_mount_point;
selinux_null.mnt = kern_mount(&sel_fs_type);
if (IS_ERR(selinux_null.mnt)) {
pr_err("selinuxfs: could not mount!\n");
err = PTR_ERR(selinux_null.mnt);
selinux_null.mnt = NULL;
return err;
goto err_unregister_fs;
}
selinux_null.dentry = try_lookup_noperm(&null_name,
@@ -2003,7 +2001,7 @@ int __init init_sel_fs(void)
pr_err("selinuxfs: could not lookup null!\n");
err = PTR_ERR(selinux_null.dentry);
selinux_null.dentry = NULL;
return err;
goto err_unmount;
}
/*
@@ -2012,5 +2010,14 @@ int __init init_sel_fs(void)
*/
(void) selinux_kernel_status_page();
return 0;
err_unmount:
kern_unmount(selinux_null.mnt);
selinux_null.mnt = NULL;
err_unregister_fs:
unregister_filesystem(&sel_fs_type);
err_remove_mount_point:
sysfs_remove_mount_point(fs_kobj, "selinux");
return err;
}
+43 -3
View File
@@ -604,10 +604,15 @@ static int type_index(void *key, void *datum, void *datap)
typdatum = datum;
p = datap;
if (!typdatum->value || typdatum->value > p->p_types.nprim ||
typdatum->bounds > p->p_types.nprim) {
pr_err("SELinux: type %s had value %u bounds %u nprim %u\n",
(char *)key, typdatum->value, typdatum->bounds,
p->p_types.nprim);
return -EINVAL;
}
if (typdatum->primary) {
if (!typdatum->value || typdatum->value > p->p_types.nprim ||
typdatum->bounds > p->p_types.nprim)
return -EINVAL;
p->sym_val_to_name[SYM_TYPES][typdatum->value - 1] = key;
p->type_val_to_struct[typdatum->value - 1] = typdatum;
}
@@ -1398,6 +1403,27 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
if (depth == (CEXPR_MAXDEPTH - 1))
return -EINVAL;
depth++;
switch (e->attr) {
case CEXPR_USER:
case CEXPR_TYPE:
if (e->op != CEXPR_EQ &&
e->op != CEXPR_NEQ)
return -EINVAL;
break;
case CEXPR_ROLE:
case CEXPR_L1L2:
case CEXPR_L1H2:
case CEXPR_H1L2:
case CEXPR_H1H2:
case CEXPR_L1H1:
case CEXPR_L2H2:
if (e->op < CEXPR_EQ ||
e->op > CEXPR_INCOMP)
return -EINVAL;
break;
default:
return -EINVAL;
}
break;
case CEXPR_NAMES:
if (!allowxtarget && (e->attr & CEXPR_XTARGET))
@@ -1405,6 +1431,20 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
if (depth == (CEXPR_MAXDEPTH - 1))
return -EINVAL;
depth++;
switch (e->attr &
~(CEXPR_TARGET|CEXPR_XTARGET)) {
case CEXPR_USER:
case CEXPR_ROLE:
case CEXPR_TYPE:
break;
default:
return -EINVAL;
}
if ((e->attr & (CEXPR_TARGET|CEXPR_XTARGET)) ==
(CEXPR_TARGET|CEXPR_XTARGET))
return -EINVAL;
if (e->op != CEXPR_EQ && e->op != CEXPR_NEQ)
return -EINVAL;
rc = ebitmap_read(&e->names, fp);
if (rc)
return rc;
+9 -9
View File
@@ -1355,8 +1355,8 @@ const char *security_get_initial_sid_context(u32 sid)
}
static int security_sid_to_context_core(u32 sid, char **scontext,
u32 *scontext_len, int force,
int only_invalid)
u32 *scontext_len, bool force,
bool only_invalid)
{
struct selinux_policy *policy;
struct policydb *policydb;
@@ -1439,14 +1439,14 @@ out_unlock:
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
{
return security_sid_to_context_core(sid, scontext,
scontext_len, 0, 0);
scontext_len, false, false);
}
int security_sid_to_context_force(u32 sid,
char **scontext, u32 *scontext_len)
{
return security_sid_to_context_core(sid, scontext,
scontext_len, 1, 0);
scontext_len, true, false);
}
/**
@@ -1466,7 +1466,7 @@ int security_sid_to_context_inval(u32 sid,
char **scontext, u32 *scontext_len)
{
return security_sid_to_context_core(sid, scontext,
scontext_len, 1, 1);
scontext_len, true, true);
}
/*
@@ -1552,7 +1552,7 @@ out:
static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags,
int force)
bool force)
{
struct selinux_policy *policy;
struct policydb *policydb;
@@ -1641,7 +1641,7 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid,
gfp_t gfp)
{
return security_context_to_sid_core(scontext, scontext_len,
sid, SECSID_NULL, gfp, 0);
sid, SECSID_NULL, gfp, false);
}
int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp)
@@ -1673,14 +1673,14 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags)
{
return security_context_to_sid_core(scontext, scontext_len,
sid, def_sid, gfp_flags, 1);
sid, def_sid, gfp_flags, true);
}
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
u32 *sid)
{
return security_context_to_sid_core(scontext, scontext_len,
sid, SECSID_NULL, GFP_KERNEL, 1);
sid, SECSID_NULL, GFP_KERNEL, true);
}
static int compute_sid_handle_invalid_context(