Commit 60144b23 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 189fefc7 9ad6e9cb
...@@ -109,7 +109,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat ...@@ -109,7 +109,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
struct avtab_node *prev, *cur, *newnode; struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h) if (!h || !h->nslot)
return -EINVAL; return -EINVAL;
hvalue = avtab_hash(key, h->mask); hvalue = avtab_hash(key, h->mask);
...@@ -154,7 +154,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu ...@@ -154,7 +154,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu
struct avtab_node *prev, *cur; struct avtab_node *prev, *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h) if (!h || !h->nslot)
return NULL; return NULL;
hvalue = avtab_hash(key, h->mask); hvalue = avtab_hash(key, h->mask);
for (prev = NULL, cur = h->htable[hvalue]; for (prev = NULL, cur = h->htable[hvalue];
...@@ -184,7 +184,7 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) ...@@ -184,7 +184,7 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
struct avtab_node *cur; struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h) if (!h || !h->nslot)
return NULL; return NULL;
hvalue = avtab_hash(key, h->mask); hvalue = avtab_hash(key, h->mask);
...@@ -220,7 +220,7 @@ avtab_search_node(struct avtab *h, struct avtab_key *key) ...@@ -220,7 +220,7 @@ avtab_search_node(struct avtab *h, struct avtab_key *key)
struct avtab_node *cur; struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h) if (!h || !h->nslot)
return NULL; return NULL;
hvalue = avtab_hash(key, h->mask); hvalue = avtab_hash(key, h->mask);
...@@ -295,6 +295,7 @@ void avtab_destroy(struct avtab *h) ...@@ -295,6 +295,7 @@ void avtab_destroy(struct avtab *h)
} }
kvfree(h->htable); kvfree(h->htable);
h->htable = NULL; h->htable = NULL;
h->nel = 0;
h->nslot = 0; h->nslot = 0;
h->mask = 0; h->mask = 0;
} }
...@@ -303,88 +304,52 @@ void avtab_init(struct avtab *h) ...@@ -303,88 +304,52 @@ void avtab_init(struct avtab *h)
{ {
h->htable = NULL; h->htable = NULL;
h->nel = 0; 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; if (!nslot)
u32 shift = 0; return 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;
h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL); h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);
if (!h->htable) if (!h->htable)
return -ENOMEM; return -ENOMEM;
avtab_alloc_out:
h->nel = 0;
h->nslot = nslot; h->nslot = nslot;
h->mask = mask; h->mask = nslot - 1;
pr_debug("SELinux: %d avtab hash slots, %d rules.\n",
h->nslot, nrules);
return 0; return 0;
} }
int avtab_duplicate(struct avtab *new, struct avtab *orig) int avtab_alloc(struct avtab *h, u32 nrules)
{ {
int i; int rc;
struct avtab_node *node, *tmp, *tail; u32 nslot = 0;
memset(new, 0, sizeof(*new));
new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL); if (nrules != 0) {
if (!new->htable) u32 shift = 1;
return -ENOMEM; u32 work = nrules >> 3;
new->nslot = orig->nslot; while (work) {
new->mask = orig->mask; work >>= 1;
shift++;
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++;
} }
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; 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) void avtab_hash_eval(struct avtab *h, char *tag)
......
...@@ -89,7 +89,7 @@ struct avtab { ...@@ -89,7 +89,7 @@ struct avtab {
void avtab_init(struct avtab *h); void avtab_init(struct avtab *h);
int avtab_alloc(struct avtab *, u32); 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); struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h); void avtab_destroy(struct avtab *h);
void avtab_hash_eval(struct avtab *h, char *tag); void avtab_hash_eval(struct avtab *h, char *tag);
......
...@@ -605,7 +605,6 @@ static int cond_dup_av_list(struct cond_av_list *new, ...@@ -605,7 +605,6 @@ static int cond_dup_av_list(struct cond_av_list *new,
struct cond_av_list *orig, struct cond_av_list *orig,
struct avtab *avtab) struct avtab *avtab)
{ {
struct avtab_node *avnode;
u32 i; u32 i;
memset(new, 0, sizeof(*new)); memset(new, 0, sizeof(*new));
...@@ -615,10 +614,11 @@ static int cond_dup_av_list(struct cond_av_list *new, ...@@ -615,10 +614,11 @@ static int cond_dup_av_list(struct cond_av_list *new,
return -ENOMEM; return -ENOMEM;
for (i = 0; i < orig->len; i++) { for (i = 0; i < orig->len; i++) {
avnode = avtab_search_node(avtab, &orig->nodes[i]->key); new->nodes[i] = avtab_insert_nonunique(avtab,
if (WARN_ON(!avnode)) &orig->nodes[i]->key,
return -EINVAL; &orig->nodes[i]->datum);
new->nodes[i] = avnode; if (!new->nodes[i])
return -ENOMEM;
new->len++; new->len++;
} }
...@@ -630,7 +630,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp, ...@@ -630,7 +630,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp,
{ {
int rc, i, j; 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) if (rc)
return rc; return rc;
......
...@@ -1552,6 +1552,7 @@ static int security_context_to_sid_core(struct selinux_state *state, ...@@ -1552,6 +1552,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
if (!str) if (!str)
goto out; goto out;
} }
retry:
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -1565,6 +1566,15 @@ static int security_context_to_sid_core(struct selinux_state *state, ...@@ -1565,6 +1566,15 @@ static int security_context_to_sid_core(struct selinux_state *state,
} else if (rc) } else if (rc)
goto out_unlock; goto out_unlock;
rc = sidtab_context_to_sid(sidtab, &context, sid); 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); context_destroy(&context);
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
...@@ -1714,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1714,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state,
struct selinux_policy *policy; struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct class_datum *cladatum = NULL; struct class_datum *cladatum;
struct context *scontext, *tcontext, newcontext; struct context *scontext, *tcontext, newcontext;
struct sidtab_entry *sentry, *tentry; struct sidtab_entry *sentry, *tentry;
struct avtab_key avkey; struct avtab_key avkey;
...@@ -1736,6 +1746,8 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1736,6 +1746,8 @@ static int security_compute_sid(struct selinux_state *state,
goto out; goto out;
} }
retry:
cladatum = NULL;
context_init(&newcontext); context_init(&newcontext);
rcu_read_lock(); rcu_read_lock();
...@@ -1880,6 +1892,11 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1880,6 +1892,11 @@ static int security_compute_sid(struct selinux_state *state,
} }
/* Obtain the sid for the context. */ /* Obtain the sid for the context. */
rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
context_destroy(&newcontext);
goto retry;
}
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
context_destroy(&newcontext); context_destroy(&newcontext);
...@@ -2192,6 +2209,7 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2192,6 +2209,7 @@ void selinux_policy_commit(struct selinux_state *state,
struct selinux_load_state *load_state) struct selinux_load_state *load_state)
{ {
struct selinux_policy *oldpolicy, *newpolicy = load_state->policy; struct selinux_policy *oldpolicy, *newpolicy = load_state->policy;
unsigned long flags;
u32 seqno; u32 seqno;
oldpolicy = rcu_dereference_protected(state->policy, oldpolicy = rcu_dereference_protected(state->policy,
...@@ -2213,7 +2231,13 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2213,7 +2231,13 @@ void selinux_policy_commit(struct selinux_state *state,
seqno = newpolicy->latest_granting; seqno = newpolicy->latest_granting;
/* Install the new policy. */ /* 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 */ /* Load the policycaps from the new policy */
security_load_policycaps(state, newpolicy); security_load_policycaps(state, newpolicy);
...@@ -2357,13 +2381,15 @@ int security_port_sid(struct selinux_state *state, ...@@ -2357,13 +2381,15 @@ int security_port_sid(struct selinux_state *state,
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
int rc = 0; int rc;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
*out_sid = SECINITSID_PORT; *out_sid = SECINITSID_PORT;
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2382,6 +2408,10 @@ int security_port_sid(struct selinux_state *state, ...@@ -2382,6 +2408,10 @@ int security_port_sid(struct selinux_state *state,
if (!c->sid[0]) { if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2408,13 +2438,15 @@ int security_ib_pkey_sid(struct selinux_state *state, ...@@ -2408,13 +2438,15 @@ int security_ib_pkey_sid(struct selinux_state *state,
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
int rc = 0; int rc;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
*out_sid = SECINITSID_UNLABELED; *out_sid = SECINITSID_UNLABELED;
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2435,6 +2467,10 @@ int security_ib_pkey_sid(struct selinux_state *state, ...@@ -2435,6 +2467,10 @@ int security_ib_pkey_sid(struct selinux_state *state,
rc = sidtab_context_to_sid(sidtab, rc = sidtab_context_to_sid(sidtab,
&c->context[0], &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2460,13 +2496,15 @@ int security_ib_endport_sid(struct selinux_state *state, ...@@ -2460,13 +2496,15 @@ int security_ib_endport_sid(struct selinux_state *state,
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
int rc = 0; int rc;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
*out_sid = SECINITSID_UNLABELED; *out_sid = SECINITSID_UNLABELED;
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2487,6 +2525,10 @@ int security_ib_endport_sid(struct selinux_state *state, ...@@ -2487,6 +2525,10 @@ int security_ib_endport_sid(struct selinux_state *state,
if (!c->sid[0]) { if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2510,7 +2552,7 @@ int security_netif_sid(struct selinux_state *state, ...@@ -2510,7 +2552,7 @@ int security_netif_sid(struct selinux_state *state,
struct selinux_policy *policy; struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc = 0; int rc;
struct ocontext *c; struct ocontext *c;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
...@@ -2518,6 +2560,8 @@ int security_netif_sid(struct selinux_state *state, ...@@ -2518,6 +2560,8 @@ int security_netif_sid(struct selinux_state *state,
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2534,10 +2578,18 @@ int security_netif_sid(struct selinux_state *state, ...@@ -2534,10 +2578,18 @@ int security_netif_sid(struct selinux_state *state,
if (!c->sid[0] || !c->sid[1]) { if (!c->sid[0] || !c->sid[1]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
rc = sidtab_context_to_sid(sidtab, &c->context[1], rc = sidtab_context_to_sid(sidtab, &c->context[1],
&c->sid[1]); &c->sid[1]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2587,6 +2639,7 @@ int security_node_sid(struct selinux_state *state, ...@@ -2587,6 +2639,7 @@ int security_node_sid(struct selinux_state *state,
return 0; return 0;
} }
retry:
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2635,6 +2688,10 @@ int security_node_sid(struct selinux_state *state, ...@@ -2635,6 +2688,10 @@ int security_node_sid(struct selinux_state *state,
rc = sidtab_context_to_sid(sidtab, rc = sidtab_context_to_sid(sidtab,
&c->context[0], &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2676,18 +2733,24 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2676,18 +2733,24 @@ int security_get_user_sids(struct selinux_state *state,
struct sidtab *sidtab; struct sidtab *sidtab;
struct context *fromcon, usercon; struct context *fromcon, usercon;
u32 *mysids = NULL, *mysids2, sid; u32 *mysids = NULL, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL; u32 i, j, mynel, maxnel = SIDS_NEL;
struct user_datum *user; struct user_datum *user;
struct role_datum *role; struct role_datum *role;
struct ebitmap_node *rnode, *tnode; struct ebitmap_node *rnode, *tnode;
int rc = 0, i, j; int rc;
*sids = NULL; *sids = NULL;
*nel = 0; *nel = 0;
if (!selinux_initialized(state)) 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(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2707,11 +2770,6 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2707,11 +2770,6 @@ int security_get_user_sids(struct selinux_state *state,
usercon.user = user->value; 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) { ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
role = policydb->role_val_to_struct[i]; role = policydb->role_val_to_struct[i];
usercon.role = i + 1; usercon.role = i + 1;
...@@ -2723,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2723,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state,
continue; continue;
rc = sidtab_context_to_sid(sidtab, &usercon, &sid); rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out_unlock; goto out_unlock;
if (mynel < maxnel) { if (mynel < maxnel) {
...@@ -2745,14 +2807,14 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2745,14 +2807,14 @@ int security_get_user_sids(struct selinux_state *state,
rcu_read_unlock(); rcu_read_unlock();
if (rc || !mynel) { if (rc || !mynel) {
kfree(mysids); kfree(mysids);
goto out; return rc;
} }
rc = -ENOMEM; rc = -ENOMEM;
mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
if (!mysids2) { if (!mysids2) {
kfree(mysids); kfree(mysids);
goto out; return rc;
} }
for (i = 0, j = 0; i < mynel; i++) { for (i = 0, j = 0; i < mynel; i++) {
struct av_decision dummy_avd; struct av_decision dummy_avd;
...@@ -2765,12 +2827,10 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2765,12 +2827,10 @@ int security_get_user_sids(struct selinux_state *state,
mysids2[j++] = mysids[i]; mysids2[j++] = mysids[i];
cond_resched(); cond_resched();
} }
rc = 0;
kfree(mysids); kfree(mysids);
*sids = mysids2; *sids = mysids2;
*nel = j; *nel = j;
out: return 0;
return rc;
} }
/** /**
...@@ -2783,6 +2843,9 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -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 * Obtain a SID to use for a file in a filesystem that
* cannot support xattr or use a fixed labeling behavior like * cannot support xattr or use a fixed labeling behavior like
* transition SIDs or task SIDs. * 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, static inline int __security_genfs_sid(struct selinux_policy *policy,
const char *fstype, const char *fstype,
...@@ -2861,11 +2924,13 @@ int security_genfs_sid(struct selinux_state *state, ...@@ -2861,11 +2924,13 @@ int security_genfs_sid(struct selinux_state *state,
return 0; return 0;
} }
rcu_read_lock(); do {
policy = rcu_dereference(state->policy); rcu_read_lock();
retval = __security_genfs_sid(policy, policy = rcu_dereference(state->policy);
fstype, path, orig_sclass, sid); retval = __security_genfs_sid(policy, fstype, path,
rcu_read_unlock(); orig_sclass, sid);
rcu_read_unlock();
} while (retval == -ESTALE);
return retval; return retval;
} }
...@@ -2888,7 +2953,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2888,7 +2953,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
struct selinux_policy *policy; struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc = 0; int rc;
struct ocontext *c; struct ocontext *c;
struct superblock_security_struct *sbsec = sb->s_security; struct superblock_security_struct *sbsec = sb->s_security;
const char *fstype = sb->s_type->name; const char *fstype = sb->s_type->name;
...@@ -2899,6 +2964,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2899,6 +2964,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -2916,6 +2983,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2916,6 +2983,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
if (!c->sid[0]) { if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]); &c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) if (rc)
goto out; goto out;
} }
...@@ -2923,6 +2994,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2923,6 +2994,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
} else { } else {
rc = __security_genfs_sid(policy, fstype, "/", rc = __security_genfs_sid(policy, fstype, "/",
SECCLASS_DIR, &sbsec->sid); SECCLASS_DIR, &sbsec->sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc) { if (rc) {
sbsec->behavior = SECURITY_FS_USE_NONE; sbsec->behavior = SECURITY_FS_USE_NONE;
rc = 0; rc = 0;
...@@ -3132,12 +3207,13 @@ int security_sid_mls_copy(struct selinux_state *state, ...@@ -3132,12 +3207,13 @@ int security_sid_mls_copy(struct selinux_state *state,
u32 len; u32 len;
int rc; int rc;
rc = 0;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
*new_sid = sid; *new_sid = sid;
goto out; return 0;
} }
retry:
rc = 0;
context_init(&newcon); context_init(&newcon);
rcu_read_lock(); rcu_read_lock();
...@@ -3196,10 +3272,14 @@ int security_sid_mls_copy(struct selinux_state *state, ...@@ -3196,10 +3272,14 @@ int security_sid_mls_copy(struct selinux_state *state,
} }
} }
rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); rc = sidtab_context_to_sid(sidtab, &newcon, new_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
context_destroy(&newcon);
goto retry;
}
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
context_destroy(&newcon); context_destroy(&newcon);
out:
return rc; return rc;
} }
...@@ -3792,6 +3872,8 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3792,6 +3872,8 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
return 0; return 0;
} }
retry:
rc = 0;
rcu_read_lock(); rcu_read_lock();
policy = rcu_dereference(state->policy); policy = rcu_dereference(state->policy);
policydb = &policy->policydb; policydb = &policy->policydb;
...@@ -3818,23 +3900,24 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3818,23 +3900,24 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
goto out; goto out;
} }
rc = -EIDRM; rc = -EIDRM;
if (!mls_context_isvalid(policydb, &ctx_new)) if (!mls_context_isvalid(policydb, &ctx_new)) {
goto out_free; ebitmap_destroy(&ctx_new.range.level[0].cat);
goto out;
}
rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); 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) if (rc)
goto out_free; goto out;
security_netlbl_cache_add(secattr, *sid); security_netlbl_cache_add(secattr, *sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
} else } else
*sid = SECSID_NULL; *sid = SECSID_NULL;
rcu_read_unlock();
return 0;
out_free:
ebitmap_destroy(&ctx_new.range.level[0].cat);
out: out:
rcu_read_unlock(); rcu_read_unlock();
return rc; return rc;
......
...@@ -39,6 +39,7 @@ int sidtab_init(struct sidtab *s) ...@@ -39,6 +39,7 @@ int sidtab_init(struct sidtab *s)
for (i = 0; i < SECINITSID_NUM; i++) for (i = 0; i < SECINITSID_NUM; i++)
s->isids[i].set = 0; s->isids[i].set = 0;
s->frozen = false;
s->count = 0; s->count = 0;
s->convert = NULL; s->convert = NULL;
hash_init(s->context_to_sid); hash_init(s->context_to_sid);
...@@ -281,6 +282,15 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context, ...@@ -281,6 +282,15 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
if (*sid) if (*sid)
goto out_unlock; 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; count = s->count;
convert = s->convert; convert = s->convert;
...@@ -474,6 +484,17 @@ void sidtab_cancel_convert(struct sidtab *s) ...@@ -474,6 +484,17 @@ void sidtab_cancel_convert(struct sidtab *s)
spin_unlock_irqrestore(&s->lock, flags); 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) static void sidtab_destroy_entry(struct sidtab_entry *entry)
{ {
context_destroy(&entry->context); context_destroy(&entry->context);
......
...@@ -86,6 +86,7 @@ struct sidtab { ...@@ -86,6 +86,7 @@ struct sidtab {
u32 count; u32 count;
/* access only under spinlock */ /* access only under spinlock */
struct sidtab_convert_params *convert; struct sidtab_convert_params *convert;
bool frozen;
spinlock_t lock; spinlock_t lock;
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
...@@ -125,6 +126,9 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params); ...@@ -125,6 +126,9 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
void sidtab_cancel_convert(struct sidtab *s); 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); int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
void sidtab_destroy(struct sidtab *s); void sidtab_destroy(struct sidtab *s);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment