Commit e67b2ec9 authored by Ondrej Mosnacek's avatar Ondrej Mosnacek Committed by Paul Moore

selinux: store role transitions in a hash table

Currently, they are stored in a linked list, which adds significant
overhead to security_transition_sid(). On Fedora, with 428 role
transitions in policy, converting this list to a hash table cuts down
its run time by about 50%. This was measured by running 'stress-ng --msg
1 --msg-ops 100000' under perf with and without this patch.
Signed-off-by: default avatarOndrej Mosnacek <omosnace@redhat.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 433e3aa3
...@@ -352,6 +352,13 @@ static int range_tr_destroy(void *key, void *datum, void *p) ...@@ -352,6 +352,13 @@ static int range_tr_destroy(void *key, void *datum, void *p)
return 0; return 0;
} }
static int role_tr_destroy(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
static void ocontext_destroy(struct ocontext *c, int i) static void ocontext_destroy(struct ocontext *c, int i)
{ {
if (!c) if (!c)
...@@ -458,6 +465,30 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) ...@@ -458,6 +465,30 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
return v; return v;
} }
static u32 role_trans_hash(struct hashtab *h, const void *k)
{
const struct role_trans_key *key = k;
return (key->role + (key->type << 3) + (key->tclass << 5)) &
(h->size - 1);
}
static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2)
{
const struct role_trans_key *key1 = k1, *key2 = k2;
int v;
v = key1->role - key2->role;
if (v)
return v;
v = key1->type - key2->type;
if (v)
return v;
return key1->tclass - key2->tclass;
}
/* /*
* Initialize a policy database structure. * Initialize a policy database structure.
*/ */
...@@ -728,7 +759,6 @@ void policydb_destroy(struct policydb *p) ...@@ -728,7 +759,6 @@ void policydb_destroy(struct policydb *p)
struct genfs *g, *gtmp; struct genfs *g, *gtmp;
int i; int i;
struct role_allow *ra, *lra = NULL; struct role_allow *ra, *lra = NULL;
struct role_trans *tr, *ltr = NULL;
for (i = 0; i < SYM_NUM; i++) { for (i = 0; i < SYM_NUM; i++) {
cond_resched(); cond_resched();
...@@ -775,12 +805,8 @@ void policydb_destroy(struct policydb *p) ...@@ -775,12 +805,8 @@ void policydb_destroy(struct policydb *p)
cond_policydb_destroy(p); cond_policydb_destroy(p);
for (tr = p->role_tr; tr; tr = tr->next) { hashtab_map(p->role_tr, role_tr_destroy, NULL);
cond_resched(); hashtab_destroy(p->role_tr);
kfree(ltr);
ltr = tr;
}
kfree(ltr);
for (ra = p->role_allow; ra; ra = ra->next) { for (ra = p->role_allow; ra; ra = ra->next) {
cond_resched(); cond_resched();
...@@ -2251,7 +2277,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, ...@@ -2251,7 +2277,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
int policydb_read(struct policydb *p, void *fp) int policydb_read(struct policydb *p, void *fp)
{ {
struct role_allow *ra, *lra; struct role_allow *ra, *lra;
struct role_trans *tr, *ltr; struct role_trans_key *rtk = NULL;
struct role_trans_datum *rtd = NULL;
int i, j, rc; int i, j, rc;
__le32 buf[4]; __le32 buf[4];
u32 len, nprim, nel; u32 len, nprim, nel;
...@@ -2416,39 +2443,50 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -2416,39 +2443,50 @@ int policydb_read(struct policydb *p, void *fp)
if (rc) if (rc)
goto bad; goto bad;
nel = le32_to_cpu(buf[0]); nel = le32_to_cpu(buf[0]);
ltr = NULL;
p->role_tr = hashtab_create(role_trans_hash, role_trans_cmp, nel);
if (!p->role_tr)
goto bad;
for (i = 0; i < nel; i++) { for (i = 0; i < nel; i++) {
rc = -ENOMEM; rc = -ENOMEM;
tr = kzalloc(sizeof(*tr), GFP_KERNEL); rtk = kmalloc(sizeof(*rtk), GFP_KERNEL);
if (!tr) if (!rtk)
goto bad; goto bad;
if (ltr)
ltr->next = tr; rc = -ENOMEM;
else rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
p->role_tr = tr; if (!rtd)
goto bad;
rc = next_entry(buf, fp, sizeof(u32)*3); rc = next_entry(buf, fp, sizeof(u32)*3);
if (rc) if (rc)
goto bad; goto bad;
rc = -EINVAL; rc = -EINVAL;
tr->role = le32_to_cpu(buf[0]); rtk->role = le32_to_cpu(buf[0]);
tr->type = le32_to_cpu(buf[1]); rtk->type = le32_to_cpu(buf[1]);
tr->new_role = le32_to_cpu(buf[2]); rtd->new_role = le32_to_cpu(buf[2]);
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
rc = next_entry(buf, fp, sizeof(u32)); rc = next_entry(buf, fp, sizeof(u32));
if (rc) if (rc)
goto bad; goto bad;
tr->tclass = le32_to_cpu(buf[0]); rtk->tclass = le32_to_cpu(buf[0]);
} else } else
tr->tclass = p->process_class; rtk->tclass = p->process_class;
rc = -EINVAL; rc = -EINVAL;
if (!policydb_role_isvalid(p, tr->role) || if (!policydb_role_isvalid(p, rtk->role) ||
!policydb_type_isvalid(p, tr->type) || !policydb_type_isvalid(p, rtk->type) ||
!policydb_class_isvalid(p, tr->tclass) || !policydb_class_isvalid(p, rtk->tclass) ||
!policydb_role_isvalid(p, tr->new_role)) !policydb_role_isvalid(p, rtd->new_role))
goto bad;
rc = hashtab_insert(p->role_tr, rtk, rtd);
if (rc)
goto bad; goto bad;
ltr = tr;
rtk = NULL;
rtd = NULL;
} }
rc = next_entry(buf, fp, sizeof(u32)); rc = next_entry(buf, fp, sizeof(u32));
...@@ -2536,6 +2574,8 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -2536,6 +2574,8 @@ int policydb_read(struct policydb *p, void *fp)
out: out:
return rc; return rc;
bad: bad:
kfree(rtk);
kfree(rtd);
policydb_destroy(p); policydb_destroy(p);
goto out; goto out;
} }
...@@ -2653,39 +2693,45 @@ static int cat_write(void *vkey, void *datum, void *ptr) ...@@ -2653,39 +2693,45 @@ static int cat_write(void *vkey, void *datum, void *ptr)
return 0; return 0;
} }
static int role_trans_write(struct policydb *p, void *fp) static int role_trans_write_one(void *key, void *datum, void *ptr)
{ {
struct role_trans *r = p->role_tr; struct role_trans_key *rtk = key;
struct role_trans *tr; struct role_trans_datum *rtd = datum;
struct policy_data *pd = ptr;
void *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3]; __le32 buf[3];
size_t nel;
int rc; int rc;
nel = 0; buf[0] = cpu_to_le32(rtk->role);
for (tr = r; tr; tr = tr->next) buf[1] = cpu_to_le32(rtk->type);
nel++; buf[2] = cpu_to_le32(rtd->new_role);
buf[0] = cpu_to_le32(nel); rc = put_entry(buf, sizeof(u32), 3, fp);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc) if (rc)
return rc; return rc;
for (tr = r; tr; tr = tr->next) { if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
buf[0] = cpu_to_le32(tr->role); buf[0] = cpu_to_le32(rtk->tclass);
buf[1] = cpu_to_le32(tr->type); rc = put_entry(buf, sizeof(u32), 1, fp);
buf[2] = cpu_to_le32(tr->new_role);
rc = put_entry(buf, sizeof(u32), 3, fp);
if (rc) if (rc)
return rc; return rc;
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
buf[0] = cpu_to_le32(tr->tclass);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
}
} }
return 0; return 0;
} }
static int role_trans_write(struct policydb *p, void *fp)
{
struct policy_data pd = { .p = p, .fp = fp };
__le32 buf[1];
int rc;
buf[0] = cpu_to_le32(p->role_tr->nel);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
return hashtab_map(p->role_tr, role_trans_write_one, &pd);
}
static int role_allow_write(struct role_allow *r, void *fp) static int role_allow_write(struct role_allow *r, void *fp)
{ {
struct role_allow *ra; struct role_allow *ra;
......
...@@ -81,12 +81,14 @@ struct role_datum { ...@@ -81,12 +81,14 @@ struct role_datum {
struct ebitmap types; /* set of authorized types for role */ struct ebitmap types; /* set of authorized types for role */
}; };
struct role_trans { struct role_trans_key {
u32 role; /* current role */ u32 role; /* current role */
u32 type; /* program executable type, or new object type */ u32 type; /* program executable type, or new object type */
u32 tclass; /* process class, or new object class */ u32 tclass; /* process class, or new object class */
};
struct role_trans_datum {
u32 new_role; /* new role */ u32 new_role; /* new role */
struct role_trans *next;
}; };
struct filename_trans_key { struct filename_trans_key {
...@@ -261,7 +263,7 @@ struct policydb { ...@@ -261,7 +263,7 @@ struct policydb {
struct avtab te_avtab; struct avtab te_avtab;
/* role transitions */ /* role transitions */
struct role_trans *role_tr; struct hashtab *role_tr;
/* file transitions with the last path component */ /* file transitions with the last path component */
/* quickly exclude lookups when parent ttype has no rules */ /* quickly exclude lookups when parent ttype has no rules */
......
...@@ -1731,7 +1731,6 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1731,7 +1731,6 @@ static int security_compute_sid(struct selinux_state *state,
struct class_datum *cladatum = NULL; struct class_datum *cladatum = NULL;
struct context *scontext, *tcontext, newcontext; struct context *scontext, *tcontext, newcontext;
struct sidtab_entry *sentry, *tentry; struct sidtab_entry *sentry, *tentry;
struct role_trans *roletr = NULL;
struct avtab_key avkey; struct avtab_key avkey;
struct avtab_datum *avdatum; struct avtab_datum *avdatum;
struct avtab_node *node; struct avtab_node *node;
...@@ -1864,16 +1863,16 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1864,16 +1863,16 @@ static int security_compute_sid(struct selinux_state *state,
/* Check for class-specific changes. */ /* Check for class-specific changes. */
if (specified & AVTAB_TRANSITION) { if (specified & AVTAB_TRANSITION) {
/* Look for a role transition rule. */ /* Look for a role transition rule. */
for (roletr = policydb->role_tr; roletr; struct role_trans_datum *rtd;
roletr = roletr->next) { struct role_trans_key rtk = {
if ((roletr->role == scontext->role) && .role = scontext->role,
(roletr->type == tcontext->type) && .type = tcontext->type,
(roletr->tclass == tclass)) { .tclass = tclass,
/* Use the role transition rule. */ };
newcontext.role = roletr->new_role;
break; rtd = hashtab_search(policydb->role_tr, &rtk);
} if (rtd)
} newcontext.role = rtd->new_role;
} }
/* Set the MLS attributes. /* Set the MLS attributes.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment