summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-05-08 03:26:43 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-05-08 03:26:43 +0300
commit917719c412c48687d4a176965d1fa35320ec457c (patch)
tree183392dde41e467c737e8162f67fcc357c732797
parentfcee7d82f27d6a8b1ddc5bbefda59b4e441e9bc0 (diff)
parent868f31e4061eca8c3cd607d79d954d5e54f204aa (diff)
downloadlinux-917719c412c48687d4a176965d1fa35320ec457c.tar.xz
Merge tag 'selinux-pr-20260507' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux fixes from Paul Moore: - Allow for multiple opens of /sys/fs/selinux/policy Prevent a single process from blocking others from reading the SELinux policy loaded in the kernel. This does have the side effect of potentially allowing userspace to trigger additional kernel memory allocations as part of the open/read operation, but this is mitigated by requiring the SELinux security/read_policy permission. - Reduce the critical sections where the SELinux policy mutex is held This includes the patch to the policy loader code where we move the permission checks and an allocation outside the mutex as well as the the patch to checkreqprot which drops the code/lock entirely. While the checkreqprot code had effectively been dropped in an earlier release, portions of the code still remained that would have triggered the mutex to perform an IMA measurement. This finally drops all of that while preserving the user visible behavior. - Eliminate potential sources of log spamming There were a few areas where processes could flood the system logs and hide other, more critical events. The previously disabled checkreqprot and runtime disable knobs in selinuxfs were two such areas that have now been greatly simplified and a pr_err() replaced with a pr_err_once(). The third such place is the /sys/fs/selinux/user file, which hasn't been used by a userspace release since 2020 and was scheduled for removal after 2025; this effectively disables this functionality, but similar to checkreqprot, it is done in a way that should not break old userspace. * tag 'selinux-pr-20260507' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: shrink critical section in sel_write_load() selinux: allow multiple opens of /sys/fs/selinux/policy selinux: prune /sys/fs/selinux/user selinux: prune /sys/fs/selinux/disable selinux: prune /sys/fs/selinux/checkreqprot
-rw-r--r--Documentation/ABI/removed/sysfs-selinux-user (renamed from Documentation/ABI/obsolete/sysfs-selinux-user)0
-rw-r--r--security/selinux/include/security.h2
-rw-r--r--security/selinux/selinuxfs.c196
-rw-r--r--security/selinux/ss/services.c125
4 files changed, 31 insertions, 292 deletions
diff --git a/Documentation/ABI/obsolete/sysfs-selinux-user b/Documentation/ABI/removed/sysfs-selinux-user
index 8ab7557f283f..8ab7557f283f 100644
--- a/Documentation/ABI/obsolete/sysfs-selinux-user
+++ b/Documentation/ABI/removed/sysfs-selinux-user
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index d1f16d7f684d..0babb8992181 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -312,8 +312,6 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
u32 *sid);
-int security_get_user_sids(u32 fromsid, const char *username, u32 **sids, u32 *nel);
-
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 83aa765a09f9..25ca7d714014 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -76,7 +76,6 @@ struct selinux_fs_info {
int *bool_pending_values;
struct dentry *class_dir;
unsigned long last_class_ino;
- bool policy_opened;
unsigned long last_ino;
struct super_block *sb;
};
@@ -272,35 +271,13 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page;
- ssize_t length;
- int new_value;
-
- if (count >= PAGE_SIZE)
- return -ENOMEM;
-
- /* No partial writes. */
- if (*ppos != 0)
- return -EINVAL;
-
- page = memdup_user_nul(buf, count);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- if (sscanf(page, "%d", &new_value) != 1) {
- length = -EINVAL;
- goto out;
- }
- length = count;
-
- if (new_value) {
- pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n");
- pr_err("SELinux: Runtime disable is not supported, use selinux=0 on the kernel cmdline.\n");
- }
-
-out:
- kfree(page);
- return length;
+ /*
+ * Setting disable is no longer supported, see
+ * https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable
+ */
+ pr_err_once("SELinux: %s (%d) wrote to disable. This is no longer supported.\n",
+ current->comm, current->pid);
+ return count;
}
static const struct file_operations sel_disable_ops = {
@@ -362,44 +339,31 @@ struct policy_load_memory {
static int sel_open_policy(struct inode *inode, struct file *filp)
{
- struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
struct policy_load_memory *plm = NULL;
int rc;
- BUG_ON(filp->private_data);
-
- mutex_lock(&selinux_state.policy_mutex);
-
rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (rc)
- goto err;
-
- rc = -EBUSY;
- if (fsi->policy_opened)
- goto err;
+ return rc;
- rc = -ENOMEM;
plm = kzalloc_obj(*plm);
if (!plm)
- goto err;
+ return -ENOMEM;
+ mutex_lock(&selinux_state.policy_mutex);
rc = security_read_policy(&plm->data, &plm->len);
if (rc)
goto err;
-
if ((size_t)i_size_read(inode) != plm->len) {
inode_lock(inode);
i_size_write(inode, plm->len);
inode_unlock(inode);
}
-
- fsi->policy_opened = 1;
+ mutex_unlock(&selinux_state.policy_mutex);
filp->private_data = plm;
- mutex_unlock(&selinux_state.policy_mutex);
-
return 0;
err:
mutex_unlock(&selinux_state.policy_mutex);
@@ -412,13 +376,8 @@ err:
static int sel_release_policy(struct inode *inode, struct file *filp)
{
- struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
struct policy_load_memory *plm = filp->private_data;
- BUG_ON(!plm);
-
- fsi->policy_opened = 0;
-
vfree(plm->data);
kfree(plm);
@@ -594,34 +553,31 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
if (!count)
return -EINVAL;
- mutex_lock(&selinux_state.policy_mutex);
-
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
if (length)
- goto out;
+ return length;
data = vmalloc(count);
- if (!data) {
- length = -ENOMEM;
- goto out;
- }
+ if (!data)
+ return -ENOMEM;
if (copy_from_user(data, buf, count) != 0) {
length = -EFAULT;
goto out;
}
+ mutex_lock(&selinux_state.policy_mutex);
length = security_load_policy(data, count, &load_state);
if (length) {
pr_warn_ratelimited("SELinux: failed to load policy\n");
- goto out;
+ goto out_unlock;
}
fsi = file_inode(file)->i_sb->s_fs_info;
length = sel_make_policy_nodes(fsi, load_state.policy);
if (length) {
pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n");
selinux_policy_cancel(&load_state);
- goto out;
+ goto out_unlock;
}
selinux_policy_commit(&load_state);
@@ -631,8 +587,9 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
-out:
+out_unlock:
mutex_unlock(&selinux_state.policy_mutex);
+out:
vfree(data);
return length;
}
@@ -689,46 +646,13 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page;
- ssize_t length;
- unsigned int new_value;
-
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
- SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
- NULL);
- if (length)
- return length;
-
- if (count >= PAGE_SIZE)
- return -ENOMEM;
-
- /* No partial writes. */
- if (*ppos != 0)
- return -EINVAL;
-
- page = memdup_user_nul(buf, count);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- if (sscanf(page, "%u", &new_value) != 1) {
- length = -EINVAL;
- goto out;
- }
- length = count;
-
- if (new_value) {
- char comm[sizeof(current->comm)];
-
- strscpy(comm, current->comm);
- pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n",
- comm, current->pid);
- }
-
- selinux_ima_measure_state();
-
-out:
- kfree(page);
- return length;
+ /*
+ * Setting checkreqprot is no longer supported, see
+ * https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-checkreqprot
+ */
+ pr_err_once("SELinux: %s (%d) wrote to checkreqprot. This is no longer supported.\n",
+ current->comm, current->pid);
+ return count;
}
static const struct file_operations sel_checkreqprot_ops = {
.read = sel_read_checkreqprot,
@@ -1073,69 +997,11 @@ out:
static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
{
- char *con = NULL, *user = NULL, *ptr;
- u32 sid, *sids = NULL;
- ssize_t length;
- char *newcon;
- int rc;
- u32 i, len, nsids;
-
- pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!"
- " This will not be supported in the future; please update your"
- " userspace.\n", current->comm, current->pid);
- ssleep(5);
-
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
- SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
- NULL);
- if (length)
- goto out;
-
- length = -ENOMEM;
- con = kzalloc(size + 1, GFP_KERNEL);
- if (!con)
- goto out;
-
- length = -ENOMEM;
- user = kzalloc(size + 1, GFP_KERNEL);
- if (!user)
- goto out;
-
- length = -EINVAL;
- if (sscanf(buf, "%s %s", con, user) != 2)
- goto out;
-
- length = security_context_str_to_sid(con, &sid, GFP_KERNEL);
- if (length)
- goto out;
-
- length = security_get_user_sids(sid, user, &sids, &nsids);
- if (length)
- goto out;
-
- length = sprintf(buf, "%u", nsids) + 1;
- ptr = buf + length;
- for (i = 0; i < nsids; i++) {
- rc = security_sid_to_context(sids[i], &newcon, &len);
- if (rc) {
- length = rc;
- goto out;
- }
- if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
- kfree(newcon);
- length = -ERANGE;
- goto out;
- }
- memcpy(ptr, newcon, len);
- kfree(newcon);
- ptr += len;
- length += len;
- }
-out:
- kfree(sids);
- kfree(user);
- kfree(con);
- return length;
+ pr_err_once("SELinux: %s (%d) wrote to user. This is no longer supported.\n",
+ current->comm, current->pid);
+ buf[0] = '0';
+ buf[1] = 0;
+ return 2;
}
static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e8e7ccbd1e44..143021c5e326 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2746,131 +2746,6 @@ out:
return rc;
}
-#define SIDS_NEL 25
-
-/**
- * security_get_user_sids - Obtain reachable SIDs for a user.
- * @fromsid: starting SID
- * @username: username
- * @sids: array of reachable SIDs for user
- * @nel: number of elements in @sids
- *
- * Generate the set of SIDs for legal security contexts
- * for a given user that can be reached by @fromsid.
- * Set *@sids to point to a dynamically allocated
- * array containing the set of SIDs. Set *@nel to the
- * number of elements in the array.
- */
-
-int security_get_user_sids(u32 fromsid,
- const char *username,
- u32 **sids,
- u32 *nel)
-{
- struct selinux_policy *policy;
- struct policydb *policydb;
- struct sidtab *sidtab;
- struct context *fromcon, usercon;
- u32 *mysids = NULL, *mysids2, sid;
- u32 i, j, mynel, maxnel = SIDS_NEL;
- struct user_datum *user;
- struct role_datum *role;
- struct ebitmap_node *rnode, *tnode;
- int rc;
-
- *sids = NULL;
- *nel = 0;
-
- if (!selinux_initialized())
- return 0;
-
- mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL);
- if (!mysids)
- return -ENOMEM;
-
-retry:
- mynel = 0;
- rcu_read_lock();
- policy = rcu_dereference(selinux_state.policy);
- policydb = &policy->policydb;
- sidtab = policy->sidtab;
-
- context_init(&usercon);
-
- rc = -EINVAL;
- fromcon = sidtab_search(sidtab, fromsid);
- if (!fromcon)
- goto out_unlock;
-
- rc = -EINVAL;
- user = symtab_search(&policydb->p_users, username);
- if (!user)
- goto out_unlock;
-
- usercon.user = user->value;
-
- ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
- role = policydb->role_val_to_struct[i];
- usercon.role = i + 1;
- ebitmap_for_each_positive_bit(&role->types, tnode, j) {
- usercon.type = j + 1;
-
- if (mls_setup_user_range(policydb, fromcon, user,
- &usercon))
- continue;
-
- rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
- if (rc == -ESTALE) {
- rcu_read_unlock();
- goto retry;
- }
- if (rc)
- goto out_unlock;
- if (mynel < maxnel) {
- mysids[mynel++] = sid;
- } else {
- rc = -ENOMEM;
- maxnel += SIDS_NEL;
- mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
- if (!mysids2)
- goto out_unlock;
- memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
- kfree(mysids);
- mysids = mysids2;
- mysids[mynel++] = sid;
- }
- }
- }
- rc = 0;
-out_unlock:
- rcu_read_unlock();
- if (rc || !mynel) {
- kfree(mysids);
- return rc;
- }
-
- rc = -ENOMEM;
- mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
- if (!mysids2) {
- kfree(mysids);
- return rc;
- }
- for (i = 0, j = 0; i < mynel; i++) {
- struct av_decision dummy_avd;
- rc = avc_has_perm_noaudit(fromsid, mysids[i],
- SECCLASS_PROCESS, /* kernel value */
- PROCESS__TRANSITION, AVC_STRICT,
- &dummy_avd);
- if (!rc)
- mysids2[j++] = mysids[i];
- cond_resched();
- }
- kfree(mysids);
- *sids = mysids2;
- *nel = j;
- return 0;
-}
-
/**
* __security_genfs_sid - Helper to obtain a SID for a file in a filesystem
* @policy: policy