diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 16:24:46 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 16:24:46 -0700 |
| commit | 4253eb09d25bcfe44f6987b6f67c09f09d80a966 (patch) | |
| tree | a87c01d2183ea31387ef8489a8a7840111e1621b /security | |
| parent | 83453b6f5131a83af7b2a4df28bc776353ac56c5 (diff) | |
| parent | fd6e2388a3ea55e58cbbbef840c1d8aa2067dbb3 (diff) | |
| download | linux-4253eb09d25bcfe44f6987b6f67c09f09d80a966.tar.gz linux-4253eb09d25bcfe44f6987b6f67c09f09d80a966.zip | |
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
Diffstat (limited to 'security')
| -rw-r--r-- | security/selinux/avc.c | 3 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 27 | ||||
| -rw-r--r-- | security/selinux/ima.c | 65 | ||||
| -rw-r--r-- | security/selinux/include/ima.h | 4 | ||||
| -rw-r--r-- | security/selinux/selinuxfs.c | 19 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 46 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 18 |
7 files changed, 121 insertions, 61 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 813e82bcfc27..a9401d6c2e5f 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -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; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 18dd28b2bb13..035aaf113d1d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -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); diff --git a/security/selinux/ima.c b/security/selinux/ima.c index aa34da9b0aeb..cda68122e032 100644 --- a/security/selinux/ima.c +++ b/security/selinux/ima.c @@ -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; - - buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1; + int i; - 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); - - rc = strlcat(buf, selinux_initialized() ? on : off, buf_len); - WARN_ON(rc >= buf_len); + seq_buf_init(&s, buf, selinux_ima_config_len); - rc = strlcat(buf, "enforcing", 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_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); + 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, selinux_state.policycap[i] ? on : off, - buf_len); - WARN_ON(rc >= buf_len); - } + WARN_ON(seq_buf_has_overflowed(&s)); return buf; } 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) { } diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 5aaaf69410bb..c7d91476971c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -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; } diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 8a32666c0ba2..b75c977c6673 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -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; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 7afce975436e..da77ad769c83 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -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( |
