]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selinux: optimize selinux_inode_getattr/permission() based on neveraudit|permissive
authorStephen Smalley <stephen.smalley.work@gmail.com>
Wed, 21 May 2025 14:41:25 +0000 (10:41 -0400)
committerPaul Moore <paul@paul-moore.com>
Thu, 19 Jun 2025 21:23:05 +0000 (17:23 -0400)
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 <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
security/selinux/hooks.c
security/selinux/include/objsec.h

index 595ceb314aeb3ee12a5966792e1cda88b2bdf107..335fbf76cdd27636ce86841d95620b3b27137d55 100644 (file)
@@ -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);
 }
 
index 6ee7dc4dfd6e0abada804f90a73de5aaa45eba7d..1d7ac59015a12d07c3d40d7cf8434e2e9fb63aac 100644 (file)
@@ -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 */