summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/selinux/Kconfig11
-rw-r--r--security/selinux/avc.c9
-rw-r--r--security/selinux/hooks.c26
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/hash.h47
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h5
-rw-r--r--security/selinux/ss/avtab.c39
9 files changed, 96 insertions, 45 deletions
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 61abc1e094a8..5588c4d573f6 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -69,6 +69,17 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE
If unsure, keep the default value.
+config SECURITY_SELINUX_AVC_HASH_BITS
+ int "SELinux avc hashtable size"
+ depends on SECURITY_SELINUX
+ range 9 14
+ default 9
+ help
+ This option sets the number of buckets used in the AVC hash table
+ to 2^SECURITY_SELINUX_AVC_HASH_BITS. A higher value helps maintain
+ shorter chain lengths especially when expanding AVC nodes via
+ /sys/fs/selinux/avc/cache_threshold.
+
config SECURITY_SELINUX_DEBUG
bool "SELinux kernel debugging support"
depends on SECURITY_SELINUX
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 430b0e23ee00..8f77b9a732e1 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -30,13 +30,14 @@
#include "avc.h"
#include "avc_ss.h"
#include "classmap.h"
+#include "hash.h"
#define CREATE_TRACE_POINTS
#include <trace/events/avc.h>
-#define AVC_CACHE_SLOTS 512
-#define AVC_DEF_CACHE_THRESHOLD 512
-#define AVC_CACHE_RECLAIM 16
+#define AVC_CACHE_SLOTS (1 << CONFIG_SECURITY_SELINUX_AVC_HASH_BITS)
+#define AVC_DEF_CACHE_THRESHOLD AVC_CACHE_SLOTS
+#define AVC_CACHE_RECLAIM 16
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field)
@@ -124,7 +125,7 @@ static struct kmem_cache *avc_xperms_cachep __ro_after_init;
static inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
- return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
+ return av_hash(ssid, tsid, (u32)tclass, (u32)(AVC_CACHE_SLOTS - 1));
}
/**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 139df920f582..712d0b18a30c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -93,6 +93,7 @@
#include <linux/fanotify.h>
#include <linux/io_uring/cmd.h>
#include <uapi/linux/lsm.h>
+#include <linux/memfd.h>
#include "initcalls.h"
#include "avc.h"
@@ -2320,6 +2321,10 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
new_crsec = selinux_cred(bprm->cred);
isec = inode_security(inode);
+ if (WARN_ON(isec->sclass != SECCLASS_FILE &&
+ isec->sclass != SECCLASS_MEMFD_FILE))
+ return -EACCES;
+
/* Default to the current task SID. */
new_crsec->sid = old_crsec->sid;
new_crsec->osid = old_crsec->sid;
@@ -2372,8 +2377,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
ad.u.file = bprm->file;
if (new_crsec->sid == old_crsec->sid) {
- rc = avc_has_perm(old_crsec->sid, isec->sid,
- SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
+ rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
+ FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
@@ -2383,8 +2388,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
if (rc)
return rc;
- rc = avc_has_perm(new_crsec->sid, isec->sid,
- SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
+ rc = avc_has_perm(new_crsec->sid, isec->sid, isec->sclass,
+ FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
@@ -2979,10 +2984,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
struct common_audit_data ad;
struct inode_security_struct *isec;
int rc;
+ bool is_memfd = false;
if (unlikely(!selinux_initialized()))
return 0;
+ if (name != NULL && name->name != NULL &&
+ !strcmp(name->name, MEMFD_ANON_NAME)) {
+ if (!selinux_policycap_memfd_class())
+ return 0;
+ is_memfd = true;
+ }
+
isec = selinux_inode(inode);
/*
@@ -3002,7 +3015,10 @@ static int selinux_inode_init_security_anon(struct inode *inode,
isec->sclass = context_isec->sclass;
isec->sid = context_isec->sid;
} else {
- isec->sclass = SECCLASS_ANON_INODE;
+ if (is_memfd)
+ isec->sclass = SECCLASS_MEMFD_FILE;
+ else
+ isec->sclass = SECCLASS_ANON_INODE;
rc = security_transition_sid(
sid, sid,
isec->sclass, name, &isec->sid);
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 5665aa5e7853..3ec85142771f 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = {
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
{ "user_namespace", { "create", NULL } },
+ { "memfd_file",
+ { COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
/* last one */ { NULL, {} }
};
diff --git a/security/selinux/include/hash.h b/security/selinux/include/hash.h
new file mode 100644
index 000000000000..18956dbef8ff
--- /dev/null
+++ b/security/selinux/include/hash.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _SELINUX_HASH_H_
+#define _SELINUX_HASH_H_
+
+/*
+ * Based on MurmurHash3, written by Austin Appleby and placed in the
+ * public domain.
+ */
+static inline u32 av_hash(u32 key1, u32 key2, u32 key3, u32 mask)
+{
+ static const u32 c1 = 0xcc9e2d51;
+ static const u32 c2 = 0x1b873593;
+ static const u32 r1 = 15;
+ static const u32 r2 = 13;
+ static const u32 m = 5;
+ static const u32 n = 0xe6546b64;
+
+ u32 hash = 0;
+
+#define mix(input) \
+ do { \
+ u32 v = input; \
+ v *= c1; \
+ v = (v << r1) | (v >> (32 - r1)); \
+ v *= c2; \
+ hash ^= v; \
+ hash = (hash << r2) | (hash >> (32 - r2)); \
+ hash = hash * m + n; \
+ } while (0)
+
+ mix(key1);
+ mix(key2);
+ mix(key3);
+
+#undef mix
+
+ hash ^= hash >> 16;
+ hash *= 0x85ebca6b;
+ hash ^= hash >> 13;
+ hash *= 0xc2b2ae35;
+ hash ^= hash >> 16;
+
+ return hash & mask;
+}
+
+#endif /* _SELINUX_HASH_H_ */
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index 135a969f873c..231d02227e59 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -18,6 +18,7 @@ enum {
POLICYDB_CAP_NETIF_WILDCARD,
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
+ POLICYDB_CAP_MEMFD_CLASS,
__POLICYDB_CAP_MAX
};
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index ff8882887651..454dab37bda3 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"netif_wildcard",
"genfs_seclabel_wildcard",
"functionfs_seclabel",
+ "memfd_class",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0f954a40d3fc..5d1dad8058b1 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void)
selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]);
}
+static inline bool selinux_policycap_memfd_class(void)
+{
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]);
+}
+
struct selinux_policy_convert_data;
struct selinux_load_state {
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace..d12ca337e649 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -20,48 +20,15 @@
#include <linux/errno.h>
#include "avtab.h"
#include "policydb.h"
+#include "hash.h"
static struct kmem_cache *avtab_node_cachep __ro_after_init;
static struct kmem_cache *avtab_xperms_cachep __ro_after_init;
-/* Based on MurmurHash3, written by Austin Appleby and placed in the
- * public domain.
- */
static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
{
- static const u32 c1 = 0xcc9e2d51;
- static const u32 c2 = 0x1b873593;
- static const u32 r1 = 15;
- static const u32 r2 = 13;
- static const u32 m = 5;
- static const u32 n = 0xe6546b64;
-
- u32 hash = 0;
-
-#define mix(input) \
- do { \
- u32 v = input; \
- v *= c1; \
- v = (v << r1) | (v >> (32 - r1)); \
- v *= c2; \
- hash ^= v; \
- hash = (hash << r2) | (hash >> (32 - r2)); \
- hash = hash * m + n; \
- } while (0)
-
- mix(keyp->target_class);
- mix(keyp->target_type);
- mix(keyp->source_type);
-
-#undef mix
-
- hash ^= hash >> 16;
- hash *= 0x85ebca6b;
- hash ^= hash >> 13;
- hash *= 0xc2b2ae35;
- hash ^= hash >> 16;
-
- return hash & mask;
+ return av_hash((u32)keyp->target_class, (u32)keyp->target_type,
+ (u32)keyp->source_type, mask);
}
static struct avtab_node *avtab_insert_node(struct avtab *h,