selinux/stable-5.12 PR 20210409

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmBwjZcUHHBhdWxAcGF1
 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXOAcg//eZL4z0ksGo9s/Y+/9qOIYH2tMPU5
 OOVZCekBENiq2LOuVbzAndeHOLZflf3iigBwtMvqHaAsdPAKH/3UedzD0/nxG39m
 S2gowEuNEfxtuBwuIZMFaMGzzLyjlZJ3xxi6omIyj/2JqPNyBbbFxR/VC4agJZI5
 oG6VfwhZJmFi1oJiNoGKjwihKHZQ90yd8UU5rMI+Np0TnP1Or3OvRaZjR47r+dWS
 tAu3nTKrVEyGTcPeGzg9TS5tIko0jQ1FyrqPDBhfaJta48bX/9s70We6rwqJj8Vg
 HiiSDPMK5EKkPLso+1vqvBI9q6xdhNeS+M2JP+/ewK/cqVKMkTVVys6l+T3a6HcY
 rIXdgTWdMFiAQ6OW44z30fiwSxW3kI5M62um31nepoqvzX7acl6R1laILFztedWM
 EOfCznZmE6ccYmcZnrqEmNsdF+Se1TUiM87bN90tAGmF9F4Yw2qGM0raiV3OJhDZ
 P2zR/+DceSHI2pNfFtB5VVXZelHoKVhoRcRWvpzn7YW3UmnAl83HoJasBfa/j4rx
 qvo+nj5ptCSX/kUYjvfvrRV1rY/BAaSVlFLpgYKY1r8/hdRN5DLpdE5cHh6Gky6B
 fJen4a7yVecp8IKK+WR3maJ0hymo5ccUoB5AKzMOXeECKRqKkIDAKiEN59g9t96+
 avKfojgsh1tNHA0=
 =mGDl
 -----END PGP SIGNATURE-----

Merge tag 'selinux-pr-20210409' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux fixes from Paul Moore:
 "Three SELinux fixes.

  These fix known problems relating to (re)loading SELinux policy or
  changing the policy booleans, and pass our test suite without problem"

* tag 'selinux-pr-20210409' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: fix race between old and new sidtab
  selinux: fix cond_list corruption when changing booleans
  selinux: make nslot handling in avtab more robust
This commit is contained in:
Linus Torvalds 2021-04-09 11:51:06 -07:00
commit 60144b23c9
6 changed files with 185 additions and 112 deletions

View File

@ -109,7 +109,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
if (!h || !h->nslot)
return -EINVAL;
hvalue = avtab_hash(key, h->mask);
@ -154,7 +154,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu
struct avtab_node *prev, *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
if (!h || !h->nslot)
return NULL;
hvalue = avtab_hash(key, h->mask);
for (prev = NULL, cur = h->htable[hvalue];
@ -184,7 +184,7 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
if (!h || !h->nslot)
return NULL;
hvalue = avtab_hash(key, h->mask);
@ -220,7 +220,7 @@ avtab_search_node(struct avtab *h, struct avtab_key *key)
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h)
if (!h || !h->nslot)
return NULL;
hvalue = avtab_hash(key, h->mask);
@ -295,6 +295,7 @@ void avtab_destroy(struct avtab *h)
}
kvfree(h->htable);
h->htable = NULL;
h->nel = 0;
h->nslot = 0;
h->mask = 0;
}
@ -303,88 +304,52 @@ void avtab_init(struct avtab *h)
{
h->htable = NULL;
h->nel = 0;
h->nslot = 0;
h->mask = 0;
}
int avtab_alloc(struct avtab *h, u32 nrules)
static int avtab_alloc_common(struct avtab *h, u32 nslot)
{
u32 mask = 0;
u32 shift = 0;
u32 work = nrules;
u32 nslot = 0;
if (nrules == 0)
goto avtab_alloc_out;
while (work) {
work = work >> 1;
shift++;
}
if (shift > 2)
shift = shift - 2;
nslot = 1 << shift;
if (nslot > MAX_AVTAB_HASH_BUCKETS)
nslot = MAX_AVTAB_HASH_BUCKETS;
mask = nslot - 1;
if (!nslot)
return 0;
h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);
if (!h->htable)
return -ENOMEM;
avtab_alloc_out:
h->nel = 0;
h->nslot = nslot;
h->mask = mask;
pr_debug("SELinux: %d avtab hash slots, %d rules.\n",
h->nslot, nrules);
h->mask = nslot - 1;
return 0;
}
int avtab_duplicate(struct avtab *new, struct avtab *orig)
int avtab_alloc(struct avtab *h, u32 nrules)
{
int i;
struct avtab_node *node, *tmp, *tail;
int rc;
u32 nslot = 0;
memset(new, 0, sizeof(*new));
new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL);
if (!new->htable)
return -ENOMEM;
new->nslot = orig->nslot;
new->mask = orig->mask;
for (i = 0; i < orig->nslot; i++) {
tail = NULL;
for (node = orig->htable[i]; node; node = node->next) {
tmp = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
if (!tmp)
goto error;
tmp->key = node->key;
if (tmp->key.specified & AVTAB_XPERMS) {
tmp->datum.u.xperms =
kmem_cache_zalloc(avtab_xperms_cachep,
GFP_KERNEL);
if (!tmp->datum.u.xperms) {
kmem_cache_free(avtab_node_cachep, tmp);
goto error;
}
tmp->datum.u.xperms = node->datum.u.xperms;
} else
tmp->datum.u.data = node->datum.u.data;
if (tail)
tail->next = tmp;
else
new->htable[i] = tmp;
tail = tmp;
new->nel++;
if (nrules != 0) {
u32 shift = 1;
u32 work = nrules >> 3;
while (work) {
work >>= 1;
shift++;
}
nslot = 1 << shift;
if (nslot > MAX_AVTAB_HASH_BUCKETS)
nslot = MAX_AVTAB_HASH_BUCKETS;
rc = avtab_alloc_common(h, nslot);
if (rc)
return rc;
}
pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules);
return 0;
error:
avtab_destroy(new);
return -ENOMEM;
}
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig)
{
return avtab_alloc_common(new, orig->nslot);
}
void avtab_hash_eval(struct avtab *h, char *tag)

View File

@ -89,7 +89,7 @@ struct avtab {
void avtab_init(struct avtab *h);
int avtab_alloc(struct avtab *, u32);
int avtab_duplicate(struct avtab *new, struct avtab *orig);
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h);
void avtab_hash_eval(struct avtab *h, char *tag);

View File

@ -605,7 +605,6 @@ static int cond_dup_av_list(struct cond_av_list *new,
struct cond_av_list *orig,
struct avtab *avtab)
{
struct avtab_node *avnode;
u32 i;
memset(new, 0, sizeof(*new));
@ -615,10 +614,11 @@ static int cond_dup_av_list(struct cond_av_list *new,
return -ENOMEM;
for (i = 0; i < orig->len; i++) {
avnode = avtab_search_node(avtab, &orig->nodes[i]->key);
if (WARN_ON(!avnode))
return -EINVAL;
new->nodes[i] = avnode;
new->nodes[i] = avtab_insert_nonunique(avtab,
&orig->nodes[i]->key,
&orig->nodes[i]->datum);
if (!new->nodes[i])
return -ENOMEM;
new->len++;
}
@ -630,7 +630,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp,
{
int rc, i, j;
rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab);
rc = avtab_alloc_dup(&newp->te_cond_avtab, &origp->te_cond_avtab);
if (rc)
return rc;

View File

@ -1552,6 +1552,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
if (!str)
goto out;
}
retry:
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -1565,6 +1566,15 @@ static int security_context_to_sid_core(struct selinux_state *state,
} else if (rc)
goto out_unlock;
rc = sidtab_context_to_sid(sidtab, &context, sid);
if (rc == -ESTALE) {
rcu_read_unlock();
if (context.str) {
str = context.str;
context.str = NULL;
}
context_destroy(&context);
goto retry;
}
context_destroy(&context);
out_unlock:
rcu_read_unlock();
@ -1714,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state,
struct selinux_policy *policy;
struct policydb *policydb;
struct sidtab *sidtab;
struct class_datum *cladatum = NULL;
struct class_datum *cladatum;
struct context *scontext, *tcontext, newcontext;
struct sidtab_entry *sentry, *tentry;
struct avtab_key avkey;
@ -1736,6 +1746,8 @@ static int security_compute_sid(struct selinux_state *state,
goto out;
}
retry:
cladatum = NULL;
context_init(&newcontext);
rcu_read_lock();
@ -1880,6 +1892,11 @@ static int security_compute_sid(struct selinux_state *state,
}
/* Obtain the sid for the context. */
rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
context_destroy(&newcontext);
goto retry;
}
out_unlock:
rcu_read_unlock();
context_destroy(&newcontext);
@ -2192,6 +2209,7 @@ void selinux_policy_commit(struct selinux_state *state,
struct selinux_load_state *load_state)
{
struct selinux_policy *oldpolicy, *newpolicy = load_state->policy;
unsigned long flags;
u32 seqno;
oldpolicy = rcu_dereference_protected(state->policy,
@ -2213,7 +2231,13 @@ void selinux_policy_commit(struct selinux_state *state,
seqno = newpolicy->latest_granting;
/* Install the new policy. */
rcu_assign_pointer(state->policy, newpolicy);
if (oldpolicy) {
sidtab_freeze_begin(oldpolicy->sidtab, &flags);
rcu_assign_pointer(state->policy, newpolicy);
sidtab_freeze_end(oldpolicy->sidtab, &flags);
} else {
rcu_assign_pointer(state->policy, newpolicy);
}
/* Load the policycaps from the new policy */
security_load_policycaps(state, newpolicy);
@ -2357,13 +2381,15 @@ int security_port_sid(struct selinux_state *state,
struct policydb *policydb;
struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
int rc;
if (!selinux_initialized(state)) {
*out_sid = SECINITSID_PORT;
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2382,6 +2408,10 @@ int security_port_sid(struct selinux_state *state,
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2408,13 +2438,15 @@ int security_ib_pkey_sid(struct selinux_state *state,
struct policydb *policydb;
struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
int rc;
if (!selinux_initialized(state)) {
*out_sid = SECINITSID_UNLABELED;
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2435,6 +2467,10 @@ int security_ib_pkey_sid(struct selinux_state *state,
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2460,13 +2496,15 @@ int security_ib_endport_sid(struct selinux_state *state,
struct policydb *policydb;
struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
int rc;
if (!selinux_initialized(state)) {
*out_sid = SECINITSID_UNLABELED;
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2487,6 +2525,10 @@ int security_ib_endport_sid(struct selinux_state *state,
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2510,7 +2552,7 @@ int security_netif_sid(struct selinux_state *state,
struct selinux_policy *policy;
struct policydb *policydb;
struct sidtab *sidtab;
int rc = 0;
int rc;
struct ocontext *c;
if (!selinux_initialized(state)) {
@ -2518,6 +2560,8 @@ int security_netif_sid(struct selinux_state *state,
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2534,10 +2578,18 @@ int security_netif_sid(struct selinux_state *state,
if (!c->sid[0] || !c->sid[1]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = sidtab_context_to_sid(sidtab, &c->context[1],
&c->sid[1]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2587,6 +2639,7 @@ int security_node_sid(struct selinux_state *state,
return 0;
}
retry:
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2635,6 +2688,10 @@ int security_node_sid(struct selinux_state *state,
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2676,18 +2733,24 @@ int security_get_user_sids(struct selinux_state *state,
struct sidtab *sidtab;
struct context *fromcon, usercon;
u32 *mysids = NULL, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
u32 i, j, mynel, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
struct ebitmap_node *rnode, *tnode;
int rc = 0, i, j;
int rc;
*sids = NULL;
*nel = 0;
if (!selinux_initialized(state))
goto out;
return 0;
mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL);
if (!mysids)
return -ENOMEM;
retry:
mynel = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2707,11 +2770,6 @@ int security_get_user_sids(struct selinux_state *state,
usercon.user = user->value;
rc = -ENOMEM;
mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
if (!mysids)
goto out_unlock;
ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
role = policydb->role_val_to_struct[i];
usercon.role = i + 1;
@ -2723,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state,
continue;
rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out_unlock;
if (mynel < maxnel) {
@ -2745,14 +2807,14 @@ int security_get_user_sids(struct selinux_state *state,
rcu_read_unlock();
if (rc || !mynel) {
kfree(mysids);
goto out;
return rc;
}
rc = -ENOMEM;
mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
if (!mysids2) {
kfree(mysids);
goto out;
return rc;
}
for (i = 0, j = 0; i < mynel; i++) {
struct av_decision dummy_avd;
@ -2765,12 +2827,10 @@ int security_get_user_sids(struct selinux_state *state,
mysids2[j++] = mysids[i];
cond_resched();
}
rc = 0;
kfree(mysids);
*sids = mysids2;
*nel = j;
out:
return rc;
return 0;
}
/**
@ -2783,6 +2843,9 @@ int security_get_user_sids(struct selinux_state *state,
* Obtain a SID to use for a file in a filesystem that
* cannot support xattr or use a fixed labeling behavior like
* transition SIDs or task SIDs.
*
* WARNING: This function may return -ESTALE, indicating that the caller
* must retry the operation after re-acquiring the policy pointer!
*/
static inline int __security_genfs_sid(struct selinux_policy *policy,
const char *fstype,
@ -2861,11 +2924,13 @@ int security_genfs_sid(struct selinux_state *state,
return 0;
}
rcu_read_lock();
policy = rcu_dereference(state->policy);
retval = __security_genfs_sid(policy,
fstype, path, orig_sclass, sid);
rcu_read_unlock();
do {
rcu_read_lock();
policy = rcu_dereference(state->policy);
retval = __security_genfs_sid(policy, fstype, path,
orig_sclass, sid);
rcu_read_unlock();
} while (retval == -ESTALE);
return retval;
}
@ -2888,7 +2953,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
struct selinux_policy *policy;
struct policydb *policydb;
struct sidtab *sidtab;
int rc = 0;
int rc;
struct ocontext *c;
struct superblock_security_struct *sbsec = sb->s_security;
const char *fstype = sb->s_type->name;
@ -2899,6 +2964,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -2916,6 +2983,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
}
@ -2923,6 +2994,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
} else {
rc = __security_genfs_sid(policy, fstype, "/",
SECCLASS_DIR, &sbsec->sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) {
sbsec->behavior = SECURITY_FS_USE_NONE;
rc = 0;
@ -3132,12 +3207,13 @@ int security_sid_mls_copy(struct selinux_state *state,
u32 len;
int rc;
rc = 0;
if (!selinux_initialized(state)) {
*new_sid = sid;
goto out;
return 0;
}
retry:
rc = 0;
context_init(&newcon);
rcu_read_lock();
@ -3196,10 +3272,14 @@ int security_sid_mls_copy(struct selinux_state *state,
}
}
rc = sidtab_context_to_sid(sidtab, &newcon, new_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
context_destroy(&newcon);
goto retry;
}
out_unlock:
rcu_read_unlock();
context_destroy(&newcon);
out:
return rc;
}
@ -3792,6 +3872,8 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
return 0;
}
retry:
rc = 0;
rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &policy->policydb;
@ -3818,23 +3900,24 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
goto out;
}
rc = -EIDRM;
if (!mls_context_isvalid(policydb, &ctx_new))
goto out_free;
if (!mls_context_isvalid(policydb, &ctx_new)) {
ebitmap_destroy(&ctx_new.range.level[0].cat);
goto out;
}
rc = sidtab_context_to_sid(sidtab, &ctx_new, sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out_free;
goto out;
security_netlbl_cache_add(secattr, *sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
} else
*sid = SECSID_NULL;
rcu_read_unlock();
return 0;
out_free:
ebitmap_destroy(&ctx_new.range.level[0].cat);
out:
rcu_read_unlock();
return rc;

View File

@ -39,6 +39,7 @@ int sidtab_init(struct sidtab *s)
for (i = 0; i < SECINITSID_NUM; i++)
s->isids[i].set = 0;
s->frozen = false;
s->count = 0;
s->convert = NULL;
hash_init(s->context_to_sid);
@ -281,6 +282,15 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
if (*sid)
goto out_unlock;
if (unlikely(s->frozen)) {
/*
* This sidtab is now frozen - tell the caller to abort and
* get the new one.
*/
rc = -ESTALE;
goto out_unlock;
}
count = s->count;
convert = s->convert;
@ -474,6 +484,17 @@ void sidtab_cancel_convert(struct sidtab *s)
spin_unlock_irqrestore(&s->lock, flags);
}
void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock)
{
spin_lock_irqsave(&s->lock, *flags);
s->frozen = true;
s->convert = NULL;
}
void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock)
{
spin_unlock_irqrestore(&s->lock, *flags);
}
static void sidtab_destroy_entry(struct sidtab_entry *entry)
{
context_destroy(&entry->context);

View File

@ -86,6 +86,7 @@ struct sidtab {
u32 count;
/* access only under spinlock */
struct sidtab_convert_params *convert;
bool frozen;
spinlock_t lock;
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
@ -125,6 +126,9 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
void sidtab_cancel_convert(struct sidtab *s);
void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock);
void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock);
int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
void sidtab_destroy(struct sidtab *s);