Skip to content

Commit e67b2ec

Browse files
WOnder93pcmoore
authored andcommitted
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 <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 433e3aa commit e67b2ec

File tree

3 files changed

+107
-60
lines changed

3 files changed

+107
-60
lines changed

security/selinux/ss/policydb.c

Lines changed: 92 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,13 @@ static int range_tr_destroy(void *key, void *datum, void *p)
352352
return 0;
353353
}
354354

355+
static int role_tr_destroy(void *key, void *datum, void *p)
356+
{
357+
kfree(key);
358+
kfree(datum);
359+
return 0;
360+
}
361+
355362
static void ocontext_destroy(struct ocontext *c, int i)
356363
{
357364
if (!c)
@@ -458,6 +465,30 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
458465
return v;
459466
}
460467

468+
static u32 role_trans_hash(struct hashtab *h, const void *k)
469+
{
470+
const struct role_trans_key *key = k;
471+
472+
return (key->role + (key->type << 3) + (key->tclass << 5)) &
473+
(h->size - 1);
474+
}
475+
476+
static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2)
477+
{
478+
const struct role_trans_key *key1 = k1, *key2 = k2;
479+
int v;
480+
481+
v = key1->role - key2->role;
482+
if (v)
483+
return v;
484+
485+
v = key1->type - key2->type;
486+
if (v)
487+
return v;
488+
489+
return key1->tclass - key2->tclass;
490+
}
491+
461492
/*
462493
* Initialize a policy database structure.
463494
*/
@@ -728,7 +759,6 @@ void policydb_destroy(struct policydb *p)
728759
struct genfs *g, *gtmp;
729760
int i;
730761
struct role_allow *ra, *lra = NULL;
731-
struct role_trans *tr, *ltr = NULL;
732762

733763
for (i = 0; i < SYM_NUM; i++) {
734764
cond_resched();
@@ -775,12 +805,8 @@ void policydb_destroy(struct policydb *p)
775805

776806
cond_policydb_destroy(p);
777807

778-
for (tr = p->role_tr; tr; tr = tr->next) {
779-
cond_resched();
780-
kfree(ltr);
781-
ltr = tr;
782-
}
783-
kfree(ltr);
808+
hashtab_map(p->role_tr, role_tr_destroy, NULL);
809+
hashtab_destroy(p->role_tr);
784810

785811
for (ra = p->role_allow; ra; ra = ra->next) {
786812
cond_resched();
@@ -2251,7 +2277,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
22512277
int policydb_read(struct policydb *p, void *fp)
22522278
{
22532279
struct role_allow *ra, *lra;
2254-
struct role_trans *tr, *ltr;
2280+
struct role_trans_key *rtk = NULL;
2281+
struct role_trans_datum *rtd = NULL;
22552282
int i, j, rc;
22562283
__le32 buf[4];
22572284
u32 len, nprim, nel;
@@ -2416,39 +2443,50 @@ int policydb_read(struct policydb *p, void *fp)
24162443
if (rc)
24172444
goto bad;
24182445
nel = le32_to_cpu(buf[0]);
2419-
ltr = NULL;
2446+
2447+
p->role_tr = hashtab_create(role_trans_hash, role_trans_cmp, nel);
2448+
if (!p->role_tr)
2449+
goto bad;
24202450
for (i = 0; i < nel; i++) {
24212451
rc = -ENOMEM;
2422-
tr = kzalloc(sizeof(*tr), GFP_KERNEL);
2423-
if (!tr)
2452+
rtk = kmalloc(sizeof(*rtk), GFP_KERNEL);
2453+
if (!rtk)
24242454
goto bad;
2425-
if (ltr)
2426-
ltr->next = tr;
2427-
else
2428-
p->role_tr = tr;
2455+
2456+
rc = -ENOMEM;
2457+
rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
2458+
if (!rtd)
2459+
goto bad;
2460+
24292461
rc = next_entry(buf, fp, sizeof(u32)*3);
24302462
if (rc)
24312463
goto bad;
24322464

24332465
rc = -EINVAL;
2434-
tr->role = le32_to_cpu(buf[0]);
2435-
tr->type = le32_to_cpu(buf[1]);
2436-
tr->new_role = le32_to_cpu(buf[2]);
2466+
rtk->role = le32_to_cpu(buf[0]);
2467+
rtk->type = le32_to_cpu(buf[1]);
2468+
rtd->new_role = le32_to_cpu(buf[2]);
24372469
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
24382470
rc = next_entry(buf, fp, sizeof(u32));
24392471
if (rc)
24402472
goto bad;
2441-
tr->tclass = le32_to_cpu(buf[0]);
2473+
rtk->tclass = le32_to_cpu(buf[0]);
24422474
} else
2443-
tr->tclass = p->process_class;
2475+
rtk->tclass = p->process_class;
24442476

24452477
rc = -EINVAL;
2446-
if (!policydb_role_isvalid(p, tr->role) ||
2447-
!policydb_type_isvalid(p, tr->type) ||
2448-
!policydb_class_isvalid(p, tr->tclass) ||
2449-
!policydb_role_isvalid(p, tr->new_role))
2478+
if (!policydb_role_isvalid(p, rtk->role) ||
2479+
!policydb_type_isvalid(p, rtk->type) ||
2480+
!policydb_class_isvalid(p, rtk->tclass) ||
2481+
!policydb_role_isvalid(p, rtd->new_role))
2482+
goto bad;
2483+
2484+
rc = hashtab_insert(p->role_tr, rtk, rtd);
2485+
if (rc)
24502486
goto bad;
2451-
ltr = tr;
2487+
2488+
rtk = NULL;
2489+
rtd = NULL;
24522490
}
24532491

24542492
rc = next_entry(buf, fp, sizeof(u32));
@@ -2536,6 +2574,8 @@ int policydb_read(struct policydb *p, void *fp)
25362574
out:
25372575
return rc;
25382576
bad:
2577+
kfree(rtk);
2578+
kfree(rtd);
25392579
policydb_destroy(p);
25402580
goto out;
25412581
}
@@ -2653,39 +2693,45 @@ static int cat_write(void *vkey, void *datum, void *ptr)
26532693
return 0;
26542694
}
26552695

2656-
static int role_trans_write(struct policydb *p, void *fp)
2696+
static int role_trans_write_one(void *key, void *datum, void *ptr)
26572697
{
2658-
struct role_trans *r = p->role_tr;
2659-
struct role_trans *tr;
2698+
struct role_trans_key *rtk = key;
2699+
struct role_trans_datum *rtd = datum;
2700+
struct policy_data *pd = ptr;
2701+
void *fp = pd->fp;
2702+
struct policydb *p = pd->p;
26602703
__le32 buf[3];
2661-
size_t nel;
26622704
int rc;
26632705

2664-
nel = 0;
2665-
for (tr = r; tr; tr = tr->next)
2666-
nel++;
2667-
buf[0] = cpu_to_le32(nel);
2668-
rc = put_entry(buf, sizeof(u32), 1, fp);
2706+
buf[0] = cpu_to_le32(rtk->role);
2707+
buf[1] = cpu_to_le32(rtk->type);
2708+
buf[2] = cpu_to_le32(rtd->new_role);
2709+
rc = put_entry(buf, sizeof(u32), 3, fp);
26692710
if (rc)
26702711
return rc;
2671-
for (tr = r; tr; tr = tr->next) {
2672-
buf[0] = cpu_to_le32(tr->role);
2673-
buf[1] = cpu_to_le32(tr->type);
2674-
buf[2] = cpu_to_le32(tr->new_role);
2675-
rc = put_entry(buf, sizeof(u32), 3, fp);
2712+
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
2713+
buf[0] = cpu_to_le32(rtk->tclass);
2714+
rc = put_entry(buf, sizeof(u32), 1, fp);
26762715
if (rc)
26772716
return rc;
2678-
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
2679-
buf[0] = cpu_to_le32(tr->tclass);
2680-
rc = put_entry(buf, sizeof(u32), 1, fp);
2681-
if (rc)
2682-
return rc;
2683-
}
26842717
}
2685-
26862718
return 0;
26872719
}
26882720

2721+
static int role_trans_write(struct policydb *p, void *fp)
2722+
{
2723+
struct policy_data pd = { .p = p, .fp = fp };
2724+
__le32 buf[1];
2725+
int rc;
2726+
2727+
buf[0] = cpu_to_le32(p->role_tr->nel);
2728+
rc = put_entry(buf, sizeof(u32), 1, fp);
2729+
if (rc)
2730+
return rc;
2731+
2732+
return hashtab_map(p->role_tr, role_trans_write_one, &pd);
2733+
}
2734+
26892735
static int role_allow_write(struct role_allow *r, void *fp)
26902736
{
26912737
struct role_allow *ra;

security/selinux/ss/policydb.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ struct role_datum {
8181
struct ebitmap types; /* set of authorized types for role */
8282
};
8383

84-
struct role_trans {
84+
struct role_trans_key {
8585
u32 role; /* current role */
8686
u32 type; /* program executable type, or new object type */
8787
u32 tclass; /* process class, or new object class */
88+
};
89+
90+
struct role_trans_datum {
8891
u32 new_role; /* new role */
89-
struct role_trans *next;
9092
};
9193

9294
struct filename_trans_key {
@@ -261,7 +263,7 @@ struct policydb {
261263
struct avtab te_avtab;
262264

263265
/* role transitions */
264-
struct role_trans *role_tr;
266+
struct hashtab *role_tr;
265267

266268
/* file transitions with the last path component */
267269
/* quickly exclude lookups when parent ttype has no rules */

security/selinux/ss/services.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,7 +1731,6 @@ static int security_compute_sid(struct selinux_state *state,
17311731
struct class_datum *cladatum = NULL;
17321732
struct context *scontext, *tcontext, newcontext;
17331733
struct sidtab_entry *sentry, *tentry;
1734-
struct role_trans *roletr = NULL;
17351734
struct avtab_key avkey;
17361735
struct avtab_datum *avdatum;
17371736
struct avtab_node *node;
@@ -1864,16 +1863,16 @@ static int security_compute_sid(struct selinux_state *state,
18641863
/* Check for class-specific changes. */
18651864
if (specified & AVTAB_TRANSITION) {
18661865
/* Look for a role transition rule. */
1867-
for (roletr = policydb->role_tr; roletr;
1868-
roletr = roletr->next) {
1869-
if ((roletr->role == scontext->role) &&
1870-
(roletr->type == tcontext->type) &&
1871-
(roletr->tclass == tclass)) {
1872-
/* Use the role transition rule. */
1873-
newcontext.role = roletr->new_role;
1874-
break;
1875-
}
1876-
}
1866+
struct role_trans_datum *rtd;
1867+
struct role_trans_key rtk = {
1868+
.role = scontext->role,
1869+
.type = tcontext->type,
1870+
.tclass = tclass,
1871+
};
1872+
1873+
rtd = hashtab_search(policydb->role_tr, &rtk);
1874+
if (rtd)
1875+
newcontext.role = rtd->new_role;
18771876
}
18781877

18791878
/* Set the MLS attributes.

0 commit comments

Comments
 (0)