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

selinux: hash context structure directly

Always hashing the string representation is inefficient. Just hash the
contents of the structure directly (using jhash). If the context is
invalid (str & len are set), then hash the string as before, otherwise
hash the structured data.

Since the context hashing function is now faster (about 10 times), this
patch decreases the overhead of security_transition_sid(), which is
called from many hooks.

The jhash function seemed as a good choice, since it is used as the
default hashing algorithm in rhashtable.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Reviewed-by: Jeff Vander Stoep <jeffv@google.com>
Tested-by: Jeff Vander Stoep <jeffv@google.com>
[PM: fixed some spelling errors in the comments pointed out by JVS]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Ondrej Mosnacek and committed by
Paul Moore
50077289 e67b2ec9

+69 -42
+1 -1
security/selinux/Makefile
··· 8 8 selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ 9 9 netnode.o netport.o status.o \ 10 10 ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ 11 - ss/policydb.o ss/services.o ss/conditional.o ss/mls.o 11 + ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/context.o 12 12 13 13 selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o 14 14
+32
security/selinux/ss/context.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Implementations of the security context functions. 4 + * 5 + * Author: Ondrej Mosnacek <omosnacek@gmail.com> 6 + * Copyright (C) 2020 Red Hat, Inc. 7 + */ 8 + 9 + #include <linux/jhash.h> 10 + 11 + #include "context.h" 12 + #include "mls.h" 13 + 14 + u32 context_compute_hash(const struct context *c) 15 + { 16 + u32 hash = 0; 17 + 18 + /* 19 + * If a context is invalid, it will always be represented by a 20 + * context struct with only the len & str set (and vice versa) 21 + * under a given policy. Since context structs from different 22 + * policies should never meet, it is safe to hash valid and 23 + * invalid contexts differently. The context_cmp() function 24 + * already operates under the same assumption. 25 + */ 26 + if (c->len) 27 + return full_name_hash(NULL, c->str, c->len); 28 + 29 + hash = jhash_3words(c->user, c->role, c->type, hash); 30 + hash = mls_range_hash(&c->range, hash); 31 + return hash; 32 + }
+4 -2
security/selinux/ss/context.h
··· 196 196 mls_context_cmp(c1, c2)); 197 197 } 198 198 199 - static inline unsigned int context_compute_hash(const char *s) 199 + u32 context_compute_hash(const struct context *c); 200 + 201 + static inline void context_add_hash(struct context *context) 200 202 { 201 - return full_name_hash(NULL, s, strlen(s)); 203 + context->hash = context_compute_hash(context); 202 204 } 203 205 204 206 #endif /* _SS_CONTEXT_H_ */
+14
security/selinux/ss/ebitmap.c
··· 19 19 #include <linux/kernel.h> 20 20 #include <linux/slab.h> 21 21 #include <linux/errno.h> 22 + #include <linux/jhash.h> 22 23 #include <net/netlabel.h> 23 24 #include "ebitmap.h" 24 25 #include "policydb.h" ··· 541 540 return rc; 542 541 } 543 542 return 0; 543 + } 544 + 545 + u32 ebitmap_hash(const struct ebitmap *e, u32 hash) 546 + { 547 + struct ebitmap_node *node; 548 + 549 + /* need to change hash even if ebitmap is empty */ 550 + hash = jhash_1word(e->highbit, hash); 551 + for (node = e->node; node; node = node->next) { 552 + hash = jhash_1word(node->startbit, hash); 553 + hash = jhash(node->maps, sizeof(node->maps), hash); 554 + } 555 + return hash; 544 556 } 545 557 546 558 void __init ebitmap_cache_init(void)
+1
security/selinux/ss/ebitmap.h
··· 131 131 void ebitmap_destroy(struct ebitmap *e); 132 132 int ebitmap_read(struct ebitmap *e, void *fp); 133 133 int ebitmap_write(struct ebitmap *e, void *fp); 134 + u32 ebitmap_hash(const struct ebitmap *e, u32 hash); 134 135 135 136 #ifdef CONFIG_NETLABEL 136 137 int ebitmap_netlbl_export(struct ebitmap *ebmap,
+11
security/selinux/ss/mls.h
··· 22 22 #ifndef _SS_MLS_H_ 23 23 #define _SS_MLS_H_ 24 24 25 + #include <linux/jhash.h> 26 + 25 27 #include "context.h" 28 + #include "ebitmap.h" 26 29 #include "policydb.h" 27 30 28 31 int mls_compute_context_len(struct policydb *p, struct context *context); ··· 103 100 return -ENOMEM; 104 101 } 105 102 #endif 103 + 104 + static inline u32 mls_range_hash(const struct mls_range *r, u32 hash) 105 + { 106 + hash = jhash_2words(r->level[0].sens, r->level[1].sens, hash); 107 + hash = ebitmap_hash(&r->level[0].cat, hash); 108 + hash = ebitmap_hash(&r->level[1].cat, hash); 109 + return hash; 110 + } 106 111 107 112 #endif /* _SS_MLS_H */ 108 113
+2 -5
security/selinux/ss/policydb.c
··· 862 862 if (!name) 863 863 continue; 864 864 865 - rc = context_add_hash(p, &c->context[0]); 866 - if (rc) { 867 - sidtab_destroy(s); 868 - goto out; 869 - } 865 + context_add_hash(&c->context[0]); 866 + 870 867 rc = sidtab_set_initial(s, sid, &c->context[0]); 871 868 if (rc) { 872 869 pr_err("SELinux: unable to load initial SID %s.\n",
+4 -31
security/selinux/ss/services.c
··· 1490 1490 return rc; 1491 1491 } 1492 1492 1493 - int context_add_hash(struct policydb *policydb, 1494 - struct context *context) 1495 - { 1496 - int rc; 1497 - char *str; 1498 - int len; 1499 - 1500 - if (context->str) { 1501 - context->hash = context_compute_hash(context->str); 1502 - } else { 1503 - rc = context_struct_to_string(policydb, context, 1504 - &str, &len); 1505 - if (rc) 1506 - return rc; 1507 - context->hash = context_compute_hash(str); 1508 - kfree(str); 1509 - } 1510 - return 0; 1511 - } 1512 - 1513 1493 static int context_struct_to_sid(struct selinux_state *state, 1514 1494 struct context *context, u32 *sid) 1515 1495 { 1516 - int rc; 1517 1496 struct sidtab *sidtab = state->ss->sidtab; 1518 - struct policydb *policydb = &state->ss->policydb; 1519 1497 1520 - if (!context->hash) { 1521 - rc = context_add_hash(policydb, context); 1522 - if (rc) 1523 - return rc; 1524 - } 1498 + if (!context->hash) 1499 + context_add_hash(context); 1525 1500 1526 1501 return sidtab_context_to_sid(sidtab, context, sid); 1527 1502 } ··· 2094 2119 goto bad; 2095 2120 } 2096 2121 2097 - rc = context_add_hash(args->newp, newc); 2098 - if (rc) 2099 - goto bad; 2122 + context_add_hash(newc); 2100 2123 2101 2124 return 0; 2102 2125 bad: ··· 2105 2132 context_destroy(newc); 2106 2133 newc->str = s; 2107 2134 newc->len = len; 2108 - newc->hash = context_compute_hash(s); 2135 + context_add_hash(newc); 2109 2136 pr_info("SELinux: Context %s became invalid (unmapped).\n", 2110 2137 newc->str); 2111 2138 return 0;
-3
security/selinux/ss/services.h
··· 8 8 #define _SS_SERVICES_H_ 9 9 10 10 #include "policydb.h" 11 - #include "context.h" 12 11 13 12 /* Mapping for a single class */ 14 13 struct selinux_mapping { ··· 35 36 36 37 void services_compute_xperms_decision(struct extended_perms_decision *xpermd, 37 38 struct avtab_node *node); 38 - 39 - int context_add_hash(struct policydb *policydb, struct context *context); 40 39 41 40 #endif /* _SS_SERVICES_H_ */