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>
This commit is contained in:
parent
2457552d1e
commit
2f3e82d694
|
@ -513,7 +513,8 @@ int mls_compute_sid(struct context *scontext,
|
||||||
u32 specified,
|
u32 specified,
|
||||||
struct context *newcontext)
|
struct context *newcontext)
|
||||||
{
|
{
|
||||||
struct range_trans *rtr;
|
struct range_trans rtr;
|
||||||
|
struct mls_range *r;
|
||||||
|
|
||||||
if (!selinux_mls_enabled)
|
if (!selinux_mls_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -521,15 +522,12 @@ int mls_compute_sid(struct context *scontext,
|
||||||
switch (specified) {
|
switch (specified) {
|
||||||
case AVTAB_TRANSITION:
|
case AVTAB_TRANSITION:
|
||||||
/* Look for a range transition rule. */
|
/* Look for a range transition rule. */
|
||||||
for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
|
rtr.source_type = scontext->type;
|
||||||
if (rtr->source_type == scontext->type &&
|
rtr.target_type = tcontext->type;
|
||||||
rtr->target_type == tcontext->type &&
|
rtr.target_class = tclass;
|
||||||
rtr->target_class == tclass) {
|
r = hashtab_search(policydb.range_tr, &rtr);
|
||||||
/* Set the range from the rule */
|
if (r)
|
||||||
return mls_range_set(newcontext,
|
return mls_range_set(newcontext, r);
|
||||||
&rtr->target_range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Fallthrough */
|
/* Fallthrough */
|
||||||
case AVTAB_CHANGE:
|
case AVTAB_CHANGE:
|
||||||
if (tclass == policydb.process_class)
|
if (tclass == policydb.process_class)
|
||||||
|
|
|
@ -177,6 +177,21 @@ out_free_role:
|
||||||
goto out;
|
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.
|
* Initialize a policy database structure.
|
||||||
*/
|
*/
|
||||||
|
@ -204,6 +219,10 @@ static int policydb_init(struct policydb *p)
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out_free_symtab;
|
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->policycaps);
|
||||||
ebitmap_init(&p->permissive_map);
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -612,6 +645,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
|
||||||
cat_destroy,
|
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)
|
static void ocontext_destroy(struct ocontext *c, int i)
|
||||||
{
|
{
|
||||||
context_destroy(&c->context[0]);
|
context_destroy(&c->context[0]);
|
||||||
|
@ -632,7 +676,6 @@ void policydb_destroy(struct policydb *p)
|
||||||
int i;
|
int i;
|
||||||
struct role_allow *ra, *lra = NULL;
|
struct role_allow *ra, *lra = NULL;
|
||||||
struct role_trans *tr, *ltr = NULL;
|
struct role_trans *tr, *ltr = NULL;
|
||||||
struct range_trans *rt, *lrt = NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < SYM_NUM; i++) {
|
for (i = 0; i < SYM_NUM; i++) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
@ -693,20 +736,8 @@ void policydb_destroy(struct policydb *p)
|
||||||
}
|
}
|
||||||
kfree(lra);
|
kfree(lra);
|
||||||
|
|
||||||
for (rt = p->range_tr; rt; rt = rt->next) {
|
hashtab_map(p->range_tr, range_tr_destroy, NULL);
|
||||||
cond_resched();
|
hashtab_destroy(p->range_tr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->type_attr_map) {
|
if (p->type_attr_map) {
|
||||||
for (i = 0; i < p->p_types.nprim; i++)
|
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;
|
u32 len, len2, config, nprim, nel, nel2;
|
||||||
char *policydb_str;
|
char *policydb_str;
|
||||||
struct policydb_compat_info *info;
|
struct policydb_compat_info *info;
|
||||||
struct range_trans *rt, *lrt;
|
struct range_trans *rt;
|
||||||
|
struct mls_range *r;
|
||||||
|
|
||||||
config = 0;
|
config = 0;
|
||||||
|
|
||||||
|
@ -2122,44 +2154,61 @@ int policydb_read(struct policydb *p, void *fp)
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto bad;
|
goto bad;
|
||||||
nel = le32_to_cpu(buf[0]);
|
nel = le32_to_cpu(buf[0]);
|
||||||
lrt = NULL;
|
|
||||||
for (i = 0; i < nel; i++) {
|
for (i = 0; i < nel; i++) {
|
||||||
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
|
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
|
||||||
if (!rt) {
|
if (!rt) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
if (lrt)
|
|
||||||
lrt->next = rt;
|
|
||||||
else
|
|
||||||
p->range_tr = rt;
|
|
||||||
rc = next_entry(buf, fp, (sizeof(u32) * 2));
|
rc = next_entry(buf, fp, (sizeof(u32) * 2));
|
||||||
if (rc < 0)
|
if (rc < 0) {
|
||||||
|
kfree(rt);
|
||||||
goto bad;
|
goto bad;
|
||||||
|
}
|
||||||
rt->source_type = le32_to_cpu(buf[0]);
|
rt->source_type = le32_to_cpu(buf[0]);
|
||||||
rt->target_type = le32_to_cpu(buf[1]);
|
rt->target_type = le32_to_cpu(buf[1]);
|
||||||
if (new_rangetr) {
|
if (new_rangetr) {
|
||||||
rc = next_entry(buf, fp, sizeof(u32));
|
rc = next_entry(buf, fp, sizeof(u32));
|
||||||
if (rc < 0)
|
if (rc < 0) {
|
||||||
|
kfree(rt);
|
||||||
goto bad;
|
goto bad;
|
||||||
|
}
|
||||||
rt->target_class = le32_to_cpu(buf[0]);
|
rt->target_class = le32_to_cpu(buf[0]);
|
||||||
} else
|
} else
|
||||||
rt->target_class = p->process_class;
|
rt->target_class = p->process_class;
|
||||||
if (!policydb_type_isvalid(p, rt->source_type) ||
|
if (!policydb_type_isvalid(p, rt->source_type) ||
|
||||||
!policydb_type_isvalid(p, rt->target_type) ||
|
!policydb_type_isvalid(p, rt->target_type) ||
|
||||||
!policydb_class_isvalid(p, rt->target_class)) {
|
!policydb_class_isvalid(p, rt->target_class)) {
|
||||||
|
kfree(rt);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
rc = mls_read_range_helper(&rt->target_range, fp);
|
r = kzalloc(sizeof(*r), GFP_KERNEL);
|
||||||
if (rc)
|
if (!r) {
|
||||||
goto bad;
|
kfree(rt);
|
||||||
if (!mls_range_isvalid(p, &rt->target_range)) {
|
rc = -ENOMEM;
|
||||||
printk(KERN_WARNING "SELinux: rangetrans: invalid range\n");
|
goto bad;
|
||||||
|
}
|
||||||
|
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;
|
goto bad;
|
||||||
}
|
}
|
||||||
lrt = rt;
|
|
||||||
}
|
}
|
||||||
|
rangetr_hash_eval(p->range_tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL);
|
p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL);
|
||||||
|
|
|
@ -113,8 +113,6 @@ struct range_trans {
|
||||||
u32 source_type;
|
u32 source_type;
|
||||||
u32 target_type;
|
u32 target_type;
|
||||||
u32 target_class;
|
u32 target_class;
|
||||||
struct mls_range target_range;
|
|
||||||
struct range_trans *next;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Boolean data type */
|
/* Boolean data type */
|
||||||
|
@ -240,8 +238,8 @@ struct policydb {
|
||||||
fixed labeling behavior. */
|
fixed labeling behavior. */
|
||||||
struct genfs *genfs;
|
struct genfs *genfs;
|
||||||
|
|
||||||
/* range transitions */
|
/* range transitions table (range_trans_key -> mls_range) */
|
||||||
struct range_trans *range_tr;
|
struct hashtab *range_tr;
|
||||||
|
|
||||||
/* type -> attribute reverse mapping */
|
/* type -> attribute reverse mapping */
|
||||||
struct ebitmap *type_attr_map;
|
struct ebitmap *type_attr_map;
|
||||||
|
|
Loading…
Reference in New Issue