Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f3e82d6 authored by Stephen Smalley's avatar Stephen Smalley Committed by James Morris
Browse files

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: default avatarStephen D. Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 2457552d
Loading
Loading
Loading
Loading
+8 −10
Original line number Diff line number Diff line
@@ -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)
+76 −27
Original line number Diff line number Diff line
@@ -177,6 +177,21 @@ static int roles_init(struct policydb *p)
	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);
+2 −4
Original line number Diff line number Diff line
@@ -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;