Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: cache the SID -> context string translation

Translating a context struct to string can be quite slow, especially if
the context has a lot of category bits set. This can cause quite
noticeable performance impact in situations where the translation needs
to be done repeatedly. A common example is a UNIX datagram socket with
the SO_PASSSEC option enabled, which is used e.g. by systemd-journald
when receiving log messages via datagram socket. This scenario can be
reproduced with:

cat /dev/urandom | base64 | logger &
timeout 30s perf record -p $(pidof systemd-journald) -a -g
kill %1
perf report -g none --pretty raw | grep security_secid_to_secctx

Before the caching introduced by this patch, computing the context
string (security_secid_to_secctx() function) takes up ~65% of
systemd-journald's CPU time (assuming a context with 1024 categories
set and Fedora x86_64 release kernel configs). After this patch
(assuming near-perfect cache hit ratio) this overhead is reduced to just
~2%.

This patch addresses the issue by caching a certain number (compile-time
configurable) of recently used context strings to speed up repeated
translations of the same context, while using only a small amount of
memory.

The cache is integrated into the existing sidtab table by adding a field
to each entry, which when not NULL contains an RCU-protected pointer to
a cache entry containing the cached string. The cache entries are kept
in a linked list sorted according to how recently they were used. On a
cache miss when the cache is full, the least recently used entry is
removed to make space for the new entry.

The patch migrates security_sid_to_context_core() to use the cache (also
a few other functions where it was possible without too much fuss, but
these mostly use the translation for logging in case of error, which is
rare).

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1733259
Cc: Michal Sekletar <msekleta@redhat.com>
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Reviewed-by: Stephen Smalley <sds@tycho.nsa.gov>
Tested-by: Stephen Smalley <sds@tycho.nsa.gov>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
[PM: lots of merge fixups due to collisions with other sidtab patches]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Ondrej Mosnacek and committed by
Paul Moore
d97bd23c 66f8e2f0

+288 -94
+11
security/selinux/Kconfig
··· 97 97 collisions may be viewed at /sys/fs/selinux/ss/sidtab_hash_stats. If 98 98 chain lengths are high (e.g. > 20) then selecting a higher value here 99 99 will ensure that lookups times are short and stable. 100 + 101 + config SECURITY_SELINUX_SID2STR_CACHE_SIZE 102 + int "NSA SELinux SID to context string translation cache size" 103 + depends on SECURITY_SELINUX 104 + default 256 105 + help 106 + This option defines the size of the internal SID -> context string 107 + cache, which improves the performance of context to string 108 + conversion. Setting this option to 0 disables the cache completely. 109 + 110 + If unsure, keep the default value.
+84 -54
security/selinux/ss/services.c
··· 91 91 char **scontext, 92 92 u32 *scontext_len); 93 93 94 + static int sidtab_entry_to_string(struct policydb *policydb, 95 + struct sidtab *sidtab, 96 + struct sidtab_entry *entry, 97 + char **scontext, 98 + u32 *scontext_len); 99 + 94 100 static void context_struct_compute_av(struct policydb *policydb, 95 101 struct context *scontext, 96 102 struct context *tcontext, ··· 722 716 } 723 717 724 718 static int security_validtrans_handle_fail(struct selinux_state *state, 725 - struct context *ocontext, 726 - struct context *ncontext, 727 - struct context *tcontext, 719 + struct sidtab_entry *oentry, 720 + struct sidtab_entry *nentry, 721 + struct sidtab_entry *tentry, 728 722 u16 tclass) 729 723 { 730 724 struct policydb *p = &state->ss->policydb; 725 + struct sidtab *sidtab = state->ss->sidtab; 731 726 char *o = NULL, *n = NULL, *t = NULL; 732 727 u32 olen, nlen, tlen; 733 728 734 - if (context_struct_to_string(p, ocontext, &o, &olen)) 729 + if (sidtab_entry_to_string(p, sidtab, oentry, &o, &olen)) 735 730 goto out; 736 - if (context_struct_to_string(p, ncontext, &n, &nlen)) 731 + if (sidtab_entry_to_string(p, sidtab, nentry, &n, &nlen)) 737 732 goto out; 738 - if (context_struct_to_string(p, tcontext, &t, &tlen)) 733 + if (sidtab_entry_to_string(p, sidtab, tentry, &t, &tlen)) 739 734 goto out; 740 735 audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, 741 736 "op=security_validate_transition seresult=denied" ··· 758 751 { 759 752 struct policydb *policydb; 760 753 struct sidtab *sidtab; 761 - struct context *ocontext; 762 - struct context *ncontext; 763 - struct context *tcontext; 754 + struct sidtab_entry *oentry; 755 + struct sidtab_entry *nentry; 756 + struct sidtab_entry *tentry; 764 757 struct class_datum *tclass_datum; 765 758 struct constraint_node *constraint; 766 759 u16 tclass; ··· 786 779 } 787 780 tclass_datum = policydb->class_val_to_struct[tclass - 1]; 788 781 789 - ocontext = sidtab_search(sidtab, oldsid); 790 - if (!ocontext) { 782 + oentry = sidtab_search_entry(sidtab, oldsid); 783 + if (!oentry) { 791 784 pr_err("SELinux: %s: unrecognized SID %d\n", 792 785 __func__, oldsid); 793 786 rc = -EINVAL; 794 787 goto out; 795 788 } 796 789 797 - ncontext = sidtab_search(sidtab, newsid); 798 - if (!ncontext) { 790 + nentry = sidtab_search_entry(sidtab, newsid); 791 + if (!nentry) { 799 792 pr_err("SELinux: %s: unrecognized SID %d\n", 800 793 __func__, newsid); 801 794 rc = -EINVAL; 802 795 goto out; 803 796 } 804 797 805 - tcontext = sidtab_search(sidtab, tasksid); 806 - if (!tcontext) { 798 + tentry = sidtab_search_entry(sidtab, tasksid); 799 + if (!tentry) { 807 800 pr_err("SELinux: %s: unrecognized SID %d\n", 808 801 __func__, tasksid); 809 802 rc = -EINVAL; ··· 812 805 813 806 constraint = tclass_datum->validatetrans; 814 807 while (constraint) { 815 - if (!constraint_expr_eval(policydb, ocontext, ncontext, 816 - tcontext, constraint->expr)) { 808 + if (!constraint_expr_eval(policydb, &oentry->context, 809 + &nentry->context, &tentry->context, 810 + constraint->expr)) { 817 811 if (user) 818 812 rc = -EPERM; 819 813 else 820 814 rc = security_validtrans_handle_fail(state, 821 - ocontext, 822 - ncontext, 823 - tcontext, 815 + oentry, 816 + nentry, 817 + tentry, 824 818 tclass); 825 819 goto out; 826 820 } ··· 863 855 { 864 856 struct policydb *policydb; 865 857 struct sidtab *sidtab; 866 - struct context *old_context, *new_context; 858 + struct sidtab_entry *old_entry, *new_entry; 867 859 struct type_datum *type; 868 860 int index; 869 861 int rc; ··· 877 869 sidtab = state->ss->sidtab; 878 870 879 871 rc = -EINVAL; 880 - old_context = sidtab_search(sidtab, old_sid); 881 - if (!old_context) { 872 + old_entry = sidtab_search_entry(sidtab, old_sid); 873 + if (!old_entry) { 882 874 pr_err("SELinux: %s: unrecognized SID %u\n", 883 875 __func__, old_sid); 884 876 goto out; 885 877 } 886 878 887 879 rc = -EINVAL; 888 - new_context = sidtab_search(sidtab, new_sid); 889 - if (!new_context) { 880 + new_entry = sidtab_search_entry(sidtab, new_sid); 881 + if (!new_entry) { 890 882 pr_err("SELinux: %s: unrecognized SID %u\n", 891 883 __func__, new_sid); 892 884 goto out; ··· 894 886 895 887 rc = 0; 896 888 /* type/domain unchanged */ 897 - if (old_context->type == new_context->type) 889 + if (old_entry->context.type == new_entry->context.type) 898 890 goto out; 899 891 900 - index = new_context->type; 892 + index = new_entry->context.type; 901 893 while (true) { 902 894 type = policydb->type_val_to_struct[index - 1]; 903 895 BUG_ON(!type); ··· 909 901 910 902 /* @newsid is bounded by @oldsid */ 911 903 rc = 0; 912 - if (type->bounds == old_context->type) 904 + if (type->bounds == old_entry->context.type) 913 905 break; 914 906 915 907 index = type->bounds; ··· 920 912 char *new_name = NULL; 921 913 u32 length; 922 914 923 - if (!context_struct_to_string(policydb, old_context, 924 - &old_name, &length) && 925 - !context_struct_to_string(policydb, new_context, 926 - &new_name, &length)) { 915 + if (!sidtab_entry_to_string(policydb, sidtab, old_entry, 916 + &old_name, &length) && 917 + !sidtab_entry_to_string(policydb, sidtab, new_entry, 918 + &new_name, &length)) { 927 919 audit_log(audit_context(), 928 920 GFP_ATOMIC, AUDIT_SELINUX_ERR, 929 921 "op=security_bounded_transition " ··· 1263 1255 return 0; 1264 1256 } 1265 1257 1258 + static int sidtab_entry_to_string(struct policydb *p, 1259 + struct sidtab *sidtab, 1260 + struct sidtab_entry *entry, 1261 + char **scontext, u32 *scontext_len) 1262 + { 1263 + int rc = sidtab_sid2str_get(sidtab, entry, scontext, scontext_len); 1264 + 1265 + if (rc != -ENOENT) 1266 + return rc; 1267 + 1268 + rc = context_struct_to_string(p, &entry->context, scontext, 1269 + scontext_len); 1270 + if (!rc && scontext) 1271 + sidtab_sid2str_put(sidtab, entry, *scontext, *scontext_len); 1272 + return rc; 1273 + } 1274 + 1266 1275 #include "initial_sid_to_string.h" 1267 1276 1268 1277 int security_sidtab_hash_stats(struct selinux_state *state, char *page) ··· 1307 1282 { 1308 1283 struct policydb *policydb; 1309 1284 struct sidtab *sidtab; 1310 - struct context *context; 1285 + struct sidtab_entry *entry; 1311 1286 int rc = 0; 1312 1287 1313 1288 if (scontext) ··· 1338 1313 read_lock(&state->ss->policy_rwlock); 1339 1314 policydb = &state->ss->policydb; 1340 1315 sidtab = state->ss->sidtab; 1316 + 1341 1317 if (force) 1342 - context = sidtab_search_force(sidtab, sid); 1318 + entry = sidtab_search_entry_force(sidtab, sid); 1343 1319 else 1344 - context = sidtab_search(sidtab, sid); 1345 - if (!context) { 1320 + entry = sidtab_search_entry(sidtab, sid); 1321 + if (!entry) { 1346 1322 pr_err("SELinux: %s: unrecognized SID %d\n", 1347 1323 __func__, sid); 1348 1324 rc = -EINVAL; 1349 1325 goto out_unlock; 1350 1326 } 1351 - if (only_invalid && !context->len) 1352 - rc = 0; 1353 - else 1354 - rc = context_struct_to_string(policydb, context, scontext, 1355 - scontext_len); 1327 + if (only_invalid && !entry->context.len) 1328 + goto out_unlock; 1329 + 1330 + rc = sidtab_entry_to_string(policydb, sidtab, entry, scontext, 1331 + scontext_len); 1332 + 1356 1333 out_unlock: 1357 1334 read_unlock(&state->ss->policy_rwlock); 1358 1335 out: ··· 1648 1621 1649 1622 static int compute_sid_handle_invalid_context( 1650 1623 struct selinux_state *state, 1651 - struct context *scontext, 1652 - struct context *tcontext, 1624 + struct sidtab_entry *sentry, 1625 + struct sidtab_entry *tentry, 1653 1626 u16 tclass, 1654 1627 struct context *newcontext) 1655 1628 { 1656 1629 struct policydb *policydb = &state->ss->policydb; 1630 + struct sidtab *sidtab = state->ss->sidtab; 1657 1631 char *s = NULL, *t = NULL, *n = NULL; 1658 1632 u32 slen, tlen, nlen; 1659 1633 struct audit_buffer *ab; 1660 1634 1661 - if (context_struct_to_string(policydb, scontext, &s, &slen)) 1635 + if (sidtab_entry_to_string(policydb, sidtab, sentry, &s, &slen)) 1662 1636 goto out; 1663 - if (context_struct_to_string(policydb, tcontext, &t, &tlen)) 1637 + if (sidtab_entry_to_string(policydb, sidtab, tentry, &t, &tlen)) 1664 1638 goto out; 1665 1639 if (context_struct_to_string(policydb, newcontext, &n, &nlen)) 1666 1640 goto out; ··· 1720 1692 struct policydb *policydb; 1721 1693 struct sidtab *sidtab; 1722 1694 struct class_datum *cladatum = NULL; 1723 - struct context *scontext = NULL, *tcontext = NULL, newcontext; 1695 + struct context *scontext, *tcontext, newcontext; 1696 + struct sidtab_entry *sentry, *tentry; 1724 1697 struct role_trans *roletr = NULL; 1725 1698 struct avtab_key avkey; 1726 1699 struct avtab_datum *avdatum; ··· 1758 1729 policydb = &state->ss->policydb; 1759 1730 sidtab = state->ss->sidtab; 1760 1731 1761 - scontext = sidtab_search(sidtab, ssid); 1762 - if (!scontext) { 1732 + sentry = sidtab_search_entry(sidtab, ssid); 1733 + if (!sentry) { 1763 1734 pr_err("SELinux: %s: unrecognized SID %d\n", 1764 1735 __func__, ssid); 1765 1736 rc = -EINVAL; 1766 1737 goto out_unlock; 1767 1738 } 1768 - tcontext = sidtab_search(sidtab, tsid); 1769 - if (!tcontext) { 1739 + tentry = sidtab_search_entry(sidtab, tsid); 1740 + if (!tentry) { 1770 1741 pr_err("SELinux: %s: unrecognized SID %d\n", 1771 1742 __func__, tsid); 1772 1743 rc = -EINVAL; 1773 1744 goto out_unlock; 1774 1745 } 1746 + 1747 + scontext = &sentry->context; 1748 + tcontext = &tentry->context; 1775 1749 1776 1750 if (tclass && tclass <= policydb->p_classes.nprim) 1777 1751 cladatum = policydb->class_val_to_struct[tclass - 1]; ··· 1876 1844 1877 1845 /* Check the validity of the context. */ 1878 1846 if (!policydb_context_isvalid(policydb, &newcontext)) { 1879 - rc = compute_sid_handle_invalid_context(state, scontext, 1880 - tcontext, 1881 - tclass, 1882 - &newcontext); 1847 + rc = compute_sid_handle_invalid_context(state, sentry, tentry, 1848 + tclass, &newcontext); 1883 1849 if (rc) 1884 1850 goto out_unlock; 1885 1851 }
+144 -31
security/selinux/ss/sidtab.c
··· 9 9 */ 10 10 #include <linux/errno.h> 11 11 #include <linux/kernel.h> 12 + #include <linux/list.h> 13 + #include <linux/rcupdate.h> 12 14 #include <linux/slab.h> 13 15 #include <linux/sched.h> 14 16 #include <linux/spinlock.h> ··· 18 16 #include "flask.h" 19 17 #include "security.h" 20 18 #include "sidtab.h" 19 + 20 + struct sidtab_str_cache { 21 + struct rcu_head rcu_member; 22 + struct list_head lru_member; 23 + struct sidtab_entry *parent; 24 + u32 len; 25 + char str[]; 26 + }; 21 27 22 28 #define index_to_sid(index) (index + SECINITSID_NUM + 1) 23 29 #define sid_to_index(sid) (sid - (SECINITSID_NUM + 1)) ··· 44 34 hash_init(s->context_to_sid); 45 35 46 36 spin_lock_init(&s->lock); 37 + 38 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 39 + s->cache_free_slots = CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE; 40 + INIT_LIST_HEAD(&s->cache_lru_list); 41 + spin_lock_init(&s->cache_lock); 42 + #endif 43 + 47 44 return 0; 48 45 } 49 46 50 47 static u32 context_to_sid(struct sidtab *s, struct context *context) 51 48 { 52 - struct sidtab_entry_leaf *entry; 49 + struct sidtab_entry *entry; 53 50 u32 sid = 0; 54 51 55 52 rcu_read_lock(); ··· 73 56 74 57 int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context) 75 58 { 76 - struct sidtab_isid_entry *entry; 59 + struct sidtab_isid_entry *isid; 77 60 int rc; 78 61 79 62 if (sid == 0 || sid > SECINITSID_NUM) 80 63 return -EINVAL; 81 64 82 - entry = &s->isids[sid - 1]; 65 + isid = &s->isids[sid - 1]; 83 66 84 - rc = context_cpy(&entry->leaf.context, context); 67 + rc = context_cpy(&isid->entry.context, context); 85 68 if (rc) 86 69 return rc; 87 70 88 - entry->set = 1; 71 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 72 + isid->entry.cache = NULL; 73 + #endif 74 + isid->set = 1; 89 75 90 76 /* 91 77 * Multiple initial sids may map to the same context. Check that this ··· 97 77 * collision. 98 78 */ 99 79 if (!context_to_sid(s, context)) { 100 - entry->leaf.sid = sid; 101 - hash_add(s->context_to_sid, &entry->leaf.list, context->hash); 80 + isid->entry.sid = sid; 81 + hash_add(s->context_to_sid, &isid->entry.list, context->hash); 102 82 } 103 83 104 84 return 0; ··· 112 92 int entries = 0; 113 93 int max_chain_len = 0; 114 94 int cur_bucket = 0; 115 - struct sidtab_entry_leaf *entry; 95 + struct sidtab_entry *entry; 116 96 117 97 rcu_read_lock(); 118 98 hash_for_each_rcu(sidtab->context_to_sid, i, entry, list) { ··· 171 151 return 0; 172 152 } 173 153 174 - static struct sidtab_entry_leaf *sidtab_do_lookup(struct sidtab *s, u32 index, 175 - int alloc) 154 + static struct sidtab_entry *sidtab_do_lookup(struct sidtab *s, u32 index, 155 + int alloc) 176 156 { 177 157 union sidtab_entry_inner *entry; 178 158 u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES; ··· 212 192 return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES]; 213 193 } 214 194 215 - static struct context *sidtab_lookup(struct sidtab *s, u32 index) 195 + static struct sidtab_entry *sidtab_lookup(struct sidtab *s, u32 index) 216 196 { 217 197 /* read entries only after reading count */ 218 198 u32 count = smp_load_acquire(&s->count); ··· 220 200 if (index >= count) 221 201 return NULL; 222 202 223 - return &sidtab_do_lookup(s, index, 0)->context; 203 + return sidtab_do_lookup(s, index, 0); 224 204 } 225 205 226 - static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid) 206 + static struct sidtab_entry *sidtab_lookup_initial(struct sidtab *s, u32 sid) 227 207 { 228 - return s->isids[sid - 1].set ? &s->isids[sid - 1].leaf.context : NULL; 208 + return s->isids[sid - 1].set ? &s->isids[sid - 1].entry : NULL; 229 209 } 230 210 231 - static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) 211 + static struct sidtab_entry *sidtab_search_core(struct sidtab *s, u32 sid, 212 + int force) 232 213 { 233 - struct context *context; 234 - 235 214 if (sid != 0) { 215 + struct sidtab_entry *entry; 216 + 236 217 if (sid > SECINITSID_NUM) 237 - context = sidtab_lookup(s, sid_to_index(sid)); 218 + entry = sidtab_lookup(s, sid_to_index(sid)); 238 219 else 239 - context = sidtab_lookup_initial(s, sid); 240 - if (context && (!context->len || force)) 241 - return context; 220 + entry = sidtab_lookup_initial(s, sid); 221 + if (entry && (!entry->context.len || force)) 222 + return entry; 242 223 } 243 224 244 225 return sidtab_lookup_initial(s, SECINITSID_UNLABELED); 245 226 } 246 227 247 - struct context *sidtab_search(struct sidtab *s, u32 sid) 228 + struct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid) 248 229 { 249 230 return sidtab_search_core(s, sid, 0); 250 231 } 251 232 252 - struct context *sidtab_search_force(struct sidtab *s, u32 sid) 233 + struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid) 253 234 { 254 235 return sidtab_search_core(s, sid, 1); 255 236 } ··· 261 240 unsigned long flags; 262 241 u32 count; 263 242 struct sidtab_convert_params *convert; 264 - struct sidtab_entry_leaf *dst, *dst_convert; 243 + struct sidtab_entry *dst, *dst_convert; 265 244 int rc; 266 245 267 246 *sid = context_to_sid(s, context); ··· 310 289 } 311 290 312 291 rc = convert->func(context, &dst_convert->context, 313 - convert->args); 292 + convert->args); 314 293 if (rc) { 315 294 context_destroy(&dst->context); 316 295 goto out_unlock; ··· 319 298 convert->target->count = count + 1; 320 299 321 300 hash_add_rcu(convert->target->context_to_sid, 322 - &dst_convert->list, dst_convert->context.hash); 301 + &dst_convert->list, dst_convert->context.hash); 323 302 } 324 303 325 304 if (context->len) ··· 340 319 341 320 static void sidtab_convert_hashtable(struct sidtab *s, u32 count) 342 321 { 343 - struct sidtab_entry_leaf *entry; 322 + struct sidtab_entry *entry; 344 323 u32 i; 345 324 346 325 for (i = 0; i < count; i++) { ··· 348 327 entry->sid = index_to_sid(i); 349 328 350 329 hash_add_rcu(s->context_to_sid, &entry->list, 351 - entry->context.hash); 330 + entry->context.hash); 352 331 353 332 } 354 333 } ··· 397 376 } 398 377 cond_resched(); 399 378 } 400 - 401 379 return 0; 402 380 } 403 381 ··· 459 439 return 0; 460 440 } 461 441 442 + static void sidtab_destroy_entry(struct sidtab_entry *entry) 443 + { 444 + context_destroy(&entry->context); 445 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 446 + kfree(rcu_dereference_raw(entry->cache)); 447 + #endif 448 + } 449 + 462 450 static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level) 463 451 { 464 452 u32 i; ··· 487 459 return; 488 460 489 461 for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++) 490 - context_destroy(&node->entries[i].context); 462 + sidtab_destroy_entry(&node->entries[i]); 491 463 kfree(node); 492 464 } 493 465 } ··· 498 470 499 471 for (i = 0; i < SECINITSID_NUM; i++) 500 472 if (s->isids[i].set) 501 - context_destroy(&s->isids[i].leaf.context); 473 + sidtab_destroy_entry(&s->isids[i].entry); 502 474 503 475 level = SIDTAB_MAX_LEVEL; 504 476 while (level && !s->roots[level].ptr_inner) ··· 511 483 * to be cleaned up here. 512 484 */ 513 485 } 486 + 487 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 488 + 489 + void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry, 490 + const char *str, u32 str_len) 491 + { 492 + struct sidtab_str_cache *cache, *victim = NULL; 493 + 494 + /* do not cache invalid contexts */ 495 + if (entry->context.len) 496 + return; 497 + 498 + /* 499 + * Skip the put operation when in non-task context to avoid the need 500 + * to disable interrupts while holding s->cache_lock. 501 + */ 502 + if (!in_task()) 503 + return; 504 + 505 + spin_lock(&s->cache_lock); 506 + 507 + cache = rcu_dereference_protected(entry->cache, 508 + lockdep_is_held(&s->cache_lock)); 509 + if (cache) { 510 + /* entry in cache - just bump to the head of LRU list */ 511 + list_move(&cache->lru_member, &s->cache_lru_list); 512 + goto out_unlock; 513 + } 514 + 515 + cache = kmalloc(sizeof(struct sidtab_str_cache) + str_len, GFP_ATOMIC); 516 + if (!cache) 517 + goto out_unlock; 518 + 519 + if (s->cache_free_slots == 0) { 520 + /* pop a cache entry from the tail and free it */ 521 + victim = container_of(s->cache_lru_list.prev, 522 + struct sidtab_str_cache, lru_member); 523 + list_del(&victim->lru_member); 524 + rcu_assign_pointer(victim->parent->cache, NULL); 525 + } else { 526 + s->cache_free_slots--; 527 + } 528 + cache->parent = entry; 529 + cache->len = str_len; 530 + memcpy(cache->str, str, str_len); 531 + list_add(&cache->lru_member, &s->cache_lru_list); 532 + 533 + rcu_assign_pointer(entry->cache, cache); 534 + 535 + out_unlock: 536 + spin_unlock(&s->cache_lock); 537 + kfree_rcu(victim, rcu_member); 538 + } 539 + 540 + int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry, 541 + char **out, u32 *out_len) 542 + { 543 + struct sidtab_str_cache *cache; 544 + int rc = 0; 545 + 546 + if (entry->context.len) 547 + return -ENOENT; /* do not cache invalid contexts */ 548 + 549 + rcu_read_lock(); 550 + 551 + cache = rcu_dereference(entry->cache); 552 + if (!cache) { 553 + rc = -ENOENT; 554 + } else { 555 + *out_len = cache->len; 556 + if (out) { 557 + *out = kmemdup(cache->str, cache->len, GFP_ATOMIC); 558 + if (!*out) 559 + rc = -ENOMEM; 560 + } 561 + } 562 + 563 + rcu_read_unlock(); 564 + 565 + if (!rc && out) 566 + sidtab_sid2str_put(s, entry, *out, *out_len); 567 + return rc; 568 + } 569 + 570 + #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
+49 -9
security/selinux/ss/sidtab.h
··· 17 17 18 18 #include "context.h" 19 19 20 - struct sidtab_entry_leaf { 20 + struct sidtab_entry { 21 21 u32 sid; 22 22 struct context context; 23 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 24 + struct sidtab_str_cache __rcu *cache; 25 + #endif 23 26 struct hlist_node list; 24 27 }; 25 - 26 - struct sidtab_node_inner; 27 - struct sidtab_node_leaf; 28 28 29 29 union sidtab_entry_inner { 30 30 struct sidtab_node_inner *ptr_inner; ··· 41 41 (SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner))) 42 42 #define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT) 43 43 #define SIDTAB_LEAF_ENTRIES \ 44 - (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf)) 44 + (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry)) 45 45 46 46 #define SIDTAB_MAX_BITS 32 47 47 #define SIDTAB_MAX U32_MAX ··· 51 51 SIDTAB_INNER_SHIFT) 52 52 53 53 struct sidtab_node_leaf { 54 - struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES]; 54 + struct sidtab_entry entries[SIDTAB_LEAF_ENTRIES]; 55 55 }; 56 56 57 57 struct sidtab_node_inner { ··· 60 60 61 61 struct sidtab_isid_entry { 62 62 int set; 63 - struct sidtab_entry_leaf leaf; 63 + struct sidtab_entry entry; 64 64 }; 65 65 66 66 struct sidtab_convert_params { ··· 87 87 struct sidtab_convert_params *convert; 88 88 spinlock_t lock; 89 89 90 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 91 + /* SID -> context string cache */ 92 + u32 cache_free_slots; 93 + struct list_head cache_lru_list; 94 + spinlock_t cache_lock; 95 + #endif 96 + 90 97 /* index == SID - 1 (no entry for SECSID_NULL) */ 91 98 struct sidtab_isid_entry isids[SECINITSID_NUM]; 92 99 ··· 103 96 104 97 int sidtab_init(struct sidtab *s); 105 98 int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context); 106 - struct context *sidtab_search(struct sidtab *s, u32 sid); 107 - struct context *sidtab_search_force(struct sidtab *s, u32 sid); 99 + struct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid); 100 + struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid); 101 + 102 + static inline struct context *sidtab_search(struct sidtab *s, u32 sid) 103 + { 104 + struct sidtab_entry *entry = sidtab_search_entry(s, sid); 105 + 106 + return entry ? &entry->context : NULL; 107 + } 108 + 109 + static inline struct context *sidtab_search_force(struct sidtab *s, u32 sid) 110 + { 111 + struct sidtab_entry *entry = sidtab_search_entry_force(s, sid); 112 + 113 + return entry ? &entry->context : NULL; 114 + } 108 115 109 116 int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params); 110 117 ··· 127 106 void sidtab_destroy(struct sidtab *s); 128 107 129 108 int sidtab_hash_stats(struct sidtab *sidtab, char *page); 109 + 110 + #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 111 + void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry, 112 + const char *str, u32 str_len); 113 + int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry, 114 + char **out, u32 *out_len); 115 + #else 116 + static inline void sidtab_sid2str_put(struct sidtab *s, 117 + struct sidtab_entry *entry, 118 + const char *str, u32 str_len) 119 + { 120 + } 121 + static inline int sidtab_sid2str_get(struct sidtab *s, 122 + struct sidtab_entry *entry, 123 + char **out, u32 *out_len) 124 + { 125 + return -ENOENT; 126 + } 127 + #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */ 130 128 131 129 #endif /* _SS_SIDTAB_H_ */ 132 130