diff options
| author | Stephen Smalley <sds@tycho.nsa.gov> | 2010-01-07 15:55:16 -0500 | 
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2010-01-25 08:29:05 +1100 | 
| commit | 2f3e82d694d3d7a2db019db1bb63385fbc1066f3 (patch) | |
| tree | 9d99a883eb2ab097a3ff1ee4e1c9bf2fa851d832 /security/selinux/ss | |
| parent | 2457552d1e6f3183cd93f81c49a8da5fe8bb0e42 (diff) | |
| download | olio-linux-3.10-2f3e82d694d3d7a2db019db1bb63385fbc1066f3.tar.xz olio-linux-3.10-2f3e82d694d3d7a2db019db1bb63385fbc1066f3.zip  | |
selinux: convert range transition list to a hashtab
Per https://bugzilla.redhat.com/show_bug.cgi?id=548145
there are sufficient range transition rules in modern (Fedora) policy to
make mls_compute_sid a significant factor on the shmem file setup path
due to the length of the range_tr list.  Replace the simple range_tr
list with a hashtab inside the security server to help mitigate this
problem.
Signed-off-by:  Stephen D. Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/ss')
| -rw-r--r-- | security/selinux/ss/mls.c | 18 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 103 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.h | 6 | 
3 files changed, 86 insertions, 41 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index e6654b543ae..443ae737014 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -513,7 +513,8 @@ int mls_compute_sid(struct context *scontext,  		    u32 specified,  		    struct context *newcontext)  { -	struct range_trans *rtr; +	struct range_trans rtr; +	struct mls_range *r;  	if (!selinux_mls_enabled)  		return 0; @@ -521,15 +522,12 @@ int mls_compute_sid(struct context *scontext,  	switch (specified) {  	case AVTAB_TRANSITION:  		/* Look for a range transition rule. */ -		for (rtr = policydb.range_tr; rtr; rtr = rtr->next) { -			if (rtr->source_type == scontext->type && -			    rtr->target_type == tcontext->type && -			    rtr->target_class == tclass) { -				/* Set the range from the rule */ -				return mls_range_set(newcontext, -						     &rtr->target_range); -			} -		} +		rtr.source_type = scontext->type; +		rtr.target_type = tcontext->type; +		rtr.target_class = tclass; +		r = hashtab_search(policydb.range_tr, &rtr); +		if (r) +			return mls_range_set(newcontext, r);  		/* Fallthrough */  	case AVTAB_CHANGE:  		if (tclass == policydb.process_class) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index f03667213ea..5b92c021920 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -177,6 +177,21 @@ out_free_role:  	goto out;  } +static u32 rangetr_hash(struct hashtab *h, const void *k) +{ +	const struct range_trans *key = k; +	return (key->source_type + (key->target_type << 3) + +		(key->target_class << 5)) & (h->size - 1); +} + +static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ +	const struct range_trans *key1 = k1, *key2 = k2; +	return (key1->source_type != key2->source_type || +		key1->target_type != key2->target_type || +		key1->target_class != key2->target_class); +} +  /*   * Initialize a policy database structure.   */ @@ -204,6 +219,10 @@ static int policydb_init(struct policydb *p)  	if (rc)  		goto out_free_symtab; +	p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); +	if (!p->range_tr) +		goto out_free_symtab; +  	ebitmap_init(&p->policycaps);  	ebitmap_init(&p->permissive_map); @@ -408,6 +427,20 @@ static void symtab_hash_eval(struct symtab *s)  		       info.slots_used, h->size, info.max_chain_len);  	}  } + +static void rangetr_hash_eval(struct hashtab *h) +{ +	struct hashtab_info info; + +	hashtab_stat(h, &info); +	printk(KERN_DEBUG "SELinux: rangetr:  %d entries and %d/%d buckets used, " +	       "longest chain length %d\n", h->nel, +	       info.slots_used, h->size, info.max_chain_len); +} +#else +static inline void rangetr_hash_eval(struct hashtab *h) +{ +}  #endif  /* @@ -612,6 +645,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =  	cat_destroy,  }; +static int range_tr_destroy(void *key, void *datum, void *p) +{ +	struct mls_range *rt = datum; +	kfree(key); +	ebitmap_destroy(&rt->level[0].cat); +	ebitmap_destroy(&rt->level[1].cat); +	kfree(datum); +	cond_resched(); +	return 0; +} +  static void ocontext_destroy(struct ocontext *c, int i)  {  	context_destroy(&c->context[0]); @@ -632,7 +676,6 @@ void policydb_destroy(struct policydb *p)  	int i;  	struct role_allow *ra, *lra = NULL;  	struct role_trans *tr, *ltr = NULL; -	struct range_trans *rt, *lrt = NULL;  	for (i = 0; i < SYM_NUM; i++) {  		cond_resched(); @@ -693,20 +736,8 @@ void policydb_destroy(struct policydb *p)  	}  	kfree(lra); -	for (rt = p->range_tr; rt; rt = rt->next) { -		cond_resched(); -		if (lrt) { -			ebitmap_destroy(&lrt->target_range.level[0].cat); -			ebitmap_destroy(&lrt->target_range.level[1].cat); -			kfree(lrt); -		} -		lrt = rt; -	} -	if (lrt) { -		ebitmap_destroy(&lrt->target_range.level[0].cat); -		ebitmap_destroy(&lrt->target_range.level[1].cat); -		kfree(lrt); -	} +	hashtab_map(p->range_tr, range_tr_destroy, NULL); +	hashtab_destroy(p->range_tr);  	if (p->type_attr_map) {  		for (i = 0; i < p->p_types.nprim; i++) @@ -1689,7 +1720,8 @@ int policydb_read(struct policydb *p, void *fp)  	u32 len, len2, config, nprim, nel, nel2;  	char *policydb_str;  	struct policydb_compat_info *info; -	struct range_trans *rt, *lrt; +	struct range_trans *rt; +	struct mls_range *r;  	config = 0; @@ -2122,44 +2154,61 @@ int policydb_read(struct policydb *p, void *fp)  		if (rc < 0)  			goto bad;  		nel = le32_to_cpu(buf[0]); -		lrt = NULL;  		for (i = 0; i < nel; i++) {  			rt = kzalloc(sizeof(*rt), GFP_KERNEL);  			if (!rt) {  				rc = -ENOMEM;  				goto bad;  			} -			if (lrt) -				lrt->next = rt; -			else -				p->range_tr = rt;  			rc = next_entry(buf, fp, (sizeof(u32) * 2)); -			if (rc < 0) +			if (rc < 0) { +				kfree(rt);  				goto bad; +			}  			rt->source_type = le32_to_cpu(buf[0]);  			rt->target_type = le32_to_cpu(buf[1]);  			if (new_rangetr) {  				rc = next_entry(buf, fp, sizeof(u32)); -				if (rc < 0) +				if (rc < 0) { +					kfree(rt);  					goto bad; +				}  				rt->target_class = le32_to_cpu(buf[0]);  			} else  				rt->target_class = p->process_class;  			if (!policydb_type_isvalid(p, rt->source_type) ||  			    !policydb_type_isvalid(p, rt->target_type) ||  			    !policydb_class_isvalid(p, rt->target_class)) { +				kfree(rt);  				rc = -EINVAL;  				goto bad;  			} -			rc = mls_read_range_helper(&rt->target_range, fp); -			if (rc) +			r = kzalloc(sizeof(*r), GFP_KERNEL); +			if (!r) { +				kfree(rt); +				rc = -ENOMEM;  				goto bad; -			if (!mls_range_isvalid(p, &rt->target_range)) { +			} +			rc = mls_read_range_helper(r, fp); +			if (rc) { +				kfree(rt); +				kfree(r); +				goto bad; +			} +			if (!mls_range_isvalid(p, r)) {  				printk(KERN_WARNING "SELinux:  rangetrans:  invalid range\n"); +				kfree(rt); +				kfree(r); +				goto bad; +			} +			rc = hashtab_insert(p->range_tr, rt, r); +			if (rc) { +				kfree(rt); +				kfree(r);  				goto bad;  			} -			lrt = rt;  		} +		rangetr_hash_eval(p->range_tr);  	}  	p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index cdcc5700946..193736b64de 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -113,8 +113,6 @@ struct range_trans {  	u32 source_type;  	u32 target_type;  	u32 target_class; -	struct mls_range target_range; -	struct range_trans *next;  };  /* Boolean data type */ @@ -240,8 +238,8 @@ struct policydb {  	   fixed labeling behavior. */  	struct genfs *genfs; -	/* range transitions */ -	struct range_trans *range_tr; +	/* range transitions table (range_trans_key -> mls_range) */ +	struct hashtab *range_tr;  	/* type -> attribute reverse mapping */  	struct ebitmap *type_attr_map;  |