mirror of https://github.com/torvalds/linux.git
selinux/stable-6.19 PR 20251201
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmkuAKEUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXPKeA/8DSW+sTkQ9BMGGnyuH1uU/r84qtVh Ft6pnIPzrogE/GKcQeFgFA9D7gQbB8J39PSxZLS3lp0UiuPCuq+D09L+uzDKzDCD Avfe84dwsI5OiplPKyHiG3bF9W2+A1zkwH2j+5uC6yF8v9J9vglo4u5vAYeE2wxA X4b2r9jMm7WJ/KFNiSiiLGEhOSjVVUrJULcmWMRPPruplPDC4dLnqYTWTbkrfF8h /oXv/+ssqbj6FqfL4WaRnjN8GgZcwaWy1qu9LVlZ40iphpbVAyPBJPLJS6Q4hhOl mMHUbYkxALPyW7riQxoXAegQjJyGgKn8Bli9U6bkiKFA2yeIhJFX+OyV1SlOAs/J g6s5XfeCzqY0Tw3eqvT1YRhp10GcA7EtBYvhAe5ARq7PkMoqxmiI587piVX9hbos a0AH9CDNoOw+8QXx27sOoD1YIaiYD9fikXKymrzRRaW/GX6i43XIKiELBMuKoIVZ iwualvQiGBLLczzm5rdqPcLgp09Agn4AHfvFWXKFgS4+IJGKjeeXNOjsp9oFEivq RnXmDpa+nBud5zeTSeSpOY2L0pvuIG5N25N6U9bTsDe+4Y6p0qIAUy8e4sQ0PA8P xyp9/fcNr9jwHeLTjDbxZqZ+MU3GLIIVPdl0zq4z2J8nhkW3wD3pQX6B4qPIuXLx YP3nwhAT9T+hU7w= =IvVa -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
51e3b98d73
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
|
|
||||||
|
#define MEMFD_ANON_NAME "[memfd]"
|
||||||
|
|
||||||
#ifdef CONFIG_MEMFD_CREATE
|
#ifdef CONFIG_MEMFD_CREATE
|
||||||
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
|
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
|
||||||
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
|
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
|
||||||
|
|
|
||||||
14
mm/memfd.c
14
mm/memfd.c
|
|
@ -460,6 +460,8 @@ static struct file *alloc_file(const char *name, unsigned int flags)
|
||||||
{
|
{
|
||||||
unsigned int *file_seals;
|
unsigned int *file_seals;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
struct inode *inode;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (flags & MFD_HUGETLB) {
|
if (flags & MFD_HUGETLB) {
|
||||||
file = hugetlb_file_setup(name, 0, VM_NORESERVE,
|
file = hugetlb_file_setup(name, 0, VM_NORESERVE,
|
||||||
|
|
@ -471,12 +473,20 @@ static struct file *alloc_file(const char *name, unsigned int flags)
|
||||||
}
|
}
|
||||||
if (IS_ERR(file))
|
if (IS_ERR(file))
|
||||||
return file;
|
return file;
|
||||||
|
|
||||||
|
inode = file_inode(file);
|
||||||
|
err = security_inode_init_security_anon(inode,
|
||||||
|
&QSTR(MEMFD_ANON_NAME), NULL);
|
||||||
|
if (err) {
|
||||||
|
fput(file);
|
||||||
|
file = ERR_PTR(err);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
|
file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
|
||||||
file->f_flags |= O_LARGEFILE;
|
file->f_flags |= O_LARGEFILE;
|
||||||
|
|
||||||
if (flags & MFD_NOEXEC_SEAL) {
|
if (flags & MFD_NOEXEC_SEAL) {
|
||||||
struct inode *inode = file_inode(file);
|
|
||||||
|
|
||||||
inode->i_mode &= ~0111;
|
inode->i_mode &= ~0111;
|
||||||
file_seals = memfd_file_seals_ptr(file);
|
file_seals = memfd_file_seals_ptr(file);
|
||||||
if (file_seals) {
|
if (file_seals) {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,17 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE
|
||||||
|
|
||||||
If unsure, keep the default value.
|
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
|
config SECURITY_SELINUX_DEBUG
|
||||||
bool "SELinux kernel debugging support"
|
bool "SELinux kernel debugging support"
|
||||||
depends on SECURITY_SELINUX
|
depends on SECURITY_SELINUX
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,14 @@
|
||||||
#include "avc.h"
|
#include "avc.h"
|
||||||
#include "avc_ss.h"
|
#include "avc_ss.h"
|
||||||
#include "classmap.h"
|
#include "classmap.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
#define CREATE_TRACE_POINTS
|
#define CREATE_TRACE_POINTS
|
||||||
#include <trace/events/avc.h>
|
#include <trace/events/avc.h>
|
||||||
|
|
||||||
#define AVC_CACHE_SLOTS 512
|
#define AVC_CACHE_SLOTS (1 << CONFIG_SECURITY_SELINUX_AVC_HASH_BITS)
|
||||||
#define AVC_DEF_CACHE_THRESHOLD 512
|
#define AVC_DEF_CACHE_THRESHOLD AVC_CACHE_SLOTS
|
||||||
#define AVC_CACHE_RECLAIM 16
|
#define AVC_CACHE_RECLAIM 16
|
||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
|
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
|
||||||
#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field)
|
#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)
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
#include <linux/fanotify.h>
|
#include <linux/fanotify.h>
|
||||||
#include <linux/io_uring/cmd.h>
|
#include <linux/io_uring/cmd.h>
|
||||||
#include <uapi/linux/lsm.h>
|
#include <uapi/linux/lsm.h>
|
||||||
|
#include <linux/memfd.h>
|
||||||
|
|
||||||
#include "initcalls.h"
|
#include "initcalls.h"
|
||||||
#include "avc.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);
|
new_crsec = selinux_cred(bprm->cred);
|
||||||
isec = inode_security(inode);
|
isec = inode_security(inode);
|
||||||
|
|
||||||
|
if (WARN_ON(isec->sclass != SECCLASS_FILE &&
|
||||||
|
isec->sclass != SECCLASS_MEMFD_FILE))
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
/* Default to the current task SID. */
|
/* Default to the current task SID. */
|
||||||
new_crsec->sid = old_crsec->sid;
|
new_crsec->sid = old_crsec->sid;
|
||||||
new_crsec->osid = 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;
|
ad.u.file = bprm->file;
|
||||||
|
|
||||||
if (new_crsec->sid == old_crsec->sid) {
|
if (new_crsec->sid == old_crsec->sid) {
|
||||||
rc = avc_has_perm(old_crsec->sid, isec->sid,
|
rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
|
||||||
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
|
FILE__EXECUTE_NO_TRANS, &ad);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2383,8 +2388,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = avc_has_perm(new_crsec->sid, isec->sid,
|
rc = avc_has_perm(new_crsec->sid, isec->sid, isec->sclass,
|
||||||
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
|
FILE__ENTRYPOINT, &ad);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
|
@ -2979,10 +2984,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
|
||||||
struct common_audit_data ad;
|
struct common_audit_data ad;
|
||||||
struct inode_security_struct *isec;
|
struct inode_security_struct *isec;
|
||||||
int rc;
|
int rc;
|
||||||
|
bool is_memfd = false;
|
||||||
|
|
||||||
if (unlikely(!selinux_initialized()))
|
if (unlikely(!selinux_initialized()))
|
||||||
return 0;
|
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);
|
isec = selinux_inode(inode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -3002,7 +3015,10 @@ static int selinux_inode_init_security_anon(struct inode *inode,
|
||||||
isec->sclass = context_isec->sclass;
|
isec->sclass = context_isec->sclass;
|
||||||
isec->sid = context_isec->sid;
|
isec->sid = context_isec->sid;
|
||||||
} else {
|
} else {
|
||||||
isec->sclass = SECCLASS_ANON_INODE;
|
if (is_memfd)
|
||||||
|
isec->sclass = SECCLASS_MEMFD_FILE;
|
||||||
|
else
|
||||||
|
isec->sclass = SECCLASS_ANON_INODE;
|
||||||
rc = security_transition_sid(
|
rc = security_transition_sid(
|
||||||
sid, sid,
|
sid, sid,
|
||||||
isec->sclass, name, &isec->sid);
|
isec->sclass, name, &isec->sid);
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = {
|
||||||
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
|
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
|
||||||
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
|
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
|
||||||
{ "user_namespace", { "create", NULL } },
|
{ "user_namespace", { "create", NULL } },
|
||||||
|
{ "memfd_file",
|
||||||
|
{ COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
|
||||||
/* last one */ { NULL, {} }
|
/* last one */ { NULL, {} }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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_ */
|
||||||
|
|
@ -18,6 +18,7 @@ enum {
|
||||||
POLICYDB_CAP_NETIF_WILDCARD,
|
POLICYDB_CAP_NETIF_WILDCARD,
|
||||||
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
|
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
|
||||||
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
|
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
|
||||||
|
POLICYDB_CAP_MEMFD_CLASS,
|
||||||
__POLICYDB_CAP_MAX
|
__POLICYDB_CAP_MAX
|
||||||
};
|
};
|
||||||
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
|
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
|
||||||
"netif_wildcard",
|
"netif_wildcard",
|
||||||
"genfs_seclabel_wildcard",
|
"genfs_seclabel_wildcard",
|
||||||
"functionfs_seclabel",
|
"functionfs_seclabel",
|
||||||
|
"memfd_class",
|
||||||
};
|
};
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void)
|
||||||
selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]);
|
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_policy_convert_data;
|
||||||
|
|
||||||
struct selinux_load_state {
|
struct selinux_load_state {
|
||||||
|
|
|
||||||
|
|
@ -20,48 +20,15 @@
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include "avtab.h"
|
#include "avtab.h"
|
||||||
#include "policydb.h"
|
#include "policydb.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
static struct kmem_cache *avtab_node_cachep __ro_after_init;
|
static struct kmem_cache *avtab_node_cachep __ro_after_init;
|
||||||
static struct kmem_cache *avtab_xperms_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 inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
|
||||||
{
|
{
|
||||||
static const u32 c1 = 0xcc9e2d51;
|
return av_hash((u32)keyp->target_class, (u32)keyp->target_type,
|
||||||
static const u32 c2 = 0x1b873593;
|
(u32)keyp->source_type, mask);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct avtab_node *avtab_insert_node(struct avtab *h,
|
static struct avtab_node *avtab_insert_node(struct avtab *h,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue