From 8a71d8fa55760eb7f6b1c8a96e771e2678625b9c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 1 May 2025 15:18:56 -0400 Subject: [PATCH 1/6] selinux: add a 5 second sleep to /sys/fs/selinux/user Commit d7b6918e22c7 ("selinux: Deprecate /sys/fs/selinux/user") started the deprecation process for /sys/fs/selinux/user: The selinuxfs "user" node allows userspace to request a list of security contexts that can be reached for a given SELinux user from a given starting context. This was used by libselinux when various login-style programs requested contexts for users, but libselinux stopped using it in 2020. Kernel support will be removed no sooner than Dec 2025. A pr_warn() message has been in place since Linux v6.13, this patch adds a five second sleep to /sys/fs/selinux/user to help make the deprecation and upcoming removal more noticeable. Signed-off-by: Paul Moore --- security/selinux/selinuxfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index e67a8ce4b64c..95765374f58d 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1072,6 +1072,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) 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, From 17bd3c01667aafaa267e64be70f9627e287ec210 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 17 Jun 2025 10:39:07 -0400 Subject: [PATCH 2/6] documentation: add links to SELinux resources Add links to the SELinux kernel subsystem README.md file, the SELinux kernel wiki, and the SELinux userspace wiki to the SELinux guide. Signed-off-by: Stephen Smalley [PM: spacing and style corrections, subject tweak] Signed-off-by: Paul Moore --- Documentation/admin-guide/LSM/SELinux.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/admin-guide/LSM/SELinux.rst b/Documentation/admin-guide/LSM/SELinux.rst index 520a1c2c6fd2..cdd65164ca96 100644 --- a/Documentation/admin-guide/LSM/SELinux.rst +++ b/Documentation/admin-guide/LSM/SELinux.rst @@ -2,6 +2,17 @@ SELinux ======= +Information about the SELinux kernel subsystem can be found at the +following links: + + https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/tree/README.md + + https://github.com/selinuxproject/selinux-kernel/wiki + +Information about the SELinux userspace can be found at: + + https://github.com/SELinuxProject/selinux/wiki + If you want to use SELinux, chances are you will want to use the distro-provided policies, or install the latest reference policy release from From 1106896146d8711fdc899e6fc792e1d01f9b9f15 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 21 May 2025 10:41:23 -0400 Subject: [PATCH 3/6] selinux: introduce neveraudit types Introduce neveraudit types i.e. types that should never trigger audit messages. This allows the AVC to skip all audit-related processing for such types. Note that neveraudit differs from dontaudit not only wrt being applied for all checks with a given source type but also in that it disables all auditing, not just permission denials. When a type is both a permissive type and a neveraudit type, the security server can short-circuit the security_compute_av() logic, allowing all permissions and not auditing any permissions. This change just introduces the basic support but does not yet further optimize the AVC or hook function logic when a type is both a permissive type and a dontaudit type. Suggested-by: Paul Moore Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/include/avc.h | 4 ++++ security/selinux/include/security.h | 4 +++- security/selinux/ss/policydb.c | 19 +++++++++++++++++++ security/selinux/ss/policydb.h | 2 ++ security/selinux/ss/services.c | 20 ++++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 281f40103663..01b5167fee1a 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -65,6 +65,10 @@ static inline u32 avc_audit_required(u32 requested, struct av_decision *avd, int result, u32 auditdeny, u32 *deniedp) { u32 denied, audited; + + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + return 0; + denied = requested & ~avd->allowed; if (unlikely(denied)) { audited = denied & avd->auditdeny; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 278c144c22d6..8201e6a3ac0f 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -47,10 +47,11 @@ #define POLICYDB_VERSION_GLBLUB 32 #define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */ #define POLICYDB_VERSION_COND_XPERMS 34 /* extended permissions in conditional policies */ +#define POLICYDB_VERSION_NEVERAUDIT 35 /* neveraudit types */ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COND_XPERMS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEVERAUDIT /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f @@ -260,6 +261,7 @@ struct extended_perms { /* definitions of av_decision.flags */ #define AVD_FLAGS_PERMISSIVE 0x0001 +#define AVD_FLAGS_NEVERAUDIT 0x0002 void security_compute_av(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9ea971943713..91df3db6a88c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -160,6 +160,11 @@ static const struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_NEVERAUDIT, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static const struct policydb_compat_info * @@ -531,6 +536,7 @@ static void policydb_init(struct policydb *p) ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); + ebitmap_init(&p->neveraudit_map); } /* @@ -852,6 +858,7 @@ void policydb_destroy(struct policydb *p) ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); + ebitmap_destroy(&p->neveraudit_map); } /* @@ -2538,6 +2545,12 @@ int policydb_read(struct policydb *p, struct policy_file *fp) goto bad; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_read(&p->neveraudit_map, fp); + if (rc) + goto bad; + } + rc = -EINVAL; info = policydb_lookup_compat(p->policyvers); if (!info) { @@ -3723,6 +3736,12 @@ int policydb_write(struct policydb *p, struct policy_file *fp) return rc; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_write(&p->neveraudit_map, fp); + if (rc) + return rc; + } + num_syms = info->sym_num; for (i = 0; i < num_syms; i++) { struct policy_data pd; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 25650224b6e7..89a180b1742f 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -300,6 +300,8 @@ struct policydb { struct ebitmap permissive_map; + struct ebitmap neveraudit_map; + /* length of this policy when it was loaded */ size_t len; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 7becf3808818..9b3b64ea6790 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1153,6 +1153,14 @@ void security_compute_av(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1172,6 +1180,8 @@ void security_compute_av(u32 ssid, policydb->allow_unknown); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; @@ -1208,6 +1218,14 @@ void security_compute_av_user(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1225,6 +1243,8 @@ void security_compute_av_user(u32 ssid, NULL); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; From 951b2de06a0bd64930949c7d3bd5a113cdf24189 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 21 May 2025 10:41:25 -0400 Subject: [PATCH 4/6] selinux: optimize selinux_inode_getattr/permission() based on neveraudit|permissive Extend the task avdcache to also cache whether the task SID is both permissive and neveraudit, and return immediately if so in both selinux_inode_getattr() and selinux_inode_permission(). The same approach could be applied to many of the hook functions although the avdcache would need to be updated for more than directory search checks in order for this optimization to be beneficial for checks on objects other than directories. To test, apply https://github.com/SELinuxProject/selinux/pull/473 to your selinux userspace, build and install libsepol, and use the following CIL policy module: $ cat neverauditpermissive.cil (typeneveraudit unconfined_t) (typepermissive unconfined_t) Without this module inserted, running the following commands: perf record make -jN # on an already built allmodconfig tree perf report --sort=symbol,dso yields the following percentages (only showing __d_lookup_rcu for reference and only showing relevant SELinux functions): 1.65% [k] __d_lookup_rcu 0.53% [k] selinux_inode_permission 0.40% [k] selinux_inode_getattr 0.15% [k] avc_lookup 0.05% [k] avc_has_perm 0.05% [k] avc_has_perm_noaudit 0.02% [k] avc_policy_seqno 0.02% [k] selinux_file_permission 0.01% [k] selinux_inode_alloc_security 0.01% [k] selinux_file_alloc_security for a total of 1.24% for SELinux compared to 1.65% for __d_lookup_rcu(). After running the following command to insert this module: semodule -i neverauditpermissive.cil and then re-running the same perf commands from above yields the following non-zero percentages: 1.74% [k] __d_lookup_rcu 0.31% [k] selinux_inode_permission 0.03% [k] selinux_inode_getattr 0.03% [k] avc_policy_seqno 0.01% [k] avc_lookup 0.01% [k] selinux_file_permission 0.01% [k] selinux_file_open for a total of 0.40% for SELinux compared to 1.74% for __d_lookup_rcu(). Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 14 +++++++++++++- security/selinux/include/objsec.h | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 595ceb314aeb..335fbf76cdd2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3181,6 +3181,8 @@ static inline void task_avdcache_update(struct task_security_struct *tsec, tsec->avdcache.dir[spot].audited = audited; tsec->avdcache.dir[spot].allowed = avd->allowed; tsec->avdcache.dir[spot].permissive = avd->flags & AVD_FLAGS_PERMISSIVE; + tsec->avdcache.permissive_neveraudit = + (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)); } /** @@ -3207,10 +3209,13 @@ static int selinux_inode_permission(struct inode *inode, int requested) if (!mask) return 0; + tsec = selinux_cred(current_cred()); + if (task_avdcache_permnoaudit(tsec)) + return 0; + isec = inode_security_rcu(inode, requested & MAY_NOT_BLOCK); if (IS_ERR(isec)) return PTR_ERR(isec); - tsec = selinux_cred(current_cred()); perms = file_mask_to_av(inode->i_mode, mask); rc = task_avdcache_search(tsec, isec, &avdc); @@ -3274,6 +3279,13 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, static int selinux_inode_getattr(const struct path *path) { + struct task_security_struct *tsec; + + tsec = selinux_cred(current_cred()); + + if (task_avdcache_permnoaudit(tsec)) + return 0; + return path_has_perm(current_cred(), path, FILE__GETATTR); } diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 6ee7dc4dfd6e..1d7ac59015a1 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -49,9 +49,17 @@ struct task_security_struct { u32 seqno; /* AVC sequence number */ unsigned int dir_spot; /* dir cache index to check first */ struct avdc_entry dir[TSEC_AVDC_DIR_SIZE]; /* dir entries */ + bool permissive_neveraudit; /* permissive and neveraudit */ } avdcache; } __randomize_layout; +static inline bool task_avdcache_permnoaudit(struct task_security_struct *tsec) +{ + return (tsec->avdcache.permissive_neveraudit && + tsec->sid == tsec->avdcache.sid && + tsec->avdcache.seqno == avc_policy_seqno()); +} + enum label_initialized { LABEL_INVALID, /* invalid or not initialized */ LABEL_INITIALIZED, /* initialized */ From 9ab71d9204c32a9814d38528d066fdf6fa128604 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 18 Jun 2025 12:17:33 -0400 Subject: [PATCH 5/6] selinux: add __GFP_NOWARN to hashtab_init() allocations As reported by syzbot, hashtab_init() can be affected by abnormally large policy loads which would cause the kernel's allocator to emit a warning in some configurations. Since the SELinux hashtab_init() code handles the case where the allocation fails, due to a large request or some other reason, we can safely add the __GFP_NOWARN flag to squelch these abnormally large allocation warnings. Reported-by: syzbot+bc2c99c2929c3d219fb3@syzkaller.appspotmail.com Tested-by: syzbot+bc2c99c2929c3d219fb3@syzkaller.appspotmail.com Signed-off-by: Paul Moore --- security/selinux/ss/hashtab.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 383fd2d70878..1382eb3bfde1 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -40,7 +40,8 @@ int hashtab_init(struct hashtab *h, u32 nel_hint) h->htable = NULL; if (size) { - h->htable = kcalloc(size, sizeof(*h->htable), GFP_KERNEL); + h->htable = kcalloc(size, sizeof(*h->htable), + GFP_KERNEL | __GFP_NOWARN); if (!h->htable) return -ENOMEM; h->size = size; From ee79ba39b3d6fdcfa53de6519d7e259e284e78f7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 15 Jun 2025 03:01:54 +0100 Subject: [PATCH 6/6] selinux: don't bother with selinuxfs_info_free() on failures Failures in sel_fill_super() will be followed by sel_kill_sb(), which will call selinuxfs_info_free() anyway. Signed-off-by: Al Viro Reviewed-by: Christian Brauner [PM: subj and description tweaks] Signed-off-by: Paul Moore --- security/selinux/selinuxfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 95765374f58d..9aa1d03ab612 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -2098,8 +2098,6 @@ err: pr_err("SELinux: %s: failed while creating inodes\n", __func__); - selinux_fs_info_free(sb); - return ret; }