Skip to content

Commit 51e3b98

Browse files
committed
Merge tag 'selinux-pr-20251201' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore: - Improve the granularity of SELinux labeling for memfd files Currently when creating a memfd file, SELinux treats it the same as any other tmpfs, or hugetlbfs, file. While simple, the drawback is that it is not possible to differentiate between memfd and tmpfs files. This adds a call to the security_inode_init_security_anon() LSM hook and wires up SELinux to provide a set of memfd specific access controls, including the ability to control the execution of memfds. As usual, the commit message has more information. - Improve the SELinux AVC lookup performance Adopt MurmurHash3 for the SELinux AVC hash function instead of the custom hash function currently used. MurmurHash3 is already used for the SELinux access vector table so the impact to the code is minimal, and performance tests have shown improvements in both hash distribution and latency. See the commit message for the performance measurments. - Introduce a Kconfig option for the SELinux AVC bucket/slot size While we have the ability to grow the number of AVC hash buckets today, the size of the buckets (slot size) is fixed at 512. This pull request makes that slot size configurable at build time through a new Kconfig knob, CONFIG_SECURITY_SELINUX_AVC_HASH_BITS. * tag 'selinux-pr-20251201' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: improve bucket distribution uniformity of avc_hash() selinux: Move avtab_hash() to a shared location for future reuse selinux: Introduce a new config to make avc cache slot size adjustable memfd,selinux: call security_inode_init_security_anon()
2 parents 121cc35 + 20d387d commit 51e3b98

11 files changed

Lines changed: 110 additions & 47 deletions

File tree

include/linux/memfd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include <linux/file.h>
66

7+
#define MEMFD_ANON_NAME "[memfd]"
8+
79
#ifdef CONFIG_MEMFD_CREATE
810
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
911
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);

mm/memfd.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ static struct file *alloc_file(const char *name, unsigned int flags)
460460
{
461461
unsigned int *file_seals;
462462
struct file *file;
463+
struct inode *inode;
464+
int err = 0;
463465

464466
if (flags & MFD_HUGETLB) {
465467
file = hugetlb_file_setup(name, 0, VM_NORESERVE,
@@ -471,12 +473,20 @@ static struct file *alloc_file(const char *name, unsigned int flags)
471473
}
472474
if (IS_ERR(file))
473475
return file;
476+
477+
inode = file_inode(file);
478+
err = security_inode_init_security_anon(inode,
479+
&QSTR(MEMFD_ANON_NAME), NULL);
480+
if (err) {
481+
fput(file);
482+
file = ERR_PTR(err);
483+
return file;
484+
}
485+
474486
file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
475487
file->f_flags |= O_LARGEFILE;
476488

477489
if (flags & MFD_NOEXEC_SEAL) {
478-
struct inode *inode = file_inode(file);
479-
480490
inode->i_mode &= ~0111;
481491
file_seals = memfd_file_seals_ptr(file);
482492
if (file_seals) {

security/selinux/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE
6969

7070
If unsure, keep the default value.
7171

72+
config SECURITY_SELINUX_AVC_HASH_BITS
73+
int "SELinux avc hashtable size"
74+
depends on SECURITY_SELINUX
75+
range 9 14
76+
default 9
77+
help
78+
This option sets the number of buckets used in the AVC hash table
79+
to 2^SECURITY_SELINUX_AVC_HASH_BITS. A higher value helps maintain
80+
shorter chain lengths especially when expanding AVC nodes via
81+
/sys/fs/selinux/avc/cache_threshold.
82+
7283
config SECURITY_SELINUX_DEBUG
7384
bool "SELinux kernel debugging support"
7485
depends on SECURITY_SELINUX

security/selinux/avc.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@
3030
#include "avc.h"
3131
#include "avc_ss.h"
3232
#include "classmap.h"
33+
#include "hash.h"
3334

3435
#define CREATE_TRACE_POINTS
3536
#include <trace/events/avc.h>
3637

37-
#define AVC_CACHE_SLOTS 512
38-
#define AVC_DEF_CACHE_THRESHOLD 512
39-
#define AVC_CACHE_RECLAIM 16
38+
#define AVC_CACHE_SLOTS (1 << CONFIG_SECURITY_SELINUX_AVC_HASH_BITS)
39+
#define AVC_DEF_CACHE_THRESHOLD AVC_CACHE_SLOTS
40+
#define AVC_CACHE_RECLAIM 16
4041

4142
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
4243
#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;
124125

125126
static inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass)
126127
{
127-
return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
128+
return av_hash(ssid, tsid, (u32)tclass, (u32)(AVC_CACHE_SLOTS - 1));
128129
}
129130

130131
/**

security/selinux/hooks.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#include <linux/fanotify.h>
9494
#include <linux/io_uring/cmd.h>
9595
#include <uapi/linux/lsm.h>
96+
#include <linux/memfd.h>
9697

9798
#include "initcalls.h"
9899
#include "avc.h"
@@ -2320,6 +2321,10 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23202321
new_crsec = selinux_cred(bprm->cred);
23212322
isec = inode_security(inode);
23222323

2324+
if (WARN_ON(isec->sclass != SECCLASS_FILE &&
2325+
isec->sclass != SECCLASS_MEMFD_FILE))
2326+
return -EACCES;
2327+
23232328
/* Default to the current task SID. */
23242329
new_crsec->sid = old_crsec->sid;
23252330
new_crsec->osid = old_crsec->sid;
@@ -2372,8 +2377,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23722377
ad.u.file = bprm->file;
23732378

23742379
if (new_crsec->sid == old_crsec->sid) {
2375-
rc = avc_has_perm(old_crsec->sid, isec->sid,
2376-
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
2380+
rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
2381+
FILE__EXECUTE_NO_TRANS, &ad);
23772382
if (rc)
23782383
return rc;
23792384
} else {
@@ -2383,8 +2388,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23832388
if (rc)
23842389
return rc;
23852390

2386-
rc = avc_has_perm(new_crsec->sid, isec->sid,
2387-
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
2391+
rc = avc_has_perm(new_crsec->sid, isec->sid, isec->sclass,
2392+
FILE__ENTRYPOINT, &ad);
23882393
if (rc)
23892394
return rc;
23902395

@@ -2979,10 +2984,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
29792984
struct common_audit_data ad;
29802985
struct inode_security_struct *isec;
29812986
int rc;
2987+
bool is_memfd = false;
29822988

29832989
if (unlikely(!selinux_initialized()))
29842990
return 0;
29852991

2992+
if (name != NULL && name->name != NULL &&
2993+
!strcmp(name->name, MEMFD_ANON_NAME)) {
2994+
if (!selinux_policycap_memfd_class())
2995+
return 0;
2996+
is_memfd = true;
2997+
}
2998+
29862999
isec = selinux_inode(inode);
29873000

29883001
/*
@@ -3002,7 +3015,10 @@ static int selinux_inode_init_security_anon(struct inode *inode,
30023015
isec->sclass = context_isec->sclass;
30033016
isec->sid = context_isec->sid;
30043017
} else {
3005-
isec->sclass = SECCLASS_ANON_INODE;
3018+
if (is_memfd)
3019+
isec->sclass = SECCLASS_MEMFD_FILE;
3020+
else
3021+
isec->sclass = SECCLASS_ANON_INODE;
30063022
rc = security_transition_sid(
30073023
sid, sid,
30083024
isec->sclass, name, &isec->sid);

security/selinux/include/classmap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = {
179179
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
180180
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
181181
{ "user_namespace", { "create", NULL } },
182+
{ "memfd_file",
183+
{ COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
182184
/* last one */ { NULL, {} }
183185
};
184186

security/selinux/include/hash.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef _SELINUX_HASH_H_
4+
#define _SELINUX_HASH_H_
5+
6+
/*
7+
* Based on MurmurHash3, written by Austin Appleby and placed in the
8+
* public domain.
9+
*/
10+
static inline u32 av_hash(u32 key1, u32 key2, u32 key3, u32 mask)
11+
{
12+
static const u32 c1 = 0xcc9e2d51;
13+
static const u32 c2 = 0x1b873593;
14+
static const u32 r1 = 15;
15+
static const u32 r2 = 13;
16+
static const u32 m = 5;
17+
static const u32 n = 0xe6546b64;
18+
19+
u32 hash = 0;
20+
21+
#define mix(input) \
22+
do { \
23+
u32 v = input; \
24+
v *= c1; \
25+
v = (v << r1) | (v >> (32 - r1)); \
26+
v *= c2; \
27+
hash ^= v; \
28+
hash = (hash << r2) | (hash >> (32 - r2)); \
29+
hash = hash * m + n; \
30+
} while (0)
31+
32+
mix(key1);
33+
mix(key2);
34+
mix(key3);
35+
36+
#undef mix
37+
38+
hash ^= hash >> 16;
39+
hash *= 0x85ebca6b;
40+
hash ^= hash >> 13;
41+
hash *= 0xc2b2ae35;
42+
hash ^= hash >> 16;
43+
44+
return hash & mask;
45+
}
46+
47+
#endif /* _SELINUX_HASH_H_ */

security/selinux/include/policycap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum {
1818
POLICYDB_CAP_NETIF_WILDCARD,
1919
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
2020
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
21+
POLICYDB_CAP_MEMFD_CLASS,
2122
__POLICYDB_CAP_MAX
2223
};
2324
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)

security/selinux/include/policycap_names.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
2121
"netif_wildcard",
2222
"genfs_seclabel_wildcard",
2323
"functionfs_seclabel",
24+
"memfd_class",
2425
};
2526
/* clang-format on */
2627

security/selinux/include/security.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void)
209209
selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]);
210210
}
211211

212+
static inline bool selinux_policycap_memfd_class(void)
213+
{
214+
return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]);
215+
}
216+
212217
struct selinux_policy_convert_data;
213218

214219
struct selinux_load_state {

0 commit comments

Comments
 (0)