Re: [PATCH v2 4/5] selinux: Use pointer to switch policydb and sidtab

From: Stephen Smalley
Date: Tue Jan 30 2018 - 09:36:36 EST


On Fri, 2018-01-26 at 15:32 +0100, peter.enderborg@xxxxxxxx wrote:
> From: Peter Enderborg <peter.enderborg@xxxxxxxx>
>
> This i preparation for switching to RCU locks. To be able to use
> RCU we need atomic switched pointer. This adds the dynamic
> memory copying to be a single pointer. It copy all the
> data structures in to new ones. This is an overhead
> for writing rules but the benifit is RCU.
>
> Signed-off-by: Peter Enderborg <peter.enderborg@xxxxxxxx>
> ---
> security/selinux/ss/services.c | 139 +++++++++++++++++++++++------
> ------------
> 1 file changed, 78 insertions(+), 61 deletions(-)
>
> diff --git a/security/selinux/ss/services.c
> b/security/selinux/ss/services.c
> index 2a8486c..81c5717 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2064,76 +2064,67 @@ static int security_preserve_bools(struct
> policydb *p);
> */
> int security_load_policy(void *data, size_t len)
> {
> - struct policydb *oldpolicydb, *newpolicydb;
> + struct policydb *oldpolicydb;
> struct sidtab oldsidtab, newsidtab;
> struct selinux_mapping *oldmap = NULL, *map = NULL;
> struct convert_context_args args;
> - struct shared_current_mapping *new_mapping;
> struct shared_current_mapping *next_rcu;
> -
> + struct shared_current_mapping *old_rcu;
> u32 seqno;
> u16 map_size;
> int rc = 0;
> struct policy_file file = { data, len }, *fp = &file;
>
> - oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL);
> - if (!oldpolicydb) {
> - rc = -ENOMEM;
> - goto out;
> - }
> - new_mapping = kzalloc(sizeof(struct shared_current_mapping),
> - GFP_KERNEL);
> - if (!new_mapping) {
> - rc = -ENOMEM;
> - goto out;
> - }
> - newpolicydb = oldpolicydb + 1;
> - next_rcu = kmalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> - if (!next_rcu) {
> - rc = -ENOMEM;
> - goto out;
> - }
> -
> if (!ss_initialized) {
> - crm = kzalloc(sizeof(struct shared_current_mapping),
> - GFP_KERNEL);
> - if (!crm) {
> + struct shared_current_mapping *first_mapping;
> +
> + first_mapping = kzalloc(sizeof(struct
> shared_current_mapping),
> + GFP_KERNEL);
> + if (!first_mapping) {
> rc = -ENOMEM;
> goto out;
> }
> avtab_cache_init();
> ebitmap_cache_init();
> hashtab_cache_init();
> - rc = policydb_read(&crm->policydb, fp);
> + rc = policydb_read(&first_mapping->policydb, fp);
> if (rc) {
> avtab_cache_destroy();
> ebitmap_cache_destroy();
> hashtab_cache_destroy();
> + kfree(first_mapping);
> goto out;
> }
>
> - crm->policydb.len = len;
> - rc = selinux_set_mapping(&crm->policydb,
> secclass_map,
> - &crm->current_mapping,
> - &crm-
> >current_mapping_size);
> + first_mapping->policydb.len = len;
> + rc = selinux_set_mapping(&first_mapping->policydb,
> secclass_map,
> + &first_mapping-
> >current_mapping,
> + &first_mapping-
> >current_mapping_size);
> if (rc) {
> - policydb_destroy(&crm->policydb);
> + policydb_destroy(&first_mapping->policydb);
> avtab_cache_destroy();
> ebitmap_cache_destroy();
> hashtab_cache_destroy();
> + kfree(first_mapping);
> goto out;
> }
>
> - rc = policydb_load_isids(&crm->policydb, &crm-
> >sidtab);
> + rc = policydb_load_isids(&first_mapping->policydb,
> + &first_mapping->sidtab);
> if (rc) {
> - policydb_destroy(&crm->policydb);
> + policydb_destroy(&first_mapping->policydb);
> avtab_cache_destroy();
> ebitmap_cache_destroy();
> hashtab_cache_destroy();
> + kfree(first_mapping);
> goto out;
> }
>
> - security_load_policycaps(&crm->policydb);
> + security_load_policycaps(&first_mapping->policydb);
> + crm = first_mapping;
> +
> + smp_mb(); /* make sure that crm exist before we */
> + /* switch ss_initialized */
> ss_initialized = 1;
> seqno = ++latest_granting;
> selinux_complete_init();
> @@ -2148,30 +2139,44 @@ int security_load_policy(void *data, size_t
> len)
> #if 0
> sidtab_hash_eval(&crm->sidtab, "sids");
> #endif
> + oldpolicydb = kzalloc(sizeof(*oldpolicydb), GFP_KERNEL);
> + if (!oldpolicydb) {
> + rc = -ENOMEM;
> + goto out;
> + }
> +
> + next_rcu = kzalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> + if (!next_rcu) {
> + kfree(oldpolicydb);
> + rc = -ENOMEM;
> + goto out;
> + }
>
> - rc = policydb_read(newpolicydb, fp);
> + rc = policydb_read(&next_rcu->policydb, fp);
> if (rc)
> goto out;
>
> - newpolicydb->len = len;
> + next_rcu->policydb.len = len;
> + read_lock(&policy_rwlock);
> /* If switching between different policy types, log MLS
> status */
> - if (crm->policydb.mls_enabled && !newpolicydb->mls_enabled)
> + if (crm->policydb.mls_enabled && !next_rcu-
> >policydb.mls_enabled)
> printk(KERN_INFO "SELinux: Disabling MLS
> support...\n");
> - else if (!crm->policydb.mls_enabled && newpolicydb-
> >mls_enabled)
> + else if (!crm->policydb.mls_enabled && next_rcu-
> >policydb.mls_enabled)
> printk(KERN_INFO "SELinux: Enabling MLS
> support...\n");
>
> - rc = policydb_load_isids(newpolicydb, &newsidtab);
> + rc = policydb_load_isids(&next_rcu->policydb, &newsidtab);
> if (rc) {
> printk(KERN_ERR "SELinux: unable to load the
> initial SIDs\n");
> - policydb_destroy(newpolicydb);
> + policydb_destroy(&next_rcu->policydb);
> goto out;
> }
>
> - rc = selinux_set_mapping(newpolicydb, secclass_map, &map,
> &map_size);
> + rc = selinux_set_mapping(&next_rcu->policydb, secclass_map,
> + &map, &map_size);
> if (rc)
> goto err;
>
> - rc = security_preserve_bools(newpolicydb);
> + rc = security_preserve_bools(&next_rcu->policydb);
> if (rc) {
> printk(KERN_ERR "SELinux: unable to preserve
> booleans\n");
> goto err;

Most of this shouldn't need to be under the read lock.

> @@ -2189,7 +2194,7 @@ int security_load_policy(void *data, size_t
> len)
> * in the new SID table.
> */
> args.oldp = &crm->policydb;
> - args.newp = newpolicydb;
> + args.newp = &next_rcu->policydb;
> rc = sidtab_map(&newsidtab, convert_context, &args);
> if (rc) {
> printk(KERN_ERR "SELinux: unable to convert the
> internal"
> @@ -2204,8 +2209,9 @@ int security_load_policy(void *data, size_t
> len)
>
> /* Install the new policydb and SID table. */
> /* next */
> + security_load_policycaps(&next_rcu->policydb);

This cannot be done outside of the write lock; it has to be atomic with
the policy switch.

> + read_unlock(&policy_rwlock);
> write_lock_irq(&policy_rwlock);
> - memcpy(&next_rcu->policydb, newpolicydb, sizeof(struct
> policydb));
> sidtab_set(&next_rcu->sidtab, &newsidtab);
> security_load_policycaps(&next_rcu->policydb);
> oldmap = crm->current_mapping;
> @@ -2213,8 +2219,9 @@ int security_load_policy(void *data, size_t
> len)
> next_rcu->current_mapping_size = map_size;
>
> seqno = ++latest_granting;
> - write_unlock_irq(&policy_rwlock);
> + old_rcu = crm;
> crm = next_rcu;
> + write_unlock_irq(&policy_rwlock);
>
> /* Free the old policydb and SID table. */
> policydb_destroy(oldpolicydb);
> @@ -2226,17 +2233,16 @@ int security_load_policy(void *data, size_t
> len)
> selinux_status_update_policyload(seqno);
> selinux_netlbl_cache_invalidate();
> selinux_xfrm_notify_policyload();
> + kfree(oldpolicydb);
> + kfree(old_rcu);
>
> rc = 0;
> goto out;
> -
> err:
> kfree(map);
> sidtab_destroy(&newsidtab);
> - policydb_destroy(newpolicydb);
> -
> + policydb_destroy(&next_rcu->policydb);
> out:
> - kfree(oldpolicydb);
> return rc;
> }
>
> @@ -2795,54 +2801,65 @@ int security_get_bools(int *len, char
> ***names, int **values)
> goto out;
> }
>
> -
> int security_set_bools(int len, int *values)
> {
> + struct shared_current_mapping *next_rcu, *old_rcu;
> int i, rc;
> int lenp, seqno = 0;
> struct cond_node *cur;
>
> - write_lock_irq(&policy_rwlock);
> -
> + next_rcu = kzalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> + read_lock(&policy_rwlock);
> + old_rcu = crm;
> + memcpy(&next_rcu->policydb, &old_rcu->policydb,
> + sizeof(struct policydb));

You are only doing a "shallow" copy of the policydb here, which
contains pointers to other structures. So then below when you modify
state, you are modifying the original, not just the copy. And you'll
end up double freeing if you free them both.

For reference, attached is a very old attempt to convert the policy
rwlock to RCU from KaiGai Kohei. It may provide some insight into what
is needed here.

> rc = -EFAULT;
> - lenp = crm->policydb.p_bools.nprim;
> + lenp = next_rcu->policydb.p_bools.nprim;
> +
> if (len != lenp)
> goto out;
>
> for (i = 0; i < len; i++) {
> if (!!values[i] !=
> - crm->policydb.bool_val_to_struct[i]->state) {
> + next_rcu->policydb.bool_val_to_struct[i]->state)
> {
> audit_log(current->audit_context,
> GFP_ATOMIC,
> AUDIT_MAC_CONFIG_CHANGE,
> "bool=%s val=%d old_val=%d auid=%u
> ses=%u",
> - sym_name(&crm->policydb, SYM_BOOLS,
> i),
> + sym_name(&next_rcu->policydb,
> SYM_BOOLS, i),
> !!values[i],
> - crm->policydb.bool_val_to_struct[i]-
> >state,
> + next_rcu-
> >policydb.bool_val_to_struct[i]->state,
> from_kuid(&init_user_ns,
> audit_get_loginuid(current)),
> audit_get_sessionid(current));
> }
> if (values[i])
> - crm->policydb.bool_val_to_struct[i]->state =
> 1;
> + next_rcu->policydb.bool_val_to_struct[i]-
> >state = 1;
> else
> - crm->policydb.bool_val_to_struct[i]->state =
> 0;
> + next_rcu->policydb.bool_val_to_struct[i]-
> >state = 0;
> }
>
> - for (cur = crm->policydb.cond_list; cur; cur = cur->next) {
> - rc = evaluate_cond_node(&crm->policydb, cur);
> + for (cur = next_rcu->policydb.cond_list; cur; cur = cur-
> >next) {
> + rc = evaluate_cond_node(&next_rcu->policydb, cur);
> if (rc)
> goto out;
> }
> + read_unlock(&policy_rwlock);
> + rc = 0;
>
> + write_lock_irq(&policy_rwlock);
> seqno = ++latest_granting;
> - rc = 0;
> -out:
> + crm = next_rcu;
> write_unlock_irq(&policy_rwlock);
> +out:
> if (!rc) {
> avc_ss_reset(seqno);
> selnl_notify_policyload(seqno);
> selinux_status_update_policyload(seqno);
> selinux_xfrm_notify_policyload();
> + } else {
> + kfree(next_rcu);
> }
> + kfree(old_rcu);
> +
> return rc;
> }
> diff -rNU4 linux-2.6.9.selinux/security/selinux/hooks.c linux-2.6.9.selinux-policydb/security/selinux/hooks.c
--- linux-2.6.9.selinux/security/selinux/hooks.c 2004-11-09 19:27:09.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/hooks.c 2004-11-09 19:27:07.000000000 +0900
@@ -290,9 +290,9 @@
#endif /* CONFIG_SECURITY_NETWORK */

/* The security server must be initialized before
any labeling or access decisions can be provided. */
-extern int ss_initialized;
+extern struct policydb *policydb;

/* The file system's label must be initialized prior to use. */

static char *labeling_behaviors[6] = {
@@ -506,16 +506,18 @@
down(&sbsec->sem);
if (sbsec->initialized)
goto out;

- if (!ss_initialized) {
+ rcu_read_lock();
+ if (!policydb) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
spin_lock(&sb_security_lock);
if (list_empty(&sbsec->list))
list_add(&sbsec->list, &superblock_security_head);
spin_unlock(&sb_security_lock);
+ rcu_read_unlock();
goto out;
}

/* Determine the labeling behavior to use for this filesystem type. */
@@ -4416,12 +4418,15 @@
{
extern void exit_sel_fs(void);
static int selinux_disabled = 0;

- if (ss_initialized) {
+ rcu_read_lock();
+ if (policydb) {
/* Not permitted after initial policy load. */
+ rcu_read_unlock();
return -EINVAL;
}
+ rcu_read_unlock();

if (selinux_disabled) {
/* Only do this once. */
return -EINVAL;
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/avtab.c linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.c
--- linux-2.6.9.selinux/security/selinux/ss/avtab.c 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.c 2004-11-10 11:43:39.000000000 +0900
@@ -231,9 +231,9 @@
kmem_cache_free(avtab_node_cachep, temp);
}
h->htable[i] = NULL;
}
- vfree(h->htable);
+ kfree(h->htable);
h->htable = NULL;
}


@@ -264,9 +264,9 @@
int avtab_init(struct avtab *h)
{
int i;

- h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE);
+ h->htable = kmalloc(sizeof(*(h->htable)) * AVTAB_SIZE, GFP_KERNEL);
if (!h->htable)
return -ENOMEM;
for (i = 0; i < AVTAB_SIZE; i++)
h->htable[i] = NULL;
@@ -406,4 +406,38 @@
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
0, SLAB_PANIC, NULL, NULL);
}
+
+int avtab_duplicate(struct avtab *new, struct avtab *orig)
+{
+ int i,rc;
+ struct avtab_node *node, *tmp, *tail;
+
+ if (!new || !orig)
+ return -EFAULT;
+
+ rc = avtab_init(new);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < AVTAB_SIZE; i++) {
+ tail = NULL;
+ for (node = orig->htable[i]; node; node = node->next) {
+ tmp = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL);
+ if (!tmp)
+ goto error;
+ memcpy(tmp, node, sizeof(struct avtab_node));
+ if (!tail) {
+ new->htable[i] = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+ new->nel++;
+ }
+ }
+ return 0;
+ error:
+ avtab_destroy(new);
+ return -ENOMEM;
+}
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/avtab.h linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.h
--- linux-2.6.9.selinux/security/selinux/ss/avtab.h 2004-11-09 19:27:07.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.h 2004-11-10 11:44:09.000000000 +0900
@@ -79,9 +79,12 @@
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);

void avtab_cache_init(void);

-#define AVTAB_HASH_BITS 15
+int avtab_duplicate(struct avtab *new, struct avtab *orig);
+
+//#define AVTAB_HASH_BITS 15
+#define AVTAB_HASH_BITS 13
#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS)
#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1)

#define AVTAB_SIZE AVTAB_HASH_BUCKETS
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/conditional.c linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.c
--- linux-2.6.9.selinux/security/selinux/ss/conditional.c 2004-11-09 19:27:07.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.c 2004-11-10 21:29:08.000000000 +0900
@@ -12,9 +12,9 @@
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <linux/slab.h>
-
+#include <linux/rcupdate.h>
#include "security.h"
#include "conditional.h"

/*
@@ -484,4 +484,213 @@
avd->auditallow |= avtab_auditallow(&node->datum);
}
return;
}
+/* ------------------------------------------------------------ */
+static struct cond_av_list *cond_dup_av_list(struct cond_av_list *orig, struct avtab *te_cond)
+{
+ struct avtab_datum *avdatum;
+ struct cond_av_list *cur, *tmp, *tail = NULL, *head = NULL;
+
+ for (cur = orig; cur; cur = cur->next) {
+ avdatum = avtab_search(te_cond, &cur->node->key, cur->node->datum.specified);
+ if (!avdatum)
+ goto error;
+
+ tmp = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+
+ tmp->node = container_of(avdatum, struct avtab_node, datum);
+ tmp->next = NULL;
+ if (!head) {
+ head = tail = tmp;
+ } else {
+ tail->next = tmp;
+ tail = tmp;
+ }
+ }
+ return head;
+ error:
+ for (cur=head; cur; cur = tmp) {
+ tmp = cur->next;
+ kfree(tmp);
+ }
+ return NULL;
+}
+
+static struct cond_expr *cond_dup_expr(struct cond_expr *orig)
+{
+ struct cond_expr *cur, *tmp, *tail, *head;
+
+ tail = head = NULL;
+ for (cur = orig; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct cond_expr), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ tmp->expr_type = cur->expr_type;
+ tmp->bool = cur->bool;
+ tmp->next = NULL;
+ if (!head) {
+ head = tail = tmp;
+ } else {
+ tail->next = tmp;
+ tail = tmp;
+ }
+ }
+ return head; /* success */
+
+ error:
+ for (cur = head; cur; cur = tmp) {
+ tmp = cur->next;
+ kfree(cur);
+ }
+ return NULL;
+}
+
+/* ------------------------------------------------------------ */
+int duplicate_policydb_cond_list(struct policydb *new, struct policydb *orig)
+{
+ int rc;
+ struct cond_node *cur, *tmp, *tail = NULL;
+
+ rc = avtab_duplicate(&new->te_cond_avtab, &orig->te_cond_avtab);
+ if (rc)
+ return rc;
+
+ new->cond_list = NULL;
+ for (cur = orig->cond_list; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct cond_node), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ memset(tmp, 0, sizeof(struct cond_node));
+ tmp->cur_state = cur->cur_state;
+
+ if (!tail) {
+ new->cond_list = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+
+ tmp->expr = cond_dup_expr(cur->expr);
+ if (cur->expr && !tmp->expr)
+ goto error;
+
+ tmp->true_list = cond_dup_av_list(cur->true_list, &new->te_cond_avtab);
+ if (cur->true_list && !tmp->true_list)
+ goto error;
+
+ tmp->false_list = cond_dup_av_list(cur->false_list, &new->te_cond_avtab);
+ if (cur->false_list && !tmp->false_list)
+ goto error;
+ }
+ return 0; /* success*/
+
+ error:
+ cond_list_destroy(new->cond_list);
+ return -ENOMEM;
+}
+
+static int cond_bools_destroy(void *key, void *datum, void *args)
+{
+ kfree(datum);
+ return 0;
+}
+
+static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args)
+{
+ struct cond_bool_datum *datum;
+
+ datum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
+ if (!datum)
+ return -ENOMEM;
+
+ memcpy(datum, orig->datum, sizeof(struct cond_bool_datum));
+
+ new->key = orig->key; /* Not need to duplicate */
+ new->datum = datum;
+ return 0;
+}
+
+static int cond_bools_index(void *key, void *datum, void *args)
+{
+ struct cond_bool_datum *booldatum, **cond_bool_array;
+
+ booldatum = datum;
+ cond_bool_array = args;
+ cond_bool_array[booldatum->value -1] = booldatum;
+
+ return 0;
+}
+
+static int duplicate_policydb_bools(struct policydb *newdb, struct policydb *orig)
+{
+ int len;
+ struct hashtab *newht;
+ struct cond_bool_datum **cond_bool_array;
+
+ if (!newdb || !orig)
+ return -EFAULT;
+
+ len = sizeof(struct cond_bool_datum *) * orig->p_bools.nprim;
+ cond_bool_array = kmalloc(len, GFP_KERNEL);
+ if (!cond_bool_array)
+ return -ENOMEM;
+ memset(cond_bool_array, 0, len);
+
+ newht = hashtab_duplicate(orig->p_bools.table, cond_bools_copy,
+ cond_bools_destroy, NULL);
+ if (!newht) {
+ kfree(cond_bool_array);
+ return -ENOMEM;
+ }
+ hashtab_map(newht, cond_bools_index, cond_bool_array);
+ newdb->bool_val_to_struct = cond_bool_array;
+ newdb->p_bools.table = newht;
+ newdb->p_bools.nprim = newht->nel;
+
+ return 0;
+}
+
+void cond_policydb_free_rcu(struct rcu_head *p)
+{
+ struct policydb *old_policy;
+
+ old_policy = container_of(p, struct policydb, rhead);
+
+ hashtab_map(old_policy->p_bools.table, cond_bools_destroy, NULL);
+ hashtab_destroy(old_policy->p_bools.table);
+ kfree(old_policy->bool_val_to_struct);
+
+ avtab_destroy(&old_policy->te_cond_avtab);
+ cond_list_destroy(old_policy->cond_list);
+
+ printk("olddb was kfree()'d at cond_policydb_free_rcu()\n");
+
+ kfree(old_policy);
+}
+
+struct policydb *cond_policydb_dup(struct policydb *orig)
+{
+ struct policydb *newdb;
+
+ newdb = kmalloc(sizeof(struct policydb), GFP_KERNEL);
+ if (!newdb)
+ return NULL;
+
+ memcpy(newdb, orig, sizeof(struct policydb));
+
+ if (duplicate_policydb_bools(newdb, orig)) {
+ kfree(newdb);
+ return NULL;
+ }
+
+ if (duplicate_policydb_cond_list(newdb, orig)) {
+ cond_policydb_destroy(newdb);
+ kfree(newdb);
+ return NULL;
+ }
+
+ return newdb; /* success */
+}
+/* ------------------------------------------------------------ */
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/conditional.h linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.h
--- linux-2.6.9.selinux/security/selinux/ss/conditional.h 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.h 2004-11-09 19:27:05.000000000 +0900
@@ -73,5 +73,8 @@
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);

int evaluate_cond_node(struct policydb *p, struct cond_node *node);

+void cond_policydb_free_rcu(struct rcu_head *p);
+struct policydb *cond_policydb_dup(struct policydb *orig);
+
#endif /* _CONDITIONAL_H_ */
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/hashtab.c linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.c
--- linux-2.6.9.selinux/security/selinux/ss/hashtab.c 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.c 2004-11-10 21:32:56.000000000 +0900
@@ -277,4 +277,56 @@

info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
}
+
+struct hashtab *hashtab_duplicate(struct hashtab *orig,
+ int (*copy)(struct hashtab_node *new, struct hashtab_node *orig, void *args),
+ int (*destroy)(void *k, void *d, void *args),
+ void *args)
+{
+ int i,rc;
+ struct hashtab *new;
+ struct hashtab_node *cur, *tmp, *tail;
+
+ if (!orig || !copy || !destroy)
+ return NULL;
+
+ new = hashtab_create(orig->hash_value, orig->keycmp, orig->size);
+ if (!new)
+ return NULL;
+
+ for (i = 0; i < orig->size; i++) {
+ tail = NULL;
+ for (cur = orig->htable[i]; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct hashtab_node), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ rc = copy(tmp, cur, args);
+ if (rc) {
+ kfree(tmp);
+ goto error;
+ }
+ tmp->next = NULL;
+ if (!tail) {
+ new->htable[i] = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+ new->nel++;
+ }
+ }
+ return new;
+ error:
+ for (i = 0; i < new->size; i++) {
+ for (cur = new->htable[i]; cur; cur = tmp) {
+ tmp = cur->next;
+ destroy(cur->key, cur->datum, args);
+ kfree(cur);
+ }
+ }
+ kfree(new);
+ return NULL;
+}
+
+
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/hashtab.h linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.h
--- linux-2.6.9.selinux/security/selinux/ss/hashtab.h 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.h 2004-11-09 19:27:05.000000000 +0900
@@ -121,5 +121,12 @@

/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);

+
+struct hashtab *hashtab_duplicate(struct hashtab *orig,
+ int (*copy)(struct hashtab_node *new, struct hashtab_node *orig, void *args),
+ int (*destroy)(void *k, void *d, void *args),
+ void *args);
+
+
#endif /* _SS_HASHTAB_H */
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/policydb.c linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.c
--- linux-2.6.9.selinux/security/selinux/ss/policydb.c 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.c 2004-11-11 10:27:54.889230773 +0900
@@ -141,8 +141,13 @@
int i, rc;

memset(p, 0, sizeof(*p));

+ p->sidtab = kmalloc(sizeof(struct sidtab), GFP_KERNEL);
+ if (!p->sidtab)
+ return -ENOMEM;
+ sidtab_init(p->sidtab);
+
for (i = 0; i < SYM_NUM; i++) {
rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
if (rc)
goto out_free_symtab;
@@ -168,8 +173,9 @@

out_free_symtab:
for (i = 0; i < SYM_NUM; i++)
hashtab_destroy(p->symtab[i].table);
+ kfree(p->sidtab);
goto out;
}

/*
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/policydb.h linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.h
--- linux-2.6.9.selinux/security/selinux/ss/policydb.h 2004-11-09 19:27:08.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.h 2004-11-09 19:27:04.000000000 +0900
@@ -17,8 +17,9 @@

#ifndef _SS_POLICYDB_H_
#define _SS_POLICYDB_H_

+#include <linux/rcupdate.h>
#include "symtab.h"
#include "avtab.h"
#include "sidtab.h"
#include "context.h"
@@ -245,8 +246,10 @@
struct ebitmap trustedreaders;
struct ebitmap trustedwriters;
struct ebitmap trustedobjects;
#endif
+ struct rcu_head rhead;
+ struct sidtab *sidtab;
};

extern int policydb_init(struct policydb *p);
extern int policydb_index_classes(struct policydb *p);
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/services.c linux-2.6.9.selinux-policydb/security/selinux/ss/services.c
--- linux-2.6.9.selinux/security/selinux/ss/services.c 2004-11-09 20:14:15.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/services.c 2004-11-10 21:29:15.000000000 +0900
@@ -41,21 +41,16 @@

extern void selnl_notify_policyload(u32 seqno);
extern int policydb_loaded_version;

-static rwlock_t policy_rwlock = RW_LOCK_UNLOCKED;
-#define POLICY_RDLOCK read_lock(&policy_rwlock)
-#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
-#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
-#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
+#define POLICY_RDLOCK rcu_read_lock();
+#define POLICY_RDUNLOCK rcu_read_unlock();

static DECLARE_MUTEX(load_sem);
-#define LOAD_LOCK down(&load_sem)
-#define LOAD_UNLOCK up(&load_sem)
+#define LOAD_LOCK do{ down(&load_sem); rcu_read_lock(); } while(0)
+#define LOAD_UNLOCK do{ rcu_read_unlock(); up(&load_sem); } while(0)

-struct sidtab sidtab;
-struct policydb policydb;
-int ss_initialized = 0;
+struct policydb *policydb = NULL;

/*
* The largest sequence number that has been used when
* providing an access decision to the access vector cache.
@@ -68,9 +63,10 @@
* Return the boolean value of a constraint expression
* when it is applied to the specified source and target
* security contexts.
*/
-static int constraint_expr_eval(struct context *scontext,
+static int constraint_expr_eval(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
struct constraint_expr *cexpr)
{
u32 val1, val2;
@@ -110,10 +106,10 @@
break;
case CEXPR_ROLE:
val1 = scontext->role;
val2 = tcontext->role;
- r1 = policydb.role_val_to_struct[val1 - 1];
- r2 = policydb.role_val_to_struct[val2 - 1];
+ r1 = p->role_val_to_struct[val1 - 1];
+ r2 = p->role_val_to_struct[val2 - 1];
switch (e->op) {
case CEXPR_DOM:
s[++sp] = ebitmap_get_bit(&r1->dominates,
val2 - 1);
@@ -191,9 +187,10 @@
/*
* Compute access vectors based on a context structure pair for
* the permissions in a particular class.
*/
-static int context_struct_compute_av(struct context *scontext,
+static int context_struct_compute_av(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 requested,
struct av_decision *avd)
@@ -214,14 +211,14 @@
if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
tclass = SECCLASS_NETLINK_SOCKET;

- if (!tclass || tclass > policydb.p_classes.nprim) {
+ if (!tclass || tclass > p->p_classes.nprim) {
printk(KERN_ERR "security_compute_av: unrecognized class %d\n",
tclass);
return -EINVAL;
}
- tclass_datum = policydb.class_val_to_struct[tclass - 1];
+ tclass_datum = p->class_val_to_struct[tclass - 1];

/*
* Initialize the access vectors to the default values.
*/
@@ -237,9 +234,9 @@
*/
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
- avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV);
+ avdatum = avtab_search(&p->te_avtab, &avkey, AVTAB_AV);
if (avdatum) {
if (avdatum->specified & AVTAB_ALLOWED)
avd->allowed = avtab_allowed(avdatum);
if (avdatum->specified & AVTAB_AUDITDENY)
@@ -248,9 +245,9 @@
avd->auditallow = avtab_auditallow(avdatum);
}

/* Check conditional av table for additional permissions */
- cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+ cond_compute_av(&p->te_cond_avtab, &avkey, avd);

/*
* Remove any permissions prohibited by the MLS policy.
*/
@@ -261,9 +258,9 @@
*/
constraint = tclass_datum->constraints;
while (constraint) {
if ((constraint->permissions & (avd->allowed)) &&
- !constraint_expr_eval(scontext, tcontext,
+ !constraint_expr_eval(p, scontext, tcontext,
constraint->expr)) {
avd->allowed = (avd->allowed) & ~(constraint->permissions);
}
constraint = constraint->next;
@@ -276,9 +273,9 @@
*/
if (tclass == SECCLASS_PROCESS &&
(avd->allowed & PROCESS__TRANSITION) &&
scontext->role != tcontext->role) {
- for (ra = policydb.role_allow; ra; ra = ra->next) {
+ for (ra = p->role_allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
tcontext->role == ra->new_role)
break;
}
@@ -307,38 +304,40 @@
u16 tclass,
u32 requested,
struct av_decision *avd)
{
+ struct policydb *p;
struct context *scontext = NULL, *tcontext = NULL;
int rc = 0;

- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
avd->allowed = requested;
avd->decided = requested;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
- return 0;
+ goto out;
}

- POLICY_RDLOCK;
-
- scontext = sidtab_search(&sidtab, ssid);
+ scontext = sidtab_search(p->sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(p->sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
goto out;
}

- rc = context_struct_compute_av(scontext, tcontext, tclass,
+ rc = context_struct_compute_av(p, scontext, tcontext, tclass,
requested, avd);
out:
POLICY_RDUNLOCK;
return rc;
@@ -350,19 +349,20 @@
* allocated string of the correct size. Set `*scontext'
* to point to this string and set `*scontext_len' to
* the length of the string.
*/
-int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+int context_struct_to_string(struct policydb *p, struct context *context,
+ char **scontext, u32 *scontext_len)
{
char *scontextp;

*scontext = NULL;
*scontext_len = 0;

/* Compute the size of the context. */
- *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
- *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
- *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ *scontext_len += strlen(p->p_user_val_to_name[context->user - 1]) + 1;
+ *scontext_len += strlen(p->p_role_val_to_name[context->role - 1]) + 1;
+ *scontext_len += strlen(p->p_type_val_to_name[context->type - 1]) + 1;
*scontext_len += mls_compute_context_len(context);

/* Allocate space for the context; caller must free this space. */
scontextp = kmalloc(*scontext_len+1,GFP_ATOMIC);
@@ -374,14 +374,14 @@
/*
* Copy the user name, role name and type name into the context.
*/
sprintf(scontextp, "%s:%s:%s:",
- policydb.p_user_val_to_name[context->user - 1],
- policydb.p_role_val_to_name[context->role - 1],
- policydb.p_type_val_to_name[context->type - 1]);
- scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
- 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
- 1 + strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ p->p_user_val_to_name[context->user - 1],
+ p->p_role_val_to_name[context->role - 1],
+ p->p_type_val_to_name[context->type - 1]);
+ scontextp += strlen(p->p_user_val_to_name[context->user - 1]) +
+ 1 + strlen(p->p_role_val_to_name[context->role - 1]) +
+ 1 + strlen(p->p_type_val_to_name[context->type - 1]) + 1;

mls_sid_to_context(context, &scontextp);

scontextp--;
@@ -403,12 +403,16 @@
* to point to this string and set @scontext_len to the length of the string.
*/
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
{
+ struct policydb *p;
struct context *context;
int rc = 0;

- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
if (sid <= SECINITSID_NUM) {
char *scontextp;

*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
@@ -421,20 +425,19 @@
"load_policy on unknown SID %d\n", sid);
rc = -EINVAL;
goto out;
}
- POLICY_RDLOCK;
- context = sidtab_search(&sidtab, sid);
+
+ context = sidtab_search(p->sidtab, sid);
if (!context) {
printk(KERN_ERR "security_sid_to_context: unrecognized SID "
"%d\n", sid);
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}
- rc = context_struct_to_string(context, scontext, scontext_len);
-out_unlock:
- POLICY_RDUNLOCK;
+ rc = context_struct_to_string(p, context, scontext, scontext_len);
out:
+ POLICY_RDUNLOCK;
return rc;

}

@@ -451,47 +454,46 @@
*/
int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
{
char *scontext2;
+ struct policydb *pol;
struct context context;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *scontextp, *p, oldc;
int rc = 0;

- if (!ss_initialized) {
+ /* Copy the string so that we can modify the copy as we parse it.
+ The string should already by null terminated, but we append a
+ null suffix to the copy to avoid problems with the existing
+ attr package, which doesn't view the null terminator as part
+ of the attribute value. */
+ scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
+ if (!scontext2)
+ return -ENOMEM;
+ memcpy(scontext2, scontext, scontext_len);
+ scontext2[scontext_len] = 0;
+
+ POLICY_RDLOCK;
+ pol = policydb;
+
+ if (!pol) {
int i;

for (i = 1; i < SECINITSID_NUM; i++) {
- if (!strcmp(initial_sid_to_string[i], scontext)) {
+ if (!strcmp(initial_sid_to_string[i], scontext2)) {
*sid = i;
goto out;
}
}
*sid = SECINITSID_KERNEL;
goto out;
}
- *sid = SECSID_NULL;
-
- /* Copy the string so that we can modify the copy as we parse it.
- The string should already by null terminated, but we append a
- null suffix to the copy to avoid problems with the existing
- attr package, which doesn't view the null terminator as part
- of the attribute value. */
- scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
- if (!scontext2) {
- rc = -ENOMEM;
- goto out;
- }
- memcpy(scontext2, scontext, scontext_len);
- scontext2[scontext_len] = 0;

context_init(&context);
*sid = SECSID_NULL;

- POLICY_RDLOCK;
-
/* Parse the security context. */

rc = -EINVAL;
scontextp = (char *) scontext2;
@@ -505,9 +507,9 @@
goto out_unlock;

*p++ = 0;

- usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+ usrdatum = hashtab_search(pol->p_users.table, scontextp);
if (!usrdatum)
goto out_unlock;

context.user = usrdatum->value;
@@ -521,9 +523,9 @@
goto out_unlock;

*p++ = 0;

- role = hashtab_search(policydb.p_roles.table, scontextp);
+ role = hashtab_search(pol->p_roles.table, scontextp);
if (!role)
goto out_unlock;
context.role = role->value;

@@ -533,9 +535,9 @@
p++;
oldc = *p;
*p++ = 0;

- typdatum = hashtab_search(policydb.p_types.table, scontextp);
+ typdatum = hashtab_search(pol->p_types.table, scontextp);
if (!typdatum)
goto out_unlock;

context.type = typdatum->value;
@@ -549,43 +551,44 @@
goto out_unlock;
}

/* Check the validity of the new context. */
- if (!policydb_context_isvalid(&policydb, &context)) {
+ if (!policydb_context_isvalid(pol, &context)) {
rc = -EINVAL;
goto out_unlock;
}
/* Obtain the new sid. */
- rc = sidtab_context_to_sid(&sidtab, &context, sid);
+ rc = sidtab_context_to_sid(pol->sidtab, &context, sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&context);
- kfree(scontext2);
out:
+ kfree(scontext2);
return rc;
}

static int compute_sid_handle_invalid_context(
+ struct policydb *p,
struct context *scontext,
struct context *tcontext,
u16 tclass,
struct context *newcontext)
{
char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen;

- if (context_struct_to_string(scontext, &s, &slen) < 0)
+ if (context_struct_to_string(p, scontext, &s, &slen) < 0)
goto out;
- if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ if (context_struct_to_string(p, tcontext, &t, &tlen) < 0)
goto out;
- if (context_struct_to_string(newcontext, &n, &nlen) < 0)
+ if (context_struct_to_string(p, newcontext, &n, &nlen) < 0)
goto out;
audit_log(current->audit_context,
"security_compute_sid: invalid context %s"
" for scontext=%s"
" tcontext=%s"
" tclass=%s",
- n, s, t, policydb.p_class_val_to_name[tclass-1]);
+ n, s, t, p->p_class_val_to_name[tclass-1]);
out:
kfree(s);
kfree(t);
kfree(n);
@@ -599,17 +602,21 @@
u16 tclass,
u32 specified,
u32 *out_sid)
{
+ struct policydb *p;
struct context *scontext = NULL, *tcontext = NULL, newcontext;
struct role_trans *roletr = NULL;
struct avtab_key avkey;
struct avtab_datum *avdatum;
struct avtab_node *node;
unsigned int type_change = 0;
int rc = 0;

- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
switch (tclass) {
case SECCLASS_PROCESS:
*out_sid = ssid;
break;
@@ -619,18 +626,16 @@
}
goto out;
}

- POLICY_RDLOCK;
-
- scontext = sidtab_search(&sidtab, ssid);
+ scontext = sidtab_search(p->sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out_unlock;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(p->sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
@@ -669,13 +674,13 @@
/* Look for a type transition/member/change rule. */
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
- avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE);
+ avdatum = avtab_search(&p->te_avtab, &avkey, AVTAB_TYPE);

/* If no permanent rule, also check for enabled conditional rules */
if(!avdatum) {
- node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified);
+ node = avtab_search_node(&p->te_cond_avtab, &avkey, specified);
for (; node != NULL; node = avtab_search_node_next(node, specified)) {
if (node->datum.specified & AVTAB_ENABLED) {
avdatum = &node->datum;
break;
@@ -703,9 +708,9 @@
switch (tclass) {
case SECCLASS_PROCESS:
if (specified & AVTAB_TRANSITION) {
/* Look for a role transition rule. */
- for (roletr = policydb.role_tr; roletr;
+ for (roletr = p->role_tr; roletr;
roletr = roletr->next) {
if (roletr->role == scontext->role &&
roletr->type == tcontext->type) {
/* Use the role transition rule. */
@@ -740,18 +745,16 @@
if (rc)
goto out_unlock;

/* Check the validity of the context. */
- if (!policydb_context_isvalid(&policydb, &newcontext)) {
- rc = compute_sid_handle_invalid_context(scontext,
- tcontext,
- tclass,
- &newcontext);
+ if (!policydb_context_isvalid(p, &newcontext)) {
+ rc = compute_sid_handle_invalid_context(p, scontext, tcontext,
+ tclass, &newcontext);
if (rc)
goto out_unlock;
}
/* Obtain the sid for the context. */
- rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
+ rc = sidtab_context_to_sid(p->sidtab, &newcontext, out_sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&newcontext);
out:
@@ -913,9 +916,9 @@

return sidtab_insert(s, sid, context);
}

-static inline int convert_context_handle_invalid_context(struct context *context)
+static inline int convert_context_handle_invalid_context(struct policydb *p, struct context *context)
{
int rc = 0;

if (selinux_enforcing) {
@@ -923,9 +926,9 @@
} else {
char *s;
u32 len;

- context_struct_to_string(context, &s, &len);
+ context_struct_to_string(p, context, &s, &len);
printk(KERN_ERR "security: context %s is invalid\n", s);
kfree(s);
}
return rc;
@@ -993,26 +996,37 @@
goto bad;

/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
- rc = convert_context_handle_invalid_context(&oldc);
+ rc = convert_context_handle_invalid_context(args->oldp, &oldc);
if (rc)
goto bad;
}

context_destroy(&oldc);
out:
return rc;
bad:
- context_struct_to_string(&oldc, &s, &len);
+ context_struct_to_string(args->oldp, &oldc, &s, &len);
context_destroy(&oldc);
printk(KERN_ERR "security: invalidating context %s\n", s);
kfree(s);
goto out;
}

extern void selinux_complete_init(void);

+void policydb_destroy_rcu(struct rcu_head *p)
+{
+ struct policydb *olddb;
+ olddb = container_of(p, struct policydb, rhead);
+
+ sidtab_destroy(olddb->sidtab);
+ policydb_destroy(olddb);
+ kfree(olddb);
+ printk("kfree(olddb) in policydb_destroy_rcu() preempt()=0x%x\n",preempt_count());
+}
+
/**
* security_load_policy - Load a security policy configuration.
* @data: binary policy data
* @len: length of data in bytes
@@ -1023,29 +1037,34 @@
* loading the new policy.
*/
int security_load_policy(void *data, size_t len)
{
- struct policydb oldpolicydb, newpolicydb;
- struct sidtab oldsidtab, newsidtab;
+ struct policydb *newdb, *olddb;
struct convert_context_args args;
u32 seqno;
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;

+ newdb = kmalloc(sizeof(struct policydb), GFP_KERNEL);
+ if (!newdb)
+ return -ENOMEM;
+
LOAD_LOCK;
+ olddb = policydb;

- if (!ss_initialized) {
+ if (!olddb) {
avtab_cache_init();
- if (policydb_read(&policydb, fp)) {
+ if (policydb_read(newdb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}
- if (policydb_load_isids(&policydb, &sidtab)) {
+ if (policydb_load_isids(newdb, newdb->sidtab)) {
LOAD_UNLOCK;
- policydb_destroy(&policydb);
+ policydb_destroy(newdb);
return -EINVAL;
}
- ss_initialized = 1;
+ smp_wmb();
+ policydb = newdb;

LOAD_UNLOCK;
selinux_complete_init();
return 0;
@@ -1054,62 +1073,52 @@
#if 0
sidtab_hash_eval(&sidtab, "sids");
#endif

- if (policydb_read(&newpolicydb, fp)) {
+ if (policydb_read(newdb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}

- sidtab_init(&newsidtab);
-
/* Verify that the existing classes did not change. */
- if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
+ if (hashtab_map(olddb->p_classes.table, validate_class, newdb)) {
printk(KERN_ERR "security: the definition of an existing "
"class changed\n");
rc = -EINVAL;
goto err;
}
-
+
/* Clone the SID table. */
- sidtab_shutdown(&sidtab);
- if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
+ sidtab_shutdown(olddb->sidtab);
+ if (sidtab_map(olddb->sidtab, clone_sid, newdb->sidtab)) {
rc = -ENOMEM;
goto err;
}

/* Convert the internal representations of contexts
in the new SID table and remove invalid SIDs. */
- args.oldp = &policydb;
- args.newp = &newpolicydb;
- sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
-
- /* Save the old policydb and SID table to free later. */
- memcpy(&oldpolicydb, &policydb, sizeof policydb);
- sidtab_set(&oldsidtab, &sidtab);
-
- /* Install the new policydb and SID table. */
- POLICY_WRLOCK;
- memcpy(&policydb, &newpolicydb, sizeof policydb);
- sidtab_set(&sidtab, &newsidtab);
+ args.oldp = olddb;
+ args.newp = newdb;
+ sidtab_map_remove_on_error(newdb->sidtab, convert_context, &args);
+
+ /* update policydb atomically */
+ smp_wmb();
+ policydb = newdb;
seqno = ++latest_granting;
-
- POLICY_WRUNLOCK;
+
LOAD_UNLOCK;

- /* Free the old policydb and SID table. */
- policydb_destroy(&oldpolicydb);
- sidtab_destroy(&oldsidtab);
+ call_rcu(&olddb->rhead, policydb_destroy_rcu);

avc_ss_reset(seqno);
selnl_notify_policyload(seqno);

return 0;

err:
LOAD_UNLOCK;
- sidtab_destroy(&newsidtab);
- policydb_destroy(&newpolicydb);
+ sidtab_destroy(newdb->sidtab);
+ policydb_destroy(newdb);
return rc;

}

@@ -1126,14 +1135,21 @@
u8 protocol,
u16 port,
u32 *out_sid)
{
+ struct policydb *p;
struct ocontext *c;
int rc = 0;

POLICY_RDLOCK;
+ p = policydb;

- c = policydb.ocontexts[OCON_PORT];
+ if (!p) {
+ *out_sid = SECINITSID_PORT;
+ goto out;
+ }
+
+ c = p->ocontexts[OCON_PORT];
while (c) {
if (c->u.port.protocol == protocol &&
c->u.port.low_port <= port &&
c->u.port.high_port >= port)
@@ -1142,9 +1158,9 @@
}

if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1169,27 +1185,35 @@
u32 *if_sid,
u32 *msg_sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;

POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *if_sid = SECINITSID_NETIF;
+ *msg_sid = SECINITSID_NETMSG;
+ goto out;
+ }

- c = policydb.ocontexts[OCON_NETIF];
+ c = p->ocontexts[OCON_NETIF];
while (c) {
if (strcmp(name, c->u.name) == 0)
break;
c = c->next;
}

if (c) {
if (!c->sid[0] || !c->sid[1]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[1],
&c->sid[1]);
if (rc)
goto out;
@@ -1231,11 +1255,18 @@
u32 addrlen,
u32 *out_sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;

POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *out_sid = SECINITSID_NODE;
+ goto out;
+ }

switch (domain) {
case AF_INET: {
u32 addr;
@@ -1246,9 +1277,9 @@
}

addr = *((u32 *)addrp);

- c = policydb.ocontexts[OCON_NODE];
+ c = p->ocontexts[OCON_NODE];
while (c) {
if (c->u.node.addr == (addr & c->u.node.mask))
break;
c = c->next;
@@ -1260,9 +1291,9 @@
if (addrlen != sizeof(u64) * 2) {
rc = -EINVAL;
goto out;
}
- c = policydb.ocontexts[OCON_NODE6];
+ c = p->ocontexts[OCON_NODE6];
while (c) {
if (match_ipv6_addrmask(addrp, c->u.node6.addr,
c->u.node6.mask))
break;
@@ -1276,9 +1307,9 @@
}

if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1313,64 +1344,66 @@
char *username,
u32 **sids,
u32 *nel)
{
+ struct policydb *p;
struct context *fromcon, usercon;
u32 *mysids, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
struct av_decision avd;
int rc = 0, i, j;

- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
*sids = NULL;
*nel = 0;
goto out;
}

- POLICY_RDLOCK;
-
- fromcon = sidtab_search(&sidtab, fromsid);
+ fromcon = sidtab_search(p->sidtab, fromsid);
if (!fromcon) {
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}

- user = hashtab_search(policydb.p_users.table, username);
+ user = hashtab_search(p->p_users.table, username);
if (!user) {
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}
usercon.user = user->value;

mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC);
if (!mysids) {
rc = -ENOMEM;
- goto out_unlock;
+ goto out;
}
memset(mysids, 0, maxnel*sizeof(*mysids));

for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) {
if (!ebitmap_get_bit(&user->roles, i))
continue;
- role = policydb.role_val_to_struct[i];
+ role = p->role_val_to_struct[i];
usercon.role = i+1;
for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) {
if (!ebitmap_get_bit(&role->types, j))
continue;
usercon.type = j+1;
mls_for_user_ranges(user,usercon) {
- rc = context_struct_compute_av(fromcon, &usercon,
+ rc = context_struct_compute_av(p, fromcon, &usercon,
SECCLASS_PROCESS,
PROCESS__TRANSITION,
&avd);
if (rc || !(avd.allowed & PROCESS__TRANSITION))
continue;
- rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
+ rc = sidtab_context_to_sid(p->sidtab, &usercon, &sid);
if (rc) {
kfree(mysids);
- goto out_unlock;
+ goto out;
}
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
@@ -1378,9 +1411,9 @@
mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
- goto out_unlock;
+ goto out;
}
memset(mysids2, 0, maxnel*sizeof(*mysids2));
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
kfree(mysids);
@@ -1393,12 +1426,10 @@
}

*sids = mysids;
*nel = mynel;
-
-out_unlock:
- POLICY_RDUNLOCK;
out:
+ POLICY_RDUNLOCK;
return rc;
}

/**
@@ -1417,15 +1448,23 @@
u16 sclass,
u32 *sid)
{
int len;
+ struct policydb *p;
struct genfs *genfs;
struct ocontext *c;
int rc = 0, cmp = 0;

POLICY_RDLOCK;
+ p = policydb;

- for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
+ if (!p) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ for (genfs = p->genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
if (cmp <= 0)
break;
}
@@ -1449,9 +1488,9 @@
goto out;
}

if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1474,13 +1513,21 @@
unsigned int *behavior,
u32 *sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;

POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *sid = SECINITSID_UNLABELED;
+ *behavior = SECURITY_FS_USE_NONE;
+ goto out;
+ }

- c = policydb.ocontexts[OCON_FSUSE];
+ c = p->ocontexts[OCON_FSUSE];
while (c) {
if (strcmp(fstype, c->u.name) == 0)
break;
c = c->next;
@@ -1488,9 +1535,9 @@

if (c) {
*behavior = c->v.behavior;
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1513,14 +1560,25 @@

int security_get_bools(int *len, char ***names, int **values)
{
int i, rc = -ENOMEM;
+ struct policydb *p;

POLICY_RDLOCK;
+ p = policydb;
+ if (!p) {
+ *len = 0;
+ *names = NULL;
+ *values = NULL;
+ rc = 0;
+ goto out;
+ }
+
+
*names = NULL;
*values = NULL;

- *len = policydb.p_bools.nprim;
+ *len = p->p_bools.nprim;
if (!*len) {
rc = 0;
goto out;
}
@@ -1535,14 +1593,14 @@
goto err;

for (i = 0; i < *len; i++) {
size_t name_len;
- (*values)[i] = policydb.bool_val_to_struct[i]->state;
- name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
+ (*values)[i] = p->bool_val_to_struct[i]->state;
+ name_len = strlen(p->p_bool_val_to_name[i]) + 1;
(*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
- strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
+ strncpy((*names)[i], p->p_bool_val_to_name[i], name_len);
(*names)[i][name_len - 1] = 0;
}
rc = 0;
out:
@@ -1563,42 +1621,53 @@
int security_set_bools(int len, int *values)
{
int i, rc = 0;
int lenp, seqno = 0;
+ struct policydb *newdb, *olddb;
struct cond_node *cur;

- POLICY_WRLOCK;
+ LOAD_LOCK;
+ olddb = policydb;

- lenp = policydb.p_bools.nprim;
+ lenp = olddb->p_bools.nprim;
if (len != lenp) {
rc = -EFAULT;
goto out;
}
+ newdb = cond_policydb_dup(olddb);
+ if (!newdb) {
+ rc = -ENOMEM;
+ goto out;
+ }

printk(KERN_INFO "security: committed booleans { ");
for (i = 0; i < len; i++) {
if (values[i]) {
- policydb.bool_val_to_struct[i]->state = 1;
+ newdb->bool_val_to_struct[i]->state = 1;
} else {
- policydb.bool_val_to_struct[i]->state = 0;
+ newdb->bool_val_to_struct[i]->state = 0;
}
if (i != 0)
printk(", ");
- printk("%s:%d", policydb.p_bool_val_to_name[i],
- policydb.bool_val_to_struct[i]->state);
+ printk("%s:%d", newdb->p_bool_val_to_name[i],
+ newdb->bool_val_to_struct[i]->state);
}
printk(" }\n");

- for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
- rc = evaluate_cond_node(&policydb, cur);
- if (rc)
+ for (cur = newdb->cond_list; cur != NULL; cur = cur->next) {
+ rc = evaluate_cond_node(newdb, cur);
+ if (rc) {
+ call_rcu(&newdb->rhead, cond_policydb_free_rcu);
goto out;
+ }
}
-
+ smp_wmb();
+ policydb = newdb;
seqno = ++latest_granting;

+ call_rcu(&olddb->rhead, cond_policydb_free_rcu);
out:
- POLICY_WRUNLOCK;
+ LOAD_UNLOCK;
if (!rc) {
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
}
@@ -1608,18 +1677,25 @@
int security_get_bool_value(int bool)
{
int rc = 0;
int len;
+ struct policydb *p;

POLICY_RDLOCK;
+ p = policydb;
+ if (!p) {
+ rc = -EFAULT;
+ goto out;
+ }
+

- len = policydb.p_bools.nprim;
+ len = p->p_bools.nprim;
if (bool >= len) {
rc = -EFAULT;
goto out;
}

- rc = policydb.bool_val_to_struct[bool]->state;
+ rc = p->bool_val_to_struct[bool]->state;
out:
POLICY_RDUNLOCK;
return rc;
}
diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/services.h linux-2.6.9.selinux-policydb/security/selinux/ss/services.h
--- linux-2.6.9.selinux/security/selinux/ss/services.h 2004-11-09 19:27:07.000000000 +0900
+++ linux-2.6.9.selinux-policydb/security/selinux/ss/services.h 2004-11-09 19:27:05.000000000 +0900
@@ -13,9 +13,9 @@
* The security server uses two global data structures
* when providing its services: the SID table (sidtab)
* and the policy database (policydb).
*/
-extern struct sidtab sidtab;
-extern struct policydb policydb;
+//extern struct sidtab sidtab;
+extern struct policydb *policydb;

#endif /* _SS_SERVICES_H_ */