Skip to content

Commit c3a2761

Browse files
WOnder93pcmoore
authored andcommitted
selinux: optimize storage of filename transitions
In these rules, each rule with the same (target type, target class, filename) values is (in practice) always mapped to the same result type. Therefore, it is much more efficient to group the rules by (ttype, tclass, filename). Thus, this patch drops the stype field from the key and changes the datum to be a linked list of one or more structures that contain a result type and an ebitmap of source types that map the given target to the given result type under the given filename. The size of the hash table is also incremented to 2048 to be more optimal for Fedora policy (which currently has ~2500 unique (ttype, tclass, filename) tuples, regardless of whether the 'unconfined' module is enabled). Not only does this dramtically reduce memory usage when the policy contains a lot of unconfined domains (ergo a lot of filename based transitions), but it also slightly reduces memory usage of strongly confined policies (modeled on Fedora policy with 'unconfined' module disabled) and significantly reduces lookup times of these rules on Fedora (roughly matches the performance of the rhashtable conversion patch [1] posted recently to [email protected]). An obvious next step is to change binary policy format to match this layout, so that disk space is also saved. However, since that requires more work (including matching userspace changes) and this patch is already beneficial on its own, I'm posting it separately. Performance/memory usage comparison: Kernel | Policy load | Policy load | Mem usage | Mem usage | openbench | | (-unconfined) | | (-unconfined) | (createfiles) -----------------|-------------|---------------|-----------|---------------|-------------- reference | 1,30s | 0,91s | 90MB | 77MB | 55 us/file rhashtable patch | 0.98s | 0,85s | 85MB | 75MB | 38 us/file this patch | 0,95s | 0,87s | 75MB | 75MB | 40 us/file (Memory usage is measured after boot. With SELinux disabled the memory usage was ~60MB on the same system.) [1] https://lore.kernel.org/selinux/[email protected]/T/ Signed-off-by: Ondrej Mosnacek <[email protected]> Acked-by: Stephen Smalley <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 253050f commit c3a2761

File tree

3 files changed

+110
-80
lines changed

3 files changed

+110
-80
lines changed

security/selinux/ss/policydb.c

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
336336

337337
static int filenametr_destroy(void *key, void *datum, void *p)
338338
{
339-
struct filename_trans *ft = key;
339+
struct filename_trans_key *ft = key;
340+
struct filename_trans_datum *next, *d = datum;
340341

341342
kfree(ft->name);
342343
kfree(key);
343-
kfree(datum);
344+
do {
345+
ebitmap_destroy(&d->stypes);
346+
next = d->next;
347+
kfree(d);
348+
d = next;
349+
} while (unlikely(d));
344350
cond_resched();
345351
return 0;
346352
}
@@ -406,12 +412,12 @@ static int roles_init(struct policydb *p)
406412

407413
static u32 filenametr_hash(struct hashtab *h, const void *k)
408414
{
409-
const struct filename_trans *ft = k;
415+
const struct filename_trans_key *ft = k;
410416
unsigned long hash;
411417
unsigned int byte_num;
412418
unsigned char focus;
413419

414-
hash = ft->stype ^ ft->ttype ^ ft->tclass;
420+
hash = ft->ttype ^ ft->tclass;
415421

416422
byte_num = 0;
417423
while ((focus = ft->name[byte_num++]))
@@ -421,14 +427,10 @@ static u32 filenametr_hash(struct hashtab *h, const void *k)
421427

422428
static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
423429
{
424-
const struct filename_trans *ft1 = k1;
425-
const struct filename_trans *ft2 = k2;
430+
const struct filename_trans_key *ft1 = k1;
431+
const struct filename_trans_key *ft2 = k2;
426432
int v;
427433

428-
v = ft1->stype - ft2->stype;
429-
if (v)
430-
return v;
431-
432434
v = ft1->ttype - ft2->ttype;
433435
if (v)
434436
return v;
@@ -495,7 +497,7 @@ static int policydb_init(struct policydb *p)
495497
goto out;
496498

497499
p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
498-
(1 << 10));
500+
(1 << 11));
499501
if (!p->filename_trans) {
500502
rc = -ENOMEM;
501503
goto out;
@@ -1882,64 +1884,84 @@ static int range_read(struct policydb *p, void *fp)
18821884

18831885
static int filename_trans_read_one(struct policydb *p, void *fp)
18841886
{
1885-
struct filename_trans *ft;
1886-
struct filename_trans_datum *otype = NULL;
1887+
struct filename_trans_key key, *ft = NULL;
1888+
struct filename_trans_datum *last, *datum = NULL;
18871889
char *name = NULL;
1888-
u32 len;
1890+
u32 len, stype, otype;
18891891
__le32 buf[4];
18901892
int rc;
18911893

1892-
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
1893-
if (!ft)
1894-
return -ENOMEM;
1895-
1896-
rc = -ENOMEM;
1897-
otype = kmalloc(sizeof(*otype), GFP_KERNEL);
1898-
if (!otype)
1899-
goto out;
1900-
19011894
/* length of the path component string */
19021895
rc = next_entry(buf, fp, sizeof(u32));
19031896
if (rc)
1904-
goto out;
1897+
return rc;
19051898
len = le32_to_cpu(buf[0]);
19061899

19071900
/* path component string */
19081901
rc = str_read(&name, GFP_KERNEL, fp, len);
19091902
if (rc)
1910-
goto out;
1911-
1912-
ft->name = name;
1903+
return rc;
19131904

19141905
rc = next_entry(buf, fp, sizeof(u32) * 4);
19151906
if (rc)
19161907
goto out;
19171908

1918-
ft->stype = le32_to_cpu(buf[0]);
1919-
ft->ttype = le32_to_cpu(buf[1]);
1920-
ft->tclass = le32_to_cpu(buf[2]);
1909+
stype = le32_to_cpu(buf[0]);
1910+
key.ttype = le32_to_cpu(buf[1]);
1911+
key.tclass = le32_to_cpu(buf[2]);
1912+
key.name = name;
19211913

1922-
otype->otype = le32_to_cpu(buf[3]);
1914+
otype = le32_to_cpu(buf[3]);
19231915

1924-
rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1);
1925-
if (rc)
1926-
goto out;
1916+
last = NULL;
1917+
datum = hashtab_search(p->filename_trans, &key);
1918+
while (datum) {
1919+
if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) {
1920+
/* conflicting/duplicate rules are ignored */
1921+
datum = NULL;
1922+
goto out;
1923+
}
1924+
if (likely(datum->otype == otype))
1925+
break;
1926+
last = datum;
1927+
datum = datum->next;
1928+
}
1929+
if (!datum) {
1930+
rc = -ENOMEM;
1931+
datum = kmalloc(sizeof(*datum), GFP_KERNEL);
1932+
if (!datum)
1933+
goto out;
19271934

1928-
rc = hashtab_insert(p->filename_trans, ft, otype);
1929-
if (rc) {
1930-
/*
1931-
* Do not return -EEXIST to the caller, or the system
1932-
* will not boot.
1933-
*/
1934-
if (rc == -EEXIST)
1935-
rc = 0;
1936-
goto out;
1935+
ebitmap_init(&datum->stypes);
1936+
datum->otype = otype;
1937+
datum->next = NULL;
1938+
1939+
if (unlikely(last)) {
1940+
last->next = datum;
1941+
} else {
1942+
rc = -ENOMEM;
1943+
ft = kmemdup(&key, sizeof(key), GFP_KERNEL);
1944+
if (!ft)
1945+
goto out;
1946+
1947+
rc = hashtab_insert(p->filename_trans, ft, datum);
1948+
if (rc)
1949+
goto out;
1950+
name = NULL;
1951+
1952+
rc = ebitmap_set_bit(&p->filename_trans_ttypes,
1953+
key.ttype, 1);
1954+
if (rc)
1955+
return rc;
1956+
}
19371957
}
1938-
return 0;
1958+
kfree(name);
1959+
return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
1960+
19391961
out:
19401962
kfree(ft);
19411963
kfree(name);
1942-
kfree(otype);
1964+
kfree(datum);
19431965
return rc;
19441966
}
19451967

@@ -1957,6 +1979,8 @@ static int filename_trans_read(struct policydb *p, void *fp)
19571979
return rc;
19581980
nel = le32_to_cpu(buf[0]);
19591981

1982+
p->filename_trans_count = nel;
1983+
19601984
for (i = 0; i < nel; i++) {
19611985
rc = filename_trans_read_one(p, fp);
19621986
if (rc)
@@ -3334,50 +3358,50 @@ static int range_write(struct policydb *p, void *fp)
33343358

33353359
static int filename_write_helper(void *key, void *data, void *ptr)
33363360
{
3337-
__le32 buf[4];
3338-
struct filename_trans *ft = key;
3339-
struct filename_trans_datum *otype = data;
3361+
struct filename_trans_key *ft = key;
3362+
struct filename_trans_datum *datum = data;
3363+
struct ebitmap_node *node;
33403364
void *fp = ptr;
3365+
__le32 buf[4];
33413366
int rc;
3342-
u32 len;
3367+
u32 bit, len = strlen(ft->name);
33433368

3344-
len = strlen(ft->name);
3345-
buf[0] = cpu_to_le32(len);
3346-
rc = put_entry(buf, sizeof(u32), 1, fp);
3347-
if (rc)
3348-
return rc;
3369+
do {
3370+
ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
3371+
buf[0] = cpu_to_le32(len);
3372+
rc = put_entry(buf, sizeof(u32), 1, fp);
3373+
if (rc)
3374+
return rc;
33493375

3350-
rc = put_entry(ft->name, sizeof(char), len, fp);
3351-
if (rc)
3352-
return rc;
3376+
rc = put_entry(ft->name, sizeof(char), len, fp);
3377+
if (rc)
3378+
return rc;
33533379

3354-
buf[0] = cpu_to_le32(ft->stype);
3355-
buf[1] = cpu_to_le32(ft->ttype);
3356-
buf[2] = cpu_to_le32(ft->tclass);
3357-
buf[3] = cpu_to_le32(otype->otype);
3380+
buf[0] = cpu_to_le32(bit + 1);
3381+
buf[1] = cpu_to_le32(ft->ttype);
3382+
buf[2] = cpu_to_le32(ft->tclass);
3383+
buf[3] = cpu_to_le32(datum->otype);
33583384

3359-
rc = put_entry(buf, sizeof(u32), 4, fp);
3360-
if (rc)
3361-
return rc;
3385+
rc = put_entry(buf, sizeof(u32), 4, fp);
3386+
if (rc)
3387+
return rc;
3388+
}
3389+
3390+
datum = datum->next;
3391+
} while (unlikely(datum));
33623392

33633393
return 0;
33643394
}
33653395

33663396
static int filename_trans_write(struct policydb *p, void *fp)
33673397
{
3368-
u32 nel;
33693398
__le32 buf[1];
33703399
int rc;
33713400

33723401
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
33733402
return 0;
33743403

3375-
nel = 0;
3376-
rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel);
3377-
if (rc)
3378-
return rc;
3379-
3380-
buf[0] = cpu_to_le32(nel);
3404+
buf[0] = cpu_to_le32(p->filename_trans_count);
33813405
rc = put_entry(buf, sizeof(u32), 1, fp);
33823406
if (rc)
33833407
return rc;

security/selinux/ss/policydb.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,16 @@ struct role_trans {
8989
struct role_trans *next;
9090
};
9191

92-
struct filename_trans {
93-
u32 stype; /* current process */
92+
struct filename_trans_key {
9493
u32 ttype; /* parent dir context */
9594
u16 tclass; /* class of new object */
9695
const char *name; /* last path component */
9796
};
9897

9998
struct filename_trans_datum {
100-
u32 otype; /* expected of new object */
99+
struct ebitmap stypes; /* bitmap of source types for this otype */
100+
u32 otype; /* resulting type of new object */
101+
struct filename_trans_datum *next; /* record for next otype*/
101102
};
102103

103104
struct role_allow {
@@ -267,6 +268,7 @@ struct policydb {
267268
struct ebitmap filename_trans_ttypes;
268269
/* actual set of filename_trans rules */
269270
struct hashtab *filename_trans;
271+
u32 filename_trans_count;
270272

271273
/* bools indexed by (value - 1) */
272274
struct cond_bool_datum **bool_val_to_struct;

security/selinux/ss/services.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,8 +1692,8 @@ static void filename_compute_type(struct policydb *policydb,
16921692
u32 stype, u32 ttype, u16 tclass,
16931693
const char *objname)
16941694
{
1695-
struct filename_trans ft;
1696-
struct filename_trans_datum *otype;
1695+
struct filename_trans_key ft;
1696+
struct filename_trans_datum *datum;
16971697

16981698
/*
16991699
* Most filename trans rules are going to live in specific directories
@@ -1703,14 +1703,18 @@ static void filename_compute_type(struct policydb *policydb,
17031703
if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype))
17041704
return;
17051705

1706-
ft.stype = stype;
17071706
ft.ttype = ttype;
17081707
ft.tclass = tclass;
17091708
ft.name = objname;
17101709

1711-
otype = hashtab_search(policydb->filename_trans, &ft);
1712-
if (otype)
1713-
newcontext->type = otype->otype;
1710+
datum = hashtab_search(policydb->filename_trans, &ft);
1711+
while (datum) {
1712+
if (ebitmap_get_bit(&datum->stypes, stype - 1)) {
1713+
newcontext->type = datum->otype;
1714+
return;
1715+
}
1716+
datum = datum->next;
1717+
}
17141718
}
17151719

17161720
static int security_compute_sid(struct selinux_state *state,

0 commit comments

Comments
 (0)