KVM: MMU: invalidate and flush on spte small->large page size change
[safe/jmp/linux-2.6] / security / selinux / ss / avtab.c
index d049c7a..1215b8e 100644 (file)
@@ -6,40 +6,40 @@
 
 /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
  *
- *     Added conditional policy language extensions
+ *     Added conditional policy language extensions
  *
  * Copyright (C) 2003 Tresys Technology, LLC
  *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
+ *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation, version 2.
+ *
+ * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *     Tuned number of hash slots for avtab to reduce memory usage
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/errno.h>
-
 #include "avtab.h"
 #include "policydb.h"
 
-#define AVTAB_HASH(keyp) \
-((keyp->target_class + \
- (keyp->target_type << 2) + \
- (keyp->source_type << 9)) & \
- AVTAB_HASH_MASK)
+static struct kmem_cache *avtab_node_cachep;
 
-static kmem_cache_t *avtab_node_cachep;
+static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
+{
+       return ((keyp->target_class + (keyp->target_type << 2) +
+                (keyp->source_type << 9)) & mask);
+}
 
 static struct avtab_node*
 avtab_insert_node(struct avtab *h, int hvalue,
-                 struct avtab_node * prev, struct avtab_node * cur,
+                 struct avtab_node *prev, struct avtab_node *cur,
                  struct avtab_key *key, struct avtab_datum *datum)
 {
-       struct avtab_node * newnode;
-       newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL);
+       struct avtab_node *newnode;
+       newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
        if (newnode == NULL)
                return NULL;
-       memset(newnode, 0, sizeof(struct avtab_node));
        newnode->key = *key;
        newnode->datum = *datum;
        if (prev) {
@@ -60,10 +60,10 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
        struct avtab_node *prev, *cur, *newnode;
        u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
 
-       if (!h)
+       if (!h || !h->htable)
                return -EINVAL;
 
-       hvalue = AVTAB_HASH(key);
+       hvalue = avtab_hash(key, h->mask);
        for (prev = NULL, cur = h->htable[hvalue];
             cur;
             prev = cur, cur = cur->next) {
@@ -84,7 +84,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
        }
 
        newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
-       if(!newnode)
+       if (!newnode)
                return -ENOMEM;
 
        return 0;
@@ -95,15 +95,15 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
  * It also returns a pointer to the node inserted.
  */
 struct avtab_node *
-avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum)
+avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
 {
        int hvalue;
-       struct avtab_node *prev, *cur, *newnode;
+       struct avtab_node *prev, *cur;
        u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
 
-       if (!h)
+       if (!h || !h->htable)
                return NULL;
-       hvalue = AVTAB_HASH(key);
+       hvalue = avtab_hash(key, h->mask);
        for (prev = NULL, cur = h->htable[hvalue];
             cur;
             prev = cur, cur = cur->next) {
@@ -122,9 +122,7 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da
                    key->target_class < cur->key.target_class)
                        break;
        }
-       newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
-
-       return newnode;
+       return avtab_insert_node(h, hvalue, prev, cur, key, datum);
 }
 
 struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
@@ -133,10 +131,10 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
        struct avtab_node *cur;
        u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
 
-       if (!h)
+       if (!h || !h->htable)
                return NULL;
 
-       hvalue = AVTAB_HASH(key);
+       hvalue = avtab_hash(key, h->mask);
        for (cur = h->htable[hvalue]; cur; cur = cur->next) {
                if (key->source_type == cur->key.source_type &&
                    key->target_type == cur->key.target_type &&
@@ -168,10 +166,10 @@ avtab_search_node(struct avtab *h, struct avtab_key *key)
        struct avtab_node *cur;
        u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
 
-       if (!h)
+       if (!h || !h->htable)
                return NULL;
 
-       hvalue = AVTAB_HASH(key);
+       hvalue = avtab_hash(key, h->mask);
        for (cur = h->htable[hvalue]; cur; cur = cur->next) {
                if (key->source_type == cur->key.source_type &&
                    key->target_type == cur->key.target_type &&
@@ -229,41 +227,72 @@ void avtab_destroy(struct avtab *h)
        if (!h || !h->htable)
                return;
 
-       for (i = 0; i < AVTAB_SIZE; i++) {
+       for (i = 0; i < h->nslot; i++) {
                cur = h->htable[i];
-               while (cur != NULL) {
+               while (cur) {
                        temp = cur;
                        cur = cur->next;
                        kmem_cache_free(avtab_node_cachep, temp);
                }
                h->htable[i] = NULL;
        }
-       vfree(h->htable);
+       kfree(h->htable);
        h->htable = NULL;
+       h->nslot = 0;
+       h->mask = 0;
 }
 
-
 int avtab_init(struct avtab *h)
 {
-       int i;
+       h->htable = NULL;
+       h->nel = 0;
+       return 0;
+}
 
-       h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE);
+int avtab_alloc(struct avtab *h, u32 nrules)
+{
+       u16 mask = 0;
+       u32 shift = 0;
+       u32 work = nrules;
+       u32 nslot = 0;
+
+       if (nrules == 0)
+               goto avtab_alloc_out;
+
+       while (work) {
+               work  = work >> 1;
+               shift++;
+       }
+       if (shift > 2)
+               shift = shift - 2;
+       nslot = 1 << shift;
+       if (nslot > MAX_AVTAB_SIZE)
+               nslot = MAX_AVTAB_SIZE;
+       mask = nslot - 1;
+
+       h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL);
        if (!h->htable)
                return -ENOMEM;
-       for (i = 0; i < AVTAB_SIZE; i++)
-               h->htable[i] = NULL;
+
+ avtab_alloc_out:
        h->nel = 0;
+       h->nslot = nslot;
+       h->mask = mask;
+       printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n",
+              h->nslot, nrules);
        return 0;
 }
 
 void avtab_hash_eval(struct avtab *h, char *tag)
 {
        int i, chain_len, slots_used, max_chain_len;
+       unsigned long long chain2_len_sum;
        struct avtab_node *cur;
 
        slots_used = 0;
        max_chain_len = 0;
-       for (i = 0; i < AVTAB_SIZE; i++) {
+       chain2_len_sum = 0;
+       for (i = 0; i < h->nslot; i++) {
                cur = h->htable[i];
                if (cur) {
                        slots_used++;
@@ -275,12 +304,14 @@ void avtab_hash_eval(struct avtab *h, char *tag)
 
                        if (chain_len > max_chain_len)
                                max_chain_len = chain_len;
+                       chain2_len_sum += chain_len * chain_len;
                }
        }
 
-       printk(KERN_INFO "%s:  %d entries and %d/%d buckets used, longest "
-              "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE,
-              max_chain_len);
+       printk(KERN_DEBUG "SELinux: %s:  %d entries and %d/%d buckets used, "
+              "longest chain length %d sum of chain length^2 %llu\n",
+              tag, h->nel, slots_used, h->nslot, max_chain_len,
+              chain2_len_sum);
 }
 
 static uint16_t spec_order[] = {
@@ -292,18 +323,19 @@ static uint16_t spec_order[] = {
        AVTAB_MEMBER
 };
 
-int avtab_read_item(void *fp, u32 vers, struct avtab *a,
-                   int (*insertf)(struct avtab *a, struct avtab_key *k,
+int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+                   int (*insertf)(struct avtab *a, struct avtab_key *k,
                                   struct avtab_datum *d, void *p),
                    void *p)
 {
        __le16 buf16[4];
        u16 enabled;
        __le32 buf32[7];
-       u32 items, items2, val;
+       u32 items, items2, val, vers = pol->policyvers;
        struct avtab_key key;
        struct avtab_datum datum;
        int i, rc;
+       unsigned set;
 
        memset(&key, 0, sizeof(struct avtab_key));
        memset(&datum, 0, sizeof(struct avtab_datum));
@@ -311,18 +343,18 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
        if (vers < POLICYDB_VERSION_AVTAB) {
                rc = next_entry(buf32, fp, sizeof(u32));
                if (rc < 0) {
-                       printk(KERN_ERR "security: avtab: truncated entry\n");
+                       printk(KERN_ERR "SELinux: avtab: truncated entry\n");
                        return -1;
                }
                items2 = le32_to_cpu(buf32[0]);
                if (items2 > ARRAY_SIZE(buf32)) {
-                       printk(KERN_ERR "security: avtab: entry overflow\n");
+                       printk(KERN_ERR "SELinux: avtab: entry overflow\n");
                        return -1;
 
                }
                rc = next_entry(buf32, fp, sizeof(u32)*items2);
                if (rc < 0) {
-                       printk(KERN_ERR "security: avtab: truncated entry\n");
+                       printk(KERN_ERR "SELinux: avtab: truncated entry\n");
                        return -1;
                }
                items = 0;
@@ -330,19 +362,19 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
                val = le32_to_cpu(buf32[items++]);
                key.source_type = (u16)val;
                if (key.source_type != val) {
-                       printk("security: avtab: truncated source type\n");
+                       printk(KERN_ERR "SELinux: avtab: truncated source type\n");
                        return -1;
                }
                val = le32_to_cpu(buf32[items++]);
                key.target_type = (u16)val;
                if (key.target_type != val) {
-                       printk("security: avtab: truncated target type\n");
+                       printk(KERN_ERR "SELinux: avtab: truncated target type\n");
                        return -1;
                }
                val = le32_to_cpu(buf32[items++]);
                key.target_class = (u16)val;
                if (key.target_class != val) {
-                       printk("security: avtab: truncated target class\n");
+                       printk(KERN_ERR "SELinux: avtab: truncated target class\n");
                        return -1;
                }
 
@@ -350,12 +382,12 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
                enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
 
                if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
-                       printk("security: avtab: null entry\n");
+                       printk(KERN_ERR "SELinux: avtab: null entry\n");
                        return -1;
                }
                if ((val & AVTAB_AV) &&
                    (val & AVTAB_TYPE)) {
-                       printk("security: avtab: entry has both access vectors and types\n");
+                       printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
                        return -1;
                }
 
@@ -364,12 +396,13 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
                                key.specified = spec_order[i] | enabled;
                                datum.data = le32_to_cpu(buf32[items++]);
                                rc = insertf(a, &key, &datum, p);
-                               if (rc) return rc;
+                               if (rc)
+                                       return rc;
                        }
                }
 
                if (items != items2) {
-                       printk("security: avtab: entry only had %d items, expected %d\n", items2, items);
+                       printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items);
                        return -1;
                }
                return 0;
@@ -377,7 +410,7 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
 
        rc = next_entry(buf16, fp, sizeof(u16)*4);
        if (rc < 0) {
-               printk("security: avtab: truncated entry\n");
+               printk(KERN_ERR "SELinux: avtab: truncated entry\n");
                return -1;
        }
 
@@ -387,12 +420,34 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
        key.target_class = le16_to_cpu(buf16[items++]);
        key.specified = le16_to_cpu(buf16[items++]);
 
+       if (!policydb_type_isvalid(pol, key.source_type) ||
+           !policydb_type_isvalid(pol, key.target_type) ||
+           !policydb_class_isvalid(pol, key.target_class)) {
+               printk(KERN_ERR "SELinux: avtab: invalid type or class\n");
+               return -1;
+       }
+
+       set = 0;
+       for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
+               if (key.specified & spec_order[i])
+                       set++;
+       }
+       if (!set || set > 1) {
+               printk(KERN_ERR "SELinux:  avtab:  more than one specifier\n");
+               return -1;
+       }
+
        rc = next_entry(buf32, fp, sizeof(u32));
        if (rc < 0) {
-               printk("security: avtab: truncated entry\n");
+               printk(KERN_ERR "SELinux: avtab: truncated entry\n");
                return -1;
        }
        datum.data = le32_to_cpu(*buf32);
+       if ((key.specified & AVTAB_TYPE) &&
+           !policydb_type_isvalid(pol, datum.data)) {
+               printk(KERN_ERR "SELinux: avtab: invalid type\n");
+               return -1;
+       }
        return insertf(a, &key, &datum, p);
 }
 
@@ -402,7 +457,7 @@ static int avtab_insertf(struct avtab *a, struct avtab_key *k,
        return avtab_insert(a, k, d);
 }
 
-int avtab_read(struct avtab *a, void *fp, u32 vers)
+int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
 {
        int rc;
        __le32 buf[1];
@@ -411,22 +466,27 @@ int avtab_read(struct avtab *a, void *fp, u32 vers)
 
        rc = next_entry(buf, fp, sizeof(u32));
        if (rc < 0) {
-               printk(KERN_ERR "security: avtab: truncated table\n");
+               printk(KERN_ERR "SELinux: avtab: truncated table\n");
                goto bad;
        }
        nel = le32_to_cpu(buf[0]);
        if (!nel) {
-               printk(KERN_ERR "security: avtab: table is empty\n");
+               printk(KERN_ERR "SELinux: avtab: table is empty\n");
                rc = -EINVAL;
                goto bad;
        }
+
+       rc = avtab_alloc(a, nel);
+       if (rc)
+               goto bad;
+
        for (i = 0; i < nel; i++) {
-               rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL);
+               rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
                if (rc) {
                        if (rc == -ENOMEM)
-                               printk(KERN_ERR "security: avtab: out of memory\n");
+                               printk(KERN_ERR "SELinux: avtab: out of memory\n");
                        else if (rc == -EEXIST)
-                               printk(KERN_ERR "security: avtab: duplicate entry\n");
+                               printk(KERN_ERR "SELinux: avtab: duplicate entry\n");
                        else
                                rc = -EINVAL;
                        goto bad;
@@ -446,10 +506,10 @@ void avtab_cache_init(void)
 {
        avtab_node_cachep = kmem_cache_create("avtab_node",
                                              sizeof(struct avtab_node),
-                                             0, SLAB_PANIC, NULL, NULL);
+                                             0, SLAB_PANIC, NULL);
 }
 
 void avtab_cache_destroy(void)
 {
-       kmem_cache_destroy (avtab_node_cachep);
+       kmem_cache_destroy(avtab_node_cachep);
 }