Skip to content

Commit d8f5f0e

Browse files
WOnder93pcmoore
authored andcommitted
selinux: fix cond_list corruption when changing booleans
Currently, duplicate_policydb_cond_list() first copies the whole conditional avtab and then tries to link to the correct entries in cond_dup_av_list() using avtab_search(). However, since the conditional avtab may contain multiple entries with the same key, this approach often fails to find the right entry, potentially leading to wrong rules being activated/deactivated when booleans are changed. To fix this, instead start with an empty conditional avtab and add the individual entries one-by-one while building the new av_lists. This approach leads to the correct result, since each entry is present in the av_lists exactly once. The issue can be reproduced with Fedora policy as follows: # sesearch -s ftpd_t -t public_content_rw_t -c dir -p create -A allow ftpd_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write }; [ ftpd_full_access ]:True allow ftpd_t public_content_rw_t:dir { add_name create link remove_name rename reparent rmdir setattr unlink watch watch_reads write }; [ ftpd_anon_write ]:True # setsebool ftpd_anon_write=off ftpd_connect_all_unreserved=off ftpd_connect_db=off ftpd_full_access=off On fixed kernels, the sesearch output is the same after the setsebool command: # sesearch -s ftpd_t -t public_content_rw_t -c dir -p create -A allow ftpd_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write }; [ ftpd_full_access ]:True allow ftpd_t public_content_rw_t:dir { add_name create link remove_name rename reparent rmdir setattr unlink watch watch_reads write }; [ ftpd_anon_write ]:True While on the broken kernels, it will be different: # sesearch -s ftpd_t -t public_content_rw_t -c dir -p create -A allow ftpd_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write }; [ ftpd_full_access ]:True allow ftpd_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write }; [ ftpd_full_access ]:True allow ftpd_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write }; [ ftpd_full_access ]:True While there, also simplify the computation of nslots. This changes the nslots values for nrules 2 or 3 to just two slots instead of 4, which makes the sequence more consistent. Cc: [email protected] Fixes: c7c556f ("selinux: refactor changing booleans") Signed-off-by: Ondrej Mosnacek <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 442dc00 commit d8f5f0e

File tree

3 files changed

+33
-69
lines changed

3 files changed

+33
-69
lines changed

security/selinux/ss/avtab.c

Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -308,84 +308,48 @@ void avtab_init(struct avtab *h)
308308
h->mask = 0;
309309
}
310310

311-
int avtab_alloc(struct avtab *h, u32 nrules)
311+
static int avtab_alloc_common(struct avtab *h, u32 nslot)
312312
{
313-
u32 shift = 0;
314-
u32 work = nrules;
315-
u32 nslot;
316-
317-
if (nrules == 0)
318-
goto avtab_alloc_out;
319-
320-
while (work) {
321-
work = work >> 1;
322-
shift++;
323-
}
324-
if (shift > 2)
325-
shift = shift - 2;
326-
nslot = 1 << shift;
327-
if (nslot > MAX_AVTAB_HASH_BUCKETS)
328-
nslot = MAX_AVTAB_HASH_BUCKETS;
313+
if (!nslot)
314+
return 0;
329315

330316
h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);
331317
if (!h->htable)
332318
return -ENOMEM;
333319

334320
h->nslot = nslot;
335321
h->mask = nslot - 1;
336-
337-
avtab_alloc_out:
338-
pr_debug("SELinux: %d avtab hash slots, %d rules.\n",
339-
h->nslot, nrules);
340322
return 0;
341323
}
342324

343-
int avtab_duplicate(struct avtab *new, struct avtab *orig)
325+
int avtab_alloc(struct avtab *h, u32 nrules)
344326
{
345-
int i;
346-
struct avtab_node *node, *tmp, *tail;
347-
348-
memset(new, 0, sizeof(*new));
349-
350-
new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL);
351-
if (!new->htable)
352-
return -ENOMEM;
353-
new->nslot = orig->nslot;
354-
new->mask = orig->mask;
355-
356-
for (i = 0; i < orig->nslot; i++) {
357-
tail = NULL;
358-
for (node = orig->htable[i]; node; node = node->next) {
359-
tmp = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
360-
if (!tmp)
361-
goto error;
362-
tmp->key = node->key;
363-
if (tmp->key.specified & AVTAB_XPERMS) {
364-
tmp->datum.u.xperms =
365-
kmem_cache_zalloc(avtab_xperms_cachep,
366-
GFP_KERNEL);
367-
if (!tmp->datum.u.xperms) {
368-
kmem_cache_free(avtab_node_cachep, tmp);
369-
goto error;
370-
}
371-
tmp->datum.u.xperms = node->datum.u.xperms;
372-
} else
373-
tmp->datum.u.data = node->datum.u.data;
374-
375-
if (tail)
376-
tail->next = tmp;
377-
else
378-
new->htable[i] = tmp;
379-
380-
tail = tmp;
381-
new->nel++;
327+
int rc;
328+
u32 nslot = 0;
329+
330+
if (nrules != 0) {
331+
u32 shift = 1;
332+
u32 work = nrules >> 3;
333+
while (work) {
334+
work >>= 1;
335+
shift++;
382336
}
337+
nslot = 1 << shift;
338+
if (nslot > MAX_AVTAB_HASH_BUCKETS)
339+
nslot = MAX_AVTAB_HASH_BUCKETS;
340+
341+
rc = avtab_alloc_common(h, nslot);
342+
if (rc)
343+
return rc;
383344
}
384345

346+
pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules);
385347
return 0;
386-
error:
387-
avtab_destroy(new);
388-
return -ENOMEM;
348+
}
349+
350+
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig)
351+
{
352+
return avtab_alloc_common(new, orig->nslot);
389353
}
390354

391355
void avtab_hash_eval(struct avtab *h, char *tag)

security/selinux/ss/avtab.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct avtab {
8989

9090
void avtab_init(struct avtab *h);
9191
int avtab_alloc(struct avtab *, u32);
92-
int avtab_duplicate(struct avtab *new, struct avtab *orig);
92+
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);
9393
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
9494
void avtab_destroy(struct avtab *h);
9595
void avtab_hash_eval(struct avtab *h, char *tag);

security/selinux/ss/conditional.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,6 @@ static int cond_dup_av_list(struct cond_av_list *new,
605605
struct cond_av_list *orig,
606606
struct avtab *avtab)
607607
{
608-
struct avtab_node *avnode;
609608
u32 i;
610609

611610
memset(new, 0, sizeof(*new));
@@ -615,10 +614,11 @@ static int cond_dup_av_list(struct cond_av_list *new,
615614
return -ENOMEM;
616615

617616
for (i = 0; i < orig->len; i++) {
618-
avnode = avtab_search_node(avtab, &orig->nodes[i]->key);
619-
if (WARN_ON(!avnode))
620-
return -EINVAL;
621-
new->nodes[i] = avnode;
617+
new->nodes[i] = avtab_insert_nonunique(avtab,
618+
&orig->nodes[i]->key,
619+
&orig->nodes[i]->datum);
620+
if (!new->nodes[i])
621+
return -ENOMEM;
622622
new->len++;
623623
}
624624

@@ -630,7 +630,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp,
630630
{
631631
int rc, i, j;
632632

633-
rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab);
633+
rc = avtab_alloc_dup(&newp->te_cond_avtab, &origp->te_cond_avtab);
634634
if (rc)
635635
return rc;
636636

0 commit comments

Comments
 (0)