From c7c556f1e81bb9e09656ed6650d0c44c84b7c016 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 11 Aug 2020 15:01:56 -0400 Subject: [PATCH] selinux: refactor changing booleans Refactor the logic for changing SELinux policy booleans in a similar manner to the refactoring of policy load, thereby reducing the size of the critical section when the policy write-lock is held and making it easier to convert the policy rwlock to RCU in the future. Instead of directly modifying the policydb in place, modify a copy and then swap it into place through a single pointer update. Only fully copy the portions of the policydb that are affected by boolean changes to avoid the full cost of a deep policydb copy. Introduce another level of indirection for the sidtab since changing booleans does not require updating the sidtab, unlike policy load. While we are here, create a common helper for notifying other kernel components and userspace of a policy change and call it from both security_set_bools() and selinux_policy_commit(). Based on an old (2004) patch by Kaigai Kohei [1] to convert the policy rwlock to RCU that was deferred at the time since it did not significantly improve performance and introduced complexity. Peter Enderborg later submitted a patch series to convert to RCU [2] that would have made changing booleans a much more expensive operation by requiring a full policydb_write();policydb_read(); sequence to deep copy the entire policydb and also had concerns regarding atomic allocations. This change is now simplified by the earlier work to encapsulate policy state in the selinux_policy struct and to refactor policy load. After this change, the last major obstacle to converting the policy rwlock to RCU is likely the sidtab live convert support. [1] https://lore.kernel.org/selinux/6e2f9128-e191-ebb3-0e87-74bfccb0767f@tycho.nsa.gov/ [2] https://lore.kernel.org/selinux/20180530141104.28569-1-peter.enderborg@sony.com/ Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/avtab.c | 49 ++++++++- security/selinux/ss/avtab.h | 1 + security/selinux/ss/conditional.c | 156 ++++++++++++++++++++++++++++ security/selinux/ss/conditional.h | 2 + security/selinux/ss/hashtab.c | 53 ++++++++++ security/selinux/ss/hashtab.h | 6 ++ security/selinux/ss/services.c | 163 ++++++++++++++++++------------ security/selinux/ss/services.h | 2 +- 8 files changed, 368 insertions(+), 64 deletions(-) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 01b300a4a882..0172d87e2b9a 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -301,7 +301,6 @@ void avtab_destroy(struct avtab *h) void avtab_init(struct avtab *h) { - kvfree(h->htable); h->htable = NULL; h->nel = 0; } @@ -340,6 +339,54 @@ int avtab_alloc(struct avtab *h, u32 nrules) return 0; } +int avtab_duplicate(struct avtab *new, struct avtab *orig) +{ + int i; + struct avtab_node *node, *tmp, *tail; + + 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++; + } + } + + return 0; +error: + avtab_destroy(new); + return -ENOMEM; +} + void avtab_hash_eval(struct avtab *h, char *tag) { int i, chain_len, slots_used, max_chain_len; diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 5fdcb6696bcc..4c4445ca9118 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -89,6 +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); 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); diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 5a47258c1d77..05c7a10e0aab 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -600,3 +600,159 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, services_compute_xperms_drivers(xperms, node); } } + +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)); + + new->nodes = kcalloc(orig->len, sizeof(*new->nodes), GFP_KERNEL); + if (!new->nodes) + 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->len++; + } + + return 0; +} + +static int duplicate_policydb_cond_list(struct policydb *newp, + struct policydb *origp) +{ + int rc, i, j; + + rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab); + if (rc) + return rc; + + newp->cond_list_len = 0; + newp->cond_list = kcalloc(origp->cond_list_len, + sizeof(*newp->cond_list), + GFP_KERNEL); + if (!newp->cond_list) + goto error; + + for (i = 0; i < origp->cond_list_len; i++) { + struct cond_node *newn = &newp->cond_list[i]; + struct cond_node *orign = &origp->cond_list[i]; + + newp->cond_list_len++; + + newn->cur_state = orign->cur_state; + newn->expr.nodes = kcalloc(orign->expr.len, + sizeof(*newn->expr.nodes), GFP_KERNEL); + if (!newn->expr.nodes) + goto error; + for (j = 0; j < orign->expr.len; j++) + newn->expr.nodes[j] = orign->expr.nodes[j]; + newn->expr.len = orign->expr.len; + + rc = cond_dup_av_list(&newn->true_list, &orign->true_list, + &newp->te_cond_avtab); + if (rc) + goto error; + + rc = cond_dup_av_list(&newn->false_list, &orign->false_list, + &newp->te_cond_avtab); + if (rc) + goto error; + } + + return 0; + +error: + avtab_destroy(&newp->te_cond_avtab); + cond_list_destroy(newp); + return -ENOMEM; +} + +static int cond_bools_destroy(void *key, void *datum, void *args) +{ + /* key was not copied so no need to free here */ + kfree(datum); + return 0; +} + +static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args) +{ + struct cond_bool_datum *datum; + + datum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); + if (!datum) + return -ENOMEM; + + memcpy(datum, orig->datum, sizeof(struct cond_bool_datum)); + + new->key = orig->key; /* No need to copy, never modified */ + new->datum = datum; + return 0; +} + +static int cond_bools_index(void *key, void *datum, void *args) +{ + struct cond_bool_datum *booldatum, **cond_bool_array; + + booldatum = datum; + cond_bool_array = args; + cond_bool_array[booldatum->value - 1] = booldatum; + + return 0; +} + +static int duplicate_policydb_bools(struct policydb *newdb, + struct policydb *orig) +{ + struct cond_bool_datum **cond_bool_array; + int rc; + + cond_bool_array = kmalloc_array(orig->p_bools.nprim, + sizeof(*orig->bool_val_to_struct), + GFP_KERNEL); + if (!cond_bool_array) + return -ENOMEM; + + rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table, + cond_bools_copy, cond_bools_destroy, NULL); + if (rc) { + kfree(cond_bool_array); + return -ENOMEM; + } + + hashtab_map(&newdb->p_bools.table, cond_bools_index, cond_bool_array); + newdb->bool_val_to_struct = cond_bool_array; + + newdb->p_bools.nprim = orig->p_bools.nprim; + + return 0; +} + +void cond_policydb_destroy_dup(struct policydb *p) +{ + hashtab_map(&p->p_bools.table, cond_bools_destroy, NULL); + hashtab_destroy(&p->p_bools.table); + cond_policydb_destroy(p); +} + +int cond_policydb_dup(struct policydb *new, struct policydb *orig) +{ + cond_policydb_init(new); + + if (duplicate_policydb_bools(new, orig)) + return -ENOMEM; + + if (duplicate_policydb_cond_list(new, orig)) { + cond_policydb_destroy_dup(new); + return -ENOMEM; + } + + return 0; +} diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index 79e7e03db859..e47ec6ddeaf6 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -79,5 +79,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key, struct extended_perms_decision *xpermd); void evaluate_cond_nodes(struct policydb *p); +void cond_policydb_destroy_dup(struct policydb *p); +int cond_policydb_dup(struct policydb *new, struct policydb *orig); #endif /* _CONDITIONAL_H_ */ diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index d9287bb4bfeb..dab8c25c739b 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -122,6 +122,59 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info) info->max_chain_len = max_chain_len; } +int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, + int (*copy)(struct hashtab_node *new, + struct hashtab_node *orig, void *args), + int (*destroy)(void *k, void *d, void *args), + void *args) +{ + struct hashtab_node *cur, *tmp, *tail; + int i, rc; + + memset(new, 0, sizeof(*new)); + + new->htable = kcalloc(orig->size, sizeof(*new->htable), GFP_KERNEL); + if (!new->htable) + return -ENOMEM; + + new->size = orig->size; + + for (i = 0; i < orig->size; i++) { + tail = NULL; + for (cur = orig->htable[i]; cur; cur = cur->next) { + tmp = kmem_cache_zalloc(hashtab_node_cachep, + GFP_KERNEL); + if (!tmp) + goto error; + rc = copy(tmp, cur, args); + if (rc) { + kmem_cache_free(hashtab_node_cachep, tmp); + goto error; + } + tmp->next = NULL; + if (!tail) + new->htable[i] = tmp; + else + tail->next = tmp; + tail = tmp; + new->nel++; + } + } + + return 0; + + error: + for (i = 0; i < new->size; i++) { + for (cur = new->htable[i]; cur; cur = tmp) { + tmp = cur->next; + destroy(cur->key, cur->datum, args); + kmem_cache_free(hashtab_node_cachep, cur); + } + } + kmem_cache_free(hashtab_node_cachep, new); + return -ENOMEM; +} + void __init hashtab_cache_init(void) { hashtab_node_cachep = kmem_cache_create("hashtab_node", diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 3c952f0f01f9..043a773bf0b7 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -136,6 +136,12 @@ int hashtab_map(struct hashtab *h, int (*apply)(void *k, void *d, void *args), void *args); +int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, + int (*copy)(struct hashtab_node *new, + struct hashtab_node *orig, void *args), + int (*destroy)(void *k, void *d, void *args), + void *args); + /* Fill info with some hash table statistics */ void hashtab_stat(struct hashtab *h, struct hashtab_info *info); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index a3f26b03c123..f6f78c65f53f 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -723,7 +723,7 @@ static int security_validtrans_handle_fail(struct selinux_state *state, u16 tclass) { struct policydb *p = &state->ss->policy->policydb; - struct sidtab *sidtab = &state->ss->policy->sidtab; + struct sidtab *sidtab = state->ss->policy->sidtab; char *o = NULL, *n = NULL, *t = NULL; u32 olen, nlen, tlen; @@ -768,7 +768,7 @@ static int security_compute_validatetrans(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; if (!user) tclass = unmap_class(&state->ss->policy->map, orig_tclass); @@ -869,7 +869,7 @@ int security_bounded_transition(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; rc = -EINVAL; old_entry = sidtab_search_entry(sidtab, old_sid); @@ -1026,7 +1026,7 @@ void security_compute_xperms_decision(struct selinux_state *state, goto allow; policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1111,7 +1111,7 @@ void security_compute_av(struct selinux_state *state, goto allow; policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1165,7 +1165,7 @@ void security_compute_av_user(struct selinux_state *state, goto allow; policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1288,7 +1288,7 @@ int security_sidtab_hash_stats(struct selinux_state *state, char *page) } read_lock(&state->ss->policy_rwlock); - rc = sidtab_hash_stats(&state->ss->policy->sidtab, page); + rc = sidtab_hash_stats(state->ss->policy->sidtab, page); read_unlock(&state->ss->policy_rwlock); return rc; @@ -1337,7 +1337,7 @@ static int security_sid_to_context_core(struct selinux_state *state, } read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; if (force) entry = sidtab_search_entry_force(sidtab, sid); @@ -1531,7 +1531,7 @@ static int security_context_to_sid_core(struct selinux_state *state, } read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; rc = string_to_context_struct(policydb, sidtab, scontext2, &context, def_sid); if (rc == -EINVAL && force) { @@ -1619,7 +1619,7 @@ static int compute_sid_handle_invalid_context( struct context *newcontext) { struct policydb *policydb = &state->ss->policy->policydb; - struct sidtab *sidtab = &state->ss->policy->sidtab; + struct sidtab *sidtab = state->ss->policy->sidtab; char *s = NULL, *t = NULL, *n = NULL; u32 slen, tlen, nlen; struct audit_buffer *ab; @@ -1724,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state, } policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; sentry = sidtab_search_entry(sidtab, ssid); if (!sentry) { @@ -2128,7 +2128,8 @@ static void selinux_policy_free(struct selinux_policy *policy) return; policydb_destroy(&policy->policydb); - sidtab_destroy(&policy->sidtab); + sidtab_destroy(policy->sidtab); + kfree(policy->sidtab); kfree(policy->map.mapping); kfree(policy); } @@ -2136,11 +2137,21 @@ static void selinux_policy_free(struct selinux_policy *policy) void selinux_policy_cancel(struct selinux_state *state, struct selinux_policy *policy) { - - sidtab_cancel_convert(&state->ss->policy->sidtab); + sidtab_cancel_convert(state->ss->policy->sidtab); selinux_policy_free(policy); } +static void selinux_notify_policy_change(struct selinux_state *state, + u32 seqno) +{ + /* Flush external caches and notify userspace of policy load */ + avc_ss_reset(state->avc, seqno); + selnl_notify_policyload(seqno); + selinux_status_update_policyload(state, seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); +} + void selinux_policy_commit(struct selinux_state *state, struct selinux_policy *newpolicy) { @@ -2185,12 +2196,8 @@ void selinux_policy_commit(struct selinux_state *state, /* Free the old policy */ selinux_policy_free(oldpolicy); - /* Flush external caches and notify userspace of policy load */ - avc_ss_reset(state->avc, seqno); - selnl_notify_policyload(seqno); - selinux_status_update_policyload(state, seqno); - selinux_netlbl_cache_invalidate(); - selinux_xfrm_notify_policyload(); + /* Notify others of the policy change */ + selinux_notify_policy_change(state, seqno); } /** @@ -2216,6 +2223,10 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, if (!newpolicy) return -ENOMEM; + newpolicy->sidtab = kzalloc(sizeof(*newpolicy->sidtab), GFP_KERNEL); + if (!newpolicy) + goto err; + rc = policydb_read(&newpolicy->policydb, fp); if (rc) goto err; @@ -2226,7 +2237,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, if (rc) goto err; - rc = policydb_load_isids(&newpolicy->policydb, &newpolicy->sidtab); + rc = policydb_load_isids(&newpolicy->policydb, newpolicy->sidtab); if (rc) { pr_err("SELinux: unable to load the initial SIDs\n"); goto err; @@ -2261,9 +2272,9 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, convert_params.func = convert_context; convert_params.args = &args; - convert_params.target = &newpolicy->sidtab; + convert_params.target = newpolicy->sidtab; - rc = sidtab_convert(&state->ss->policy->sidtab, &convert_params); + rc = sidtab_convert(state->ss->policy->sidtab, &convert_params); if (rc) { pr_err("SELinux: unable to convert the internal" " representation of contexts in the new SID" @@ -2306,7 +2317,7 @@ int security_port_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; c = policydb->ocontexts[OCON_PORT]; while (c) { @@ -2351,7 +2362,7 @@ int security_ib_pkey_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; c = policydb->ocontexts[OCON_IBPKEY]; while (c) { @@ -2397,7 +2408,7 @@ int security_ib_endport_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; c = policydb->ocontexts[OCON_IBENDPORT]; while (c) { @@ -2442,7 +2453,7 @@ int security_netif_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; c = policydb->ocontexts[OCON_NETIF]; while (c) { @@ -2505,7 +2516,7 @@ int security_node_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; switch (domain) { case AF_INET: { @@ -2605,7 +2616,7 @@ int security_get_user_sids(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; context_init(&usercon); @@ -2705,7 +2716,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy, u32 *sid) { struct policydb *policydb = &policy->policydb; - struct sidtab *sidtab = &policy->sidtab; + struct sidtab *sidtab = policy->sidtab; int len; u16 sclass; struct genfs *genfs; @@ -2802,7 +2813,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; c = policydb->ocontexts[OCON_FSUSE]; while (c) { @@ -2891,49 +2902,77 @@ int security_get_bools(struct selinux_policy *policy, int security_set_bools(struct selinux_state *state, u32 len, int *values) { - struct policydb *policydb; + struct selinux_policy *newpolicy, *oldpolicy; int rc; - u32 i, lenp, seqno = 0; + u32 i, seqno = 0; - write_lock_irq(&state->ss->policy_rwlock); + /* + * NOTE: We do not need to take the policy read-lock + * around the code below because other policy-modifying + * operations are already excluded by selinuxfs via + * fsi->mutex. + */ - policydb = &state->ss->policy->policydb; + /* Consistency check on number of booleans, should never fail */ + if (WARN_ON(len != state->ss->policy->policydb.p_bools.nprim)) + return -EINVAL; - rc = -EFAULT; - lenp = policydb->p_bools.nprim; - if (len != lenp) - goto out; + newpolicy = kmemdup(state->ss->policy, sizeof(*newpolicy), + GFP_KERNEL); + if (!newpolicy) + return -ENOMEM; + oldpolicy = state->ss->policy; + + /* + * Deep copy only the parts of the policydb that might be + * modified as a result of changing booleans. + */ + rc = cond_policydb_dup(&newpolicy->policydb, &oldpolicy->policydb); + if (rc) { + kfree(newpolicy); + return -ENOMEM; + } + + /* Update the boolean states in the copy */ for (i = 0; i < len; i++) { - if (!!values[i] != policydb->bool_val_to_struct[i]->state) { + int new_state = !!values[i]; + int old_state = newpolicy->policydb.bool_val_to_struct[i]->state; + + if (new_state != old_state) { audit_log(audit_context(), GFP_ATOMIC, AUDIT_MAC_CONFIG_CHANGE, "bool=%s val=%d old_val=%d auid=%u ses=%u", - sym_name(policydb, SYM_BOOLS, i), - !!values[i], - policydb->bool_val_to_struct[i]->state, + sym_name(&newpolicy->policydb, SYM_BOOLS, i), + new_state, + old_state, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); + newpolicy->policydb.bool_val_to_struct[i]->state = new_state; } - if (values[i]) - policydb->bool_val_to_struct[i]->state = 1; - else - policydb->bool_val_to_struct[i]->state = 0; } - evaluate_cond_nodes(policydb); + /* Re-evaluate the conditional rules in the copy */ + evaluate_cond_nodes(&newpolicy->policydb); + /* Install the new policy */ + write_lock_irq(&state->ss->policy_rwlock); + state->ss->policy = newpolicy; seqno = ++state->ss->latest_granting; - rc = 0; -out: write_unlock_irq(&state->ss->policy_rwlock); - if (!rc) { - avc_ss_reset(state->avc, seqno); - selnl_notify_policyload(seqno); - selinux_status_update_policyload(state, seqno); - selinux_xfrm_notify_policyload(); - } - return rc; + + /* + * Free the conditional portions of the old policydb + * that were copied for the new policy. + */ + cond_policydb_destroy_dup(&oldpolicy->policydb); + + /* Free the old policy structure but not what it references. */ + kfree(oldpolicy); + + /* Notify others of the policy change */ + selinux_notify_policy_change(state, seqno); + return 0; } int security_get_bool_value(struct selinux_state *state, @@ -3015,7 +3054,7 @@ int security_sid_mls_copy(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; if (!policydb->mls_enabled) { *new_sid = sid; @@ -3125,7 +3164,7 @@ int security_net_peersid_resolve(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; /* * We don't need to check initialized here since the only way both @@ -3467,7 +3506,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) goto out; } - ctxt = sidtab_search(&state->ss->policy->sidtab, sid); + ctxt = sidtab_search(state->ss->policy->sidtab, sid); if (unlikely(!ctxt)) { WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", sid); @@ -3643,7 +3682,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, read_lock(&state->ss->policy_rwlock); policydb = &state->ss->policy->policydb; - sidtab = &state->ss->policy->sidtab; + sidtab = state->ss->policy->sidtab; if (secattr->flags & NETLBL_SECATTR_CACHE) *sid = *(u32 *)secattr->cache->data; @@ -3713,7 +3752,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, policydb = &state->ss->policy->policydb; rc = -ENOENT; - ctx = sidtab_search(&state->ss->policy->sidtab, sid); + ctx = sidtab_search(state->ss->policy->sidtab, sid); if (ctx == NULL) goto out; diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index c36933c1c363..06931e34cb24 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h @@ -23,7 +23,7 @@ struct selinux_map { }; struct selinux_policy { - struct sidtab sidtab; + struct sidtab *sidtab; struct policydb policydb; struct selinux_map map; };