lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <20250610172226.1470741-21-stephen.smalley.work@gmail.com> Date: Tue, 10 Jun 2025 13:21:51 -0400 From: Stephen Smalley <stephen.smalley.work@...il.com> To: selinux@...r.kernel.org Cc: paul@...l-moore.com, omosnace@...hat.com, netdev@...r.kernel.org, Stephen Smalley <stephen.smalley.work@...il.com> Subject: [PATCH v4 20/42] selinux: maintain a small cache in the global SID table Maintain a small cache in the global SID table to avoid needing to map the context string each time we use a given SID in a different namespace than the original one. Signed-off-by: Stephen Smalley <stephen.smalley.work@...il.com> --- security/selinux/Kconfig | 11 +++ security/selinux/global_sidtab.c | 58 ++++++++++++++++ security/selinux/hooks.c | 1 + security/selinux/include/global_sidtab.h | 7 ++ security/selinux/include/sidtab.h | 29 +++++++- security/selinux/ss/sidtab.c | 85 ++++++++++++++++++++++++ 6 files changed, 189 insertions(+), 2 deletions(-) diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index aa25da389c46..f7bd54aa136c 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -141,3 +141,14 @@ config SECURITY_SELINUX_MAXNSDEPTH help This option sets the default maximum depth of SELinux namespaces. The value may be viewed or modified via /sys/fs/selinux/maxnsdepth. + +config SECURITY_SELINUX_SS_SID_CACHE_SIZE + int "Global SID to security server SID translation cache size" + depends on SECURITY_SELINUX_NS + default 4 + help + This option defines the size of the global SID -> security server + SID cache, which improves the performance of mapping global SIDs. + Setting this option to 0 disables the cache completely. + + If unsure, keep the default value. diff --git a/security/selinux/global_sidtab.c b/security/selinux/global_sidtab.c index 48934b429c8c..e1acf6607788 100644 --- a/security/selinux/global_sidtab.c +++ b/security/selinux/global_sidtab.c @@ -119,6 +119,11 @@ static int map_global_sid_to_ss(struct selinux_state *state, u32 sid, int rc; char *scontext; u32 scontext_len; +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 + struct sidtab_ss_sid_cache *cache; + unsigned long flags; + int i, first, last; +#endif if (sid <= SECINITSID_NUM) { *ss_sid = sid; @@ -136,6 +141,16 @@ static int map_global_sid_to_ss(struct selinux_state *state, u32 sid, rcu_read_unlock(); return 0; } +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 + cache = &entry->ss_sid_cache; + for (i = cache->first; i >= 0 && i <= cache->last; i++) { + if (cache->state[i] == state && cache->ss_sid[i]) { + *ss_sid = cache->ss_sid[i]; + rcu_read_unlock(); + return 0; + } + } +#endif rcu_read_unlock(); rc = global_sid_to_context(sid, &scontext, &scontext_len); @@ -144,10 +159,53 @@ static int map_global_sid_to_ss(struct selinux_state *state, u32 sid, rc = selinux_ss_context_to_sid_force(state, scontext, scontext_len, ss_sid, gfp); +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 + if (rc == 0) { + spin_lock_irqsave(&global_sidtab.lock, flags); + entry = sidtab_search_entry_force(&global_sidtab, sid); + if (!entry) { + spin_unlock_irqrestore(&global_sidtab.lock, flags); + return -EINVAL; + } + cache = &entry->ss_sid_cache; + first = cache->first; + last = cache->last; + for (i = 0; i < ARRAY_SIZE(cache->ss_sid) && cache->ss_sid[i]; + i++) + ; + if (i < ARRAY_SIZE(cache->ss_sid)) { + WRITE_ONCE(cache->ss_sid[i], *ss_sid); + /* ensure that the SID is written before the state */ + smp_wmb(); + WRITE_ONCE(cache->state[i], state); + } else { + if (first == -1) + i = 0; + else + i = first; + WRITE_ONCE(cache->ss_sid[i], *ss_sid); + /* ensure that the SID is written before the state */ + smp_wmb(); + WRITE_ONCE(cache->state[i], state); + } + /* ensure that state is updated before indices */ + smp_wmb(); + if (first == -1 || i < first) + WRITE_ONCE(cache->first, i); + if (last == -1 || i > last) + WRITE_ONCE(cache->last, i); + spin_unlock_irqrestore(&global_sidtab.lock, flags); + } +#endif kfree(scontext); return rc; } +void global_sidtab_invalidate_state(struct selinux_state *state) +{ + sidtab_invalidate_state(&global_sidtab, state); +} + static int map_ss_sid_to_global(struct selinux_state *state, u32 ss_sid, u32 *out_sid, gfp_t gfp) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1f03c799b4cd..fd889db63238 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7848,6 +7848,7 @@ static void selinux_state_free(struct work_struct *work) do { parent = state->parent; + global_sidtab_invalidate_state(state); if (state->status_page) __free_page(state->status_page); selinux_policy_free(state->policy); diff --git a/security/selinux/include/global_sidtab.h b/security/selinux/include/global_sidtab.h index 2e06bb865326..bf450d775b66 100644 --- a/security/selinux/include/global_sidtab.h +++ b/security/selinux/include/global_sidtab.h @@ -9,11 +9,18 @@ #ifdef CONFIG_SECURITY_SELINUX_NS extern int global_sidtab_init(void); + +struct selinux_state; +void global_sidtab_invalidate_state(struct selinux_state *state); #else static inline int global_sidtab_init(void) { return 0; } + +static inline void global_sidtab_invalidate_state(struct selinux_state *state) +{ +} #endif /* CONFIG_SECURITY_SELINUX_NS */ #endif /* _GLOBAL_SIDTAB_H_ */ diff --git a/security/selinux/include/sidtab.h b/security/selinux/include/sidtab.h index 61389c588775..2df3ac0df935 100644 --- a/security/selinux/include/sidtab.h +++ b/security/selinux/include/sidtab.h @@ -18,6 +18,16 @@ #include "context.h" +#ifdef CONFIG_SECURITY_SELINUX_NS +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 +struct sidtab_ss_sid_cache { + u32 ss_sid[CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE]; + struct selinux_state *state[CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE]; + int first, last; +}; +#endif +#endif + struct sidtab_entry { u32 sid; u32 hash; @@ -27,8 +37,11 @@ struct sidtab_entry { #endif struct hlist_node list; #ifdef CONFIG_SECURITY_SELINUX_NS - u32 ss_sid; // global SID table only - struct selinux_state *state; // global SID table only + u32 ss_sid; + struct selinux_state *state; +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 + struct sidtab_ss_sid_cache ss_sid_cache; +#endif #endif }; @@ -166,4 +179,16 @@ static inline int sidtab_sid2str_get(struct sidtab *s, } #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */ +#ifdef CONFIG_SECURITY_SELINUX_NS +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 +extern void sidtab_invalidate_state(struct sidtab *s, + struct selinux_state *state); +#else +static inline void sidtab_invalidate_state(struct sidtab *s, + struct selinux_state *state) +{ +} +#endif /* CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 */ +#endif /* CONFIG_SECURITY_SELINUX_NS */ + #endif /* _SS_SIDTAB_H_ */ diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 19991f01cd20..eea37f78bec9 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -648,3 +648,88 @@ int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry, char **out, } #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */ + +#ifdef CONFIG_SECURITY_SELINUX_NS +#if CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 +static void sidtab_invalidate_state_entry(struct sidtab_entry *entry, + struct selinux_state *state) +{ + struct sidtab_ss_sid_cache *cache; + int i, first, last; + + cache = &entry->ss_sid_cache; + first = cache->first; + last = cache->last; + for (i = first; i >= 0 && i <= last; i++) { + if (cache->state[i] == state) { + WRITE_ONCE(cache->ss_sid[i], 0); + WRITE_ONCE(cache->state[i], NULL); + if (first == i) { + for (first = i + 1; first <= last && + !cache->ss_sid[first]; first++) + ; + if (first == (i+1)) + first = -1; + WRITE_ONCE(cache->first, first); + } + if (last == i) { + for (last = i - 1; last >= first && + last >= 0 && + !cache->ss_sid[last]; last--) + ; + if (last == (i-1)) + last = -1; + WRITE_ONCE(cache->last, last); + } + return; + } + } +} + +static void sidtab_invalidate_state_tree(union sidtab_entry_inner entry, + u32 level, + struct selinux_state *state) +{ + u32 i; + + if (level != 0) { + struct sidtab_node_inner *node = entry.ptr_inner; + + if (!node) + return; + + for (i = 0; i < SIDTAB_INNER_ENTRIES; i++) + sidtab_invalidate_state_tree(node->entries[i], + level - 1, state); + } else { + struct sidtab_node_leaf *node = entry.ptr_leaf; + + if (!node) + return; + + for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++) + sidtab_invalidate_state_entry(&node->entries[i], state); + } +} + +void sidtab_invalidate_state(struct sidtab *s, struct selinux_state *state) +{ + u32 i, level; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < SECINITSID_NUM; i++) + if (s->isids[i].set) + sidtab_invalidate_state_entry(&s->isids[i].entry, state); + + level = SIDTAB_MAX_LEVEL; + while (level && !s->roots[level].ptr_inner) + --level; + + sidtab_invalidate_state_tree(s->roots[level], level, state); + + spin_unlock_irqrestore(&s->lock, flags); +} +#endif /* CONFIG_SECURITY_SELINUX_SS_SID_CACHE_SIZE > 0 */ +#endif /* CONFIG_SECURITY_SELINUX_NS */ -- 2.49.0
Powered by blists - more mailing lists