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: Ondrej Mosnacek <omosnace@redhat.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
parent
433e3aa377
commit
e67b2ec9f6
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
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;
|
goto bad;
|
||||||
ltr = tr;
|
|
||||||
|
rc = hashtab_insert(p->role_tr, rtk, rtd);
|
||||||
|
if (rc)
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
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)
|
|||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
bad:
|
bad:
|
||||||
|
kfree(rtk);
|
||||||
|
kfree(rtd);
|
||||||
policydb_destroy(p);
|
policydb_destroy(p);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2653,37 +2693,43 @@ 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), 1, fp);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
for (tr = r; tr; tr = tr->next) {
|
|
||||||
buf[0] = cpu_to_le32(tr->role);
|
|
||||||
buf[1] = cpu_to_le32(tr->type);
|
|
||||||
buf[2] = cpu_to_le32(tr->new_role);
|
|
||||||
rc = put_entry(buf, sizeof(u32), 3, fp);
|
rc = put_entry(buf, sizeof(u32), 3, fp);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
|
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
|
||||||
buf[0] = cpu_to_le32(tr->tclass);
|
buf[0] = cpu_to_le32(rtk->tclass);
|
||||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||||
if (rc)
|
if (rc)
|
||||||
return 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)
|
||||||
|
@ -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 {
|
|||||||
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,
|
|||||||
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,
|
|||||||
/* 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.
|
||||||
|
Loading…
Reference in New Issue
Block a user