selinux: hash context structure directly
authorOndrej Mosnacek <omosnace@redhat.com>
Fri, 17 Apr 2020 08:11:56 +0000 (10:11 +0200)
committerPaul Moore <paul@paul-moore.com>
Fri, 17 Apr 2020 20:04:34 +0000 (16:04 -0400)
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>
security/selinux/Makefile
security/selinux/ss/context.c [new file with mode: 0644]
security/selinux/ss/context.h
security/selinux/ss/ebitmap.c
security/selinux/ss/ebitmap.h
security/selinux/ss/mls.h
security/selinux/ss/policydb.c
security/selinux/ss/services.c
security/selinux/ss/services.h

index 0c77ede1cc1120f05d7d6f6d0eb4e50941e4340a..4d8e0e8adf0b12b2526bc2ec42abbf1e80e26bf4 100644 (file)
@@ -8,7 +8,7 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
 selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
             netnode.o netport.o status.o \
             ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
-            ss/policydb.o ss/services.o ss/conditional.o ss/mls.o
+            ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/context.o
 
 selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
 
diff --git a/security/selinux/ss/context.c b/security/selinux/ss/context.c
new file mode 100644 (file)
index 0000000..38bc0aa
--- /dev/null
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implementations of the security context functions.
+ *
+ * Author: Ondrej Mosnacek <omosnacek@gmail.com>
+ * Copyright (C) 2020 Red Hat, Inc.
+ */
+
+#include <linux/jhash.h>
+
+#include "context.h"
+#include "mls.h"
+
+u32 context_compute_hash(const struct context *c)
+{
+       u32 hash = 0;
+
+       /*
+        * If a context is invalid, it will always be represented by a
+        * context struct with only the len & str set (and vice versa)
+        * under a given policy. Since context structs from different
+        * policies should never meet, it is safe to hash valid and
+        * invalid contexts differently. The context_cmp() function
+        * already operates under the same assumption.
+        */
+       if (c->len)
+               return full_name_hash(NULL, c->str, c->len);
+
+       hash = jhash_3words(c->user, c->role, c->type, hash);
+       hash = mls_range_hash(&c->range, hash);
+       return hash;
+}
index 3ba044fe02ed49d7948674d333a83dfd989d16bf..e7ae7e21449b8f97f5c84753d8831f4771f161c6 100644 (file)
@@ -196,9 +196,11 @@ static inline int context_cmp(struct context *c1, struct context *c2)
                mls_context_cmp(c1, c2));
 }
 
-static inline unsigned int context_compute_hash(const char *s)
+u32 context_compute_hash(const struct context *c);
+
+static inline void context_add_hash(struct context *context)
 {
-       return full_name_hash(NULL, s, strlen(s));
+       context->hash = context_compute_hash(context);
 }
 
 #endif /* _SS_CONTEXT_H_ */
index c8c3663111e273d05d7ab05e1aa5f57222f6c4b8..14bedc95c6dcf0c14261a3ea5c2351510f4158ca 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <linux/jhash.h>
 #include <net/netlabel.h>
 #include "ebitmap.h"
 #include "policydb.h"
@@ -542,6 +543,19 @@ int ebitmap_write(struct ebitmap *e, void *fp)
        return 0;
 }
 
+u32 ebitmap_hash(const struct ebitmap *e, u32 hash)
+{
+       struct ebitmap_node *node;
+
+       /* need to change hash even if ebitmap is empty */
+       hash = jhash_1word(e->highbit, hash);
+       for (node = e->node; node; node = node->next) {
+               hash = jhash_1word(node->startbit, hash);
+               hash = jhash(node->maps, sizeof(node->maps), hash);
+       }
+       return hash;
+}
+
 void __init ebitmap_cache_init(void)
 {
        ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
index 9a23b81b88327dca4ade72fb4d1fc1189903bb32..9eb2d0af2805e2054bfa94f2646a192e5f54aacf 100644 (file)
@@ -131,6 +131,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
 void ebitmap_destroy(struct ebitmap *e);
 int ebitmap_read(struct ebitmap *e, void *fp);
 int ebitmap_write(struct ebitmap *e, void *fp);
+u32 ebitmap_hash(const struct ebitmap *e, u32 hash);
 
 #ifdef CONFIG_NETLABEL
 int ebitmap_netlbl_export(struct ebitmap *ebmap,
index 7954b1e60b6461ac793dfa2858d067b87c00a25a..15cacde0ff612e76778170624044be557a31f189 100644 (file)
 #ifndef _SS_MLS_H_
 #define _SS_MLS_H_
 
+#include <linux/jhash.h>
+
 #include "context.h"
+#include "ebitmap.h"
 #include "policydb.h"
 
 int mls_compute_context_len(struct policydb *p, struct context *context);
@@ -101,5 +104,13 @@ static inline int mls_import_netlbl_cat(struct policydb *p,
 }
 #endif
 
+static inline u32 mls_range_hash(const struct mls_range *r, u32 hash)
+{
+       hash = jhash_2words(r->level[0].sens, r->level[1].sens, hash);
+       hash = ebitmap_hash(&r->level[0].cat, hash);
+       hash = ebitmap_hash(&r->level[1].cat, hash);
+       return hash;
+}
+
 #endif /* _SS_MLS_H */
 
index 4f0cfffd008d60af8d4168e28b63432cfb8970cc..2849bc362828c4379657d2039e06fb7c378b4620 100644 (file)
@@ -862,11 +862,8 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
                if (!name)
                        continue;
 
-               rc = context_add_hash(p, &c->context[0]);
-               if (rc) {
-                       sidtab_destroy(s);
-                       goto out;
-               }
+               context_add_hash(&c->context[0]);
+
                rc = sidtab_set_initial(s, sid, &c->context[0]);
                if (rc) {
                        pr_err("SELinux:  unable to load initial SID %s.\n",
index 07cdda2ff49c91abefa6275674e654db0b93964c..ed330691430978ba685d18d0cf394591efcd8cef 100644 (file)
@@ -1490,38 +1490,13 @@ out:
        return rc;
 }
 
-int context_add_hash(struct policydb *policydb,
-                    struct context *context)
-{
-       int rc;
-       char *str;
-       int len;
-
-       if (context->str) {
-               context->hash = context_compute_hash(context->str);
-       } else {
-               rc = context_struct_to_string(policydb, context,
-                                             &str, &len);
-               if (rc)
-                       return rc;
-               context->hash = context_compute_hash(str);
-               kfree(str);
-       }
-       return 0;
-}
-
 static int context_struct_to_sid(struct selinux_state *state,
                                 struct context *context, u32 *sid)
 {
-       int rc;
        struct sidtab *sidtab = state->ss->sidtab;
-       struct policydb *policydb = &state->ss->policydb;
 
-       if (!context->hash) {
-               rc = context_add_hash(policydb, context);
-               if (rc)
-                       return rc;
-       }
+       if (!context->hash)
+               context_add_hash(context);
 
        return sidtab_context_to_sid(sidtab, context, sid);
 }
@@ -2119,9 +2094,7 @@ static int convert_context(struct context *oldc, struct context *newc, void *p)
                        goto bad;
        }
 
-       rc = context_add_hash(args->newp, newc);
-       if (rc)
-               goto bad;
+       context_add_hash(newc);
 
        return 0;
 bad:
@@ -2132,7 +2105,7 @@ bad:
        context_destroy(newc);
        newc->str = s;
        newc->len = len;
-       newc->hash = context_compute_hash(s);
+       context_add_hash(newc);
        pr_info("SELinux:  Context %s became invalid (unmapped).\n",
                newc->str);
        return 0;
index e9bddf33e53d3ca5bbb8b7042509d2dd302b68de..a06f3d8352164499e11beef7b35eafe9f2109a3f 100644 (file)
@@ -8,7 +8,6 @@
 #define _SS_SERVICES_H_
 
 #include "policydb.h"
-#include "context.h"
 
 /* Mapping for a single class */
 struct selinux_mapping {
@@ -37,6 +36,4 @@ void services_compute_xperms_drivers(struct extended_perms *xperms,
 void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
                                        struct avtab_node *node);
 
-int context_add_hash(struct policydb *policydb, struct context *context);
-
 #endif /* _SS_SERVICES_H_ */