Commit 1b8b31a2 authored by Stephen Smalley's avatar Stephen Smalley Committed by Paul Moore

selinux: convert policy read-write lock to RCU

Convert the policy read-write lock to RCU.  This is significantly
simplified by the earlier work to encapsulate the policy data
structures and refactor the policy load and boolean setting logic.
Move the latest_granting sequence number into the selinux_policy
structure so that it can be updated atomically with the policy.
Since removing the policy rwlock and moving latest_granting reduces
the selinux_ss structure to nothing more than a wrapper around the
selinux_policy pointer, get rid of the extra layer of indirection.

At present this change merely passes a hardcoded 1 to
rcu_dereference_check() in the cases where we know we do not need to
take rcu_read_lock(), with the preceding comment explaining why.
Alternatively we could pass fsi->mutex down from selinuxfs and
apply a lockdep check on it instead.

Based in part on earlier attempts to convert the policy rwlock
to RCU by Kaigai Kohei [1] and by Peter Enderborg [2].

[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: default avatarStephen Smalley <stephen.smalley.work@gmail.com>
Reviewed-by: default avatarOndrej Mosnacek <omosnace@redhat.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent c76a2f9e
...@@ -7235,7 +7235,6 @@ static __init int selinux_init(void) ...@@ -7235,7 +7235,6 @@ static __init int selinux_init(void)
memset(&selinux_state, 0, sizeof(selinux_state)); memset(&selinux_state, 0, sizeof(selinux_state));
enforcing_set(&selinux_state, selinux_enforcing_boot); enforcing_set(&selinux_state, selinux_enforcing_boot);
selinux_state.checkreqprot = selinux_checkreqprot_boot; selinux_state.checkreqprot = selinux_checkreqprot_boot;
selinux_ss_init(&selinux_state.ss);
selinux_avc_init(&selinux_state.avc); selinux_avc_init(&selinux_state.avc);
mutex_init(&selinux_state.status_lock); mutex_init(&selinux_state.status_lock);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/dcache.h> #include <linux/dcache.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "flask.h" #include "flask.h"
...@@ -84,7 +85,6 @@ extern int selinux_enabled_boot; ...@@ -84,7 +85,6 @@ extern int selinux_enabled_boot;
#define POLICYDB_BOUNDS_MAXDEPTH 4 #define POLICYDB_BOUNDS_MAXDEPTH 4
struct selinux_avc; struct selinux_avc;
struct selinux_ss;
struct selinux_policy; struct selinux_policy;
struct selinux_state { struct selinux_state {
...@@ -102,10 +102,9 @@ struct selinux_state { ...@@ -102,10 +102,9 @@ struct selinux_state {
struct mutex status_lock; struct mutex status_lock;
struct selinux_avc *avc; struct selinux_avc *avc;
struct selinux_ss *ss; struct selinux_policy __rcu *policy;
} __randomize_layout; } __randomize_layout;
void selinux_ss_init(struct selinux_ss **ss);
void selinux_avc_init(struct selinux_avc **avc); void selinux_avc_init(struct selinux_avc **avc);
extern struct selinux_state selinux_state; extern struct selinux_state selinux_state;
......
...@@ -66,14 +66,6 @@ ...@@ -66,14 +66,6 @@
#include "audit.h" #include "audit.h"
#include "policycap_names.h" #include "policycap_names.h"
static struct selinux_ss selinux_ss;
void selinux_ss_init(struct selinux_ss **ss)
{
rwlock_init(&selinux_ss.policy_rwlock);
*ss = &selinux_ss;
}
/* Forward declaration. */ /* Forward declaration. */
static int context_struct_to_string(struct policydb *policydb, static int context_struct_to_string(struct policydb *policydb,
struct context *context, struct context *context,
...@@ -239,13 +231,15 @@ static void map_decision(struct selinux_map *map, ...@@ -239,13 +231,15 @@ static void map_decision(struct selinux_map *map,
int security_mls_enabled(struct selinux_state *state) int security_mls_enabled(struct selinux_state *state)
{ {
int mls_enabled; int mls_enabled;
struct selinux_policy *policy;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
mls_enabled = state->ss->policy->policydb.mls_enabled; policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); mls_enabled = policy->policydb.mls_enabled;
rcu_read_unlock();
return mls_enabled; return mls_enabled;
} }
...@@ -717,13 +711,14 @@ static void context_struct_compute_av(struct policydb *policydb, ...@@ -717,13 +711,14 @@ static void context_struct_compute_av(struct policydb *policydb,
} }
static int security_validtrans_handle_fail(struct selinux_state *state, static int security_validtrans_handle_fail(struct selinux_state *state,
struct selinux_policy *policy,
struct sidtab_entry *oentry, struct sidtab_entry *oentry,
struct sidtab_entry *nentry, struct sidtab_entry *nentry,
struct sidtab_entry *tentry, struct sidtab_entry *tentry,
u16 tclass) u16 tclass)
{ {
struct policydb *p = &state->ss->policy->policydb; struct policydb *p = &policy->policydb;
struct sidtab *sidtab = state->ss->policy->sidtab; struct sidtab *sidtab = policy->sidtab;
char *o = NULL, *n = NULL, *t = NULL; char *o = NULL, *n = NULL, *t = NULL;
u32 olen, nlen, tlen; u32 olen, nlen, tlen;
...@@ -751,6 +746,7 @@ static int security_compute_validatetrans(struct selinux_state *state, ...@@ -751,6 +746,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
u32 oldsid, u32 newsid, u32 tasksid, u32 oldsid, u32 newsid, u32 tasksid,
u16 orig_tclass, bool user) u16 orig_tclass, bool user)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct sidtab_entry *oentry; struct sidtab_entry *oentry;
...@@ -765,13 +761,14 @@ static int security_compute_validatetrans(struct selinux_state *state, ...@@ -765,13 +761,14 @@ static int security_compute_validatetrans(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policydb = &state->ss->policy->policydb; policy = rcu_dereference(state->policy);
sidtab = state->ss->policy->sidtab; policydb = &policy->policydb;
sidtab = policy->sidtab;
if (!user) if (!user)
tclass = unmap_class(&state->ss->policy->map, orig_tclass); tclass = unmap_class(&policy->map, orig_tclass);
else else
tclass = orig_tclass; tclass = orig_tclass;
...@@ -814,6 +811,7 @@ static int security_compute_validatetrans(struct selinux_state *state, ...@@ -814,6 +811,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
rc = -EPERM; rc = -EPERM;
else else
rc = security_validtrans_handle_fail(state, rc = security_validtrans_handle_fail(state,
policy,
oentry, oentry,
nentry, nentry,
tentry, tentry,
...@@ -824,7 +822,7 @@ static int security_compute_validatetrans(struct selinux_state *state, ...@@ -824,7 +822,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -856,6 +854,7 @@ int security_validate_transition(struct selinux_state *state, ...@@ -856,6 +854,7 @@ int security_validate_transition(struct selinux_state *state,
int security_bounded_transition(struct selinux_state *state, int security_bounded_transition(struct selinux_state *state,
u32 old_sid, u32 new_sid) u32 old_sid, u32 new_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct sidtab_entry *old_entry, *new_entry; struct sidtab_entry *old_entry, *new_entry;
...@@ -866,10 +865,10 @@ int security_bounded_transition(struct selinux_state *state, ...@@ -866,10 +865,10 @@ int security_bounded_transition(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
rc = -EINVAL; rc = -EINVAL;
old_entry = sidtab_search_entry(sidtab, old_sid); old_entry = sidtab_search_entry(sidtab, old_sid);
...@@ -930,17 +929,20 @@ int security_bounded_transition(struct selinux_state *state, ...@@ -930,17 +929,20 @@ int security_bounded_transition(struct selinux_state *state,
kfree(old_name); kfree(old_name);
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
static void avd_init(struct selinux_state *state, struct av_decision *avd) static void avd_init(struct selinux_policy *policy, struct av_decision *avd)
{ {
avd->allowed = 0; avd->allowed = 0;
avd->auditallow = 0; avd->auditallow = 0;
avd->auditdeny = 0xffffffff; avd->auditdeny = 0xffffffff;
avd->seqno = state->ss->latest_granting; if (policy)
avd->seqno = policy->latest_granting;
else
avd->seqno = 0;
avd->flags = 0; avd->flags = 0;
} }
...@@ -1005,6 +1007,7 @@ void security_compute_xperms_decision(struct selinux_state *state, ...@@ -1005,6 +1007,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
u8 driver, u8 driver,
struct extended_perms_decision *xpermd) struct extended_perms_decision *xpermd)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
u16 tclass; u16 tclass;
...@@ -1021,12 +1024,13 @@ void security_compute_xperms_decision(struct selinux_state *state, ...@@ -1021,12 +1024,13 @@ void security_compute_xperms_decision(struct selinux_state *state,
memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p)); memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p));
memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p)); memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p));
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
if (!selinux_initialized(state)) if (!selinux_initialized(state))
goto allow; goto allow;
policydb = &state->ss->policy->policydb; policy = rcu_dereference(state->policy);
sidtab = state->ss->policy->sidtab; policydb = &policy->policydb;
sidtab = policy->sidtab;
scontext = sidtab_search(sidtab, ssid); scontext = sidtab_search(sidtab, ssid);
if (!scontext) { if (!scontext) {
...@@ -1042,7 +1046,7 @@ void security_compute_xperms_decision(struct selinux_state *state, ...@@ -1042,7 +1046,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
goto out; goto out;
} }
tclass = unmap_class(&state->ss->policy->map, orig_tclass); tclass = unmap_class(&policy->map, orig_tclass);
if (unlikely(orig_tclass && !tclass)) { if (unlikely(orig_tclass && !tclass)) {
if (policydb->allow_unknown) if (policydb->allow_unknown)
goto allow; goto allow;
...@@ -1074,7 +1078,7 @@ void security_compute_xperms_decision(struct selinux_state *state, ...@@ -1074,7 +1078,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
} }
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return; return;
allow: allow:
memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p)); memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p));
...@@ -1099,19 +1103,21 @@ void security_compute_av(struct selinux_state *state, ...@@ -1099,19 +1103,21 @@ void security_compute_av(struct selinux_state *state,
struct av_decision *avd, struct av_decision *avd,
struct extended_perms *xperms) struct extended_perms *xperms)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
u16 tclass; u16 tclass;
struct context *scontext = NULL, *tcontext = NULL; struct context *scontext = NULL, *tcontext = NULL;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
avd_init(state, avd); policy = rcu_dereference(state->policy);
avd_init(policy, avd);
xperms->len = 0; xperms->len = 0;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
goto allow; goto allow;
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
scontext = sidtab_search(sidtab, ssid); scontext = sidtab_search(sidtab, ssid);
if (!scontext) { if (!scontext) {
...@@ -1131,7 +1137,7 @@ void security_compute_av(struct selinux_state *state, ...@@ -1131,7 +1137,7 @@ void security_compute_av(struct selinux_state *state,
goto out; goto out;
} }
tclass = unmap_class(&state->ss->policy->map, orig_tclass); tclass = unmap_class(&policy->map, orig_tclass);
if (unlikely(orig_tclass && !tclass)) { if (unlikely(orig_tclass && !tclass)) {
if (policydb->allow_unknown) if (policydb->allow_unknown)
goto allow; goto allow;
...@@ -1139,10 +1145,10 @@ void security_compute_av(struct selinux_state *state, ...@@ -1139,10 +1145,10 @@ void security_compute_av(struct selinux_state *state,
} }
context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
xperms); xperms);
map_decision(&state->ss->policy->map, orig_tclass, avd, map_decision(&policy->map, orig_tclass, avd,
policydb->allow_unknown); policydb->allow_unknown);
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return; return;
allow: allow:
avd->allowed = 0xffffffff; avd->allowed = 0xffffffff;
...@@ -1155,17 +1161,19 @@ void security_compute_av_user(struct selinux_state *state, ...@@ -1155,17 +1161,19 @@ void security_compute_av_user(struct selinux_state *state,
u16 tclass, u16 tclass,
struct av_decision *avd) struct av_decision *avd)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct context *scontext = NULL, *tcontext = NULL; struct context *scontext = NULL, *tcontext = NULL;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
avd_init(state, avd); policy = rcu_dereference(state->policy);
avd_init(policy, avd);
if (!selinux_initialized(state)) if (!selinux_initialized(state))
goto allow; goto allow;
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
scontext = sidtab_search(sidtab, ssid); scontext = sidtab_search(sidtab, ssid);
if (!scontext) { if (!scontext) {
...@@ -1194,7 +1202,7 @@ void security_compute_av_user(struct selinux_state *state, ...@@ -1194,7 +1202,7 @@ void security_compute_av_user(struct selinux_state *state,
context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
NULL); NULL);
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return; return;
allow: allow:
avd->allowed = 0xffffffff; avd->allowed = 0xffffffff;
...@@ -1279,6 +1287,7 @@ static int sidtab_entry_to_string(struct policydb *p, ...@@ -1279,6 +1287,7 @@ static int sidtab_entry_to_string(struct policydb *p,
int security_sidtab_hash_stats(struct selinux_state *state, char *page) int security_sidtab_hash_stats(struct selinux_state *state, char *page)
{ {
struct selinux_policy *policy;
int rc; int rc;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
...@@ -1287,9 +1296,10 @@ int security_sidtab_hash_stats(struct selinux_state *state, char *page) ...@@ -1287,9 +1296,10 @@ int security_sidtab_hash_stats(struct selinux_state *state, char *page)
return -EINVAL; return -EINVAL;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
rc = sidtab_hash_stats(state->ss->policy->sidtab, page); policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); rc = sidtab_hash_stats(policy->sidtab, page);
rcu_read_unlock();
return rc; return rc;
} }
...@@ -1306,6 +1316,7 @@ static int security_sid_to_context_core(struct selinux_state *state, ...@@ -1306,6 +1316,7 @@ static int security_sid_to_context_core(struct selinux_state *state,
u32 *scontext_len, int force, u32 *scontext_len, int force,
int only_invalid) int only_invalid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct sidtab_entry *entry; struct sidtab_entry *entry;
...@@ -1335,9 +1346,10 @@ static int security_sid_to_context_core(struct selinux_state *state, ...@@ -1335,9 +1346,10 @@ static int security_sid_to_context_core(struct selinux_state *state,
"load_policy on unknown SID %d\n", __func__, sid); "load_policy on unknown SID %d\n", __func__, sid);
return -EINVAL; return -EINVAL;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policydb = &state->ss->policy->policydb; policy = rcu_dereference(state->policy);
sidtab = state->ss->policy->sidtab; policydb = &policy->policydb;
sidtab = policy->sidtab;
if (force) if (force)
entry = sidtab_search_entry_force(sidtab, sid); entry = sidtab_search_entry_force(sidtab, sid);
...@@ -1356,7 +1368,7 @@ static int security_sid_to_context_core(struct selinux_state *state, ...@@ -1356,7 +1368,7 @@ static int security_sid_to_context_core(struct selinux_state *state,
scontext_len); scontext_len);
out_unlock: out_unlock:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -1491,6 +1503,7 @@ static int security_context_to_sid_core(struct selinux_state *state, ...@@ -1491,6 +1503,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
u32 *sid, u32 def_sid, gfp_t gfp_flags, u32 *sid, u32 def_sid, gfp_t gfp_flags,
int force) int force)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
char *scontext2, *str = NULL; char *scontext2, *str = NULL;
...@@ -1529,9 +1542,10 @@ static int security_context_to_sid_core(struct selinux_state *state, ...@@ -1529,9 +1542,10 @@ static int security_context_to_sid_core(struct selinux_state *state,
if (!str) if (!str)
goto out; goto out;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policydb = &state->ss->policy->policydb; policy = rcu_dereference(state->policy);
sidtab = state->ss->policy->sidtab; policydb = &policy->policydb;
sidtab = policy->sidtab;
rc = string_to_context_struct(policydb, sidtab, scontext2, rc = string_to_context_struct(policydb, sidtab, scontext2,
&context, def_sid); &context, def_sid);
if (rc == -EINVAL && force) { if (rc == -EINVAL && force) {
...@@ -1543,7 +1557,7 @@ static int security_context_to_sid_core(struct selinux_state *state, ...@@ -1543,7 +1557,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
rc = sidtab_context_to_sid(sidtab, &context, sid); rc = sidtab_context_to_sid(sidtab, &context, sid);
context_destroy(&context); context_destroy(&context);
out_unlock: out_unlock:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
out: out:
kfree(scontext2); kfree(scontext2);
kfree(str); kfree(str);
...@@ -1613,13 +1627,14 @@ int security_context_to_sid_force(struct selinux_state *state, ...@@ -1613,13 +1627,14 @@ int security_context_to_sid_force(struct selinux_state *state,
static int compute_sid_handle_invalid_context( static int compute_sid_handle_invalid_context(
struct selinux_state *state, struct selinux_state *state,
struct selinux_policy *policy,
struct sidtab_entry *sentry, struct sidtab_entry *sentry,
struct sidtab_entry *tentry, struct sidtab_entry *tentry,
u16 tclass, u16 tclass,
struct context *newcontext) struct context *newcontext)
{ {
struct policydb *policydb = &state->ss->policy->policydb; struct policydb *policydb = &policy->policydb;
struct sidtab *sidtab = state->ss->policy->sidtab; struct sidtab *sidtab = policy->sidtab;
char *s = NULL, *t = NULL, *n = NULL; char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen; u32 slen, tlen, nlen;
struct audit_buffer *ab; struct audit_buffer *ab;
...@@ -1686,6 +1701,7 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1686,6 +1701,7 @@ static int security_compute_sid(struct selinux_state *state,
u32 *out_sid, u32 *out_sid,
bool kern) bool kern)
{ {
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 = NULL;
...@@ -1712,19 +1728,21 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1712,19 +1728,21 @@ static int security_compute_sid(struct selinux_state *state,
context_init(&newcontext); context_init(&newcontext);
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
if (kern) { if (kern) {
tclass = unmap_class(&state->ss->policy->map, orig_tclass); tclass = unmap_class(&policy->map, orig_tclass);
sock = security_is_socket_class(orig_tclass); sock = security_is_socket_class(orig_tclass);
} else { } else {
tclass = orig_tclass; tclass = orig_tclass;
sock = security_is_socket_class(map_class(&state->ss->policy->map, sock = security_is_socket_class(map_class(&policy->map,
tclass)); tclass));
} }
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
sentry = sidtab_search_entry(sidtab, ssid); sentry = sidtab_search_entry(sidtab, ssid);
if (!sentry) { if (!sentry) {
...@@ -1844,15 +1862,16 @@ static int security_compute_sid(struct selinux_state *state, ...@@ -1844,15 +1862,16 @@ static int security_compute_sid(struct selinux_state *state,
/* Check the validity of the context. */ /* Check the validity of the context. */
if (!policydb_context_isvalid(policydb, &newcontext)) { if (!policydb_context_isvalid(policydb, &newcontext)) {
rc = compute_sid_handle_invalid_context(state, sentry, tentry, rc = compute_sid_handle_invalid_context(state, policy, sentry,
tclass, &newcontext); tentry, tclass,
&newcontext);
if (rc) if (rc)
goto out_unlock; goto out_unlock;
} }
/* 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);
out_unlock: out_unlock:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
context_destroy(&newcontext); context_destroy(&newcontext);
out: out:
return rc; return rc;
...@@ -1939,9 +1958,9 @@ int security_change_sid(struct selinux_state *state, ...@@ -1939,9 +1958,9 @@ int security_change_sid(struct selinux_state *state,
static inline int convert_context_handle_invalid_context( static inline int convert_context_handle_invalid_context(
struct selinux_state *state, struct selinux_state *state,
struct policydb *policydb,
struct context *context) struct context *context)
{ {
struct policydb *policydb = &state->ss->policy->policydb;
char *s; char *s;
u32 len; u32 len;
...@@ -2073,7 +2092,9 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) ...@@ -2073,7 +2092,9 @@ static int convert_context(struct context *oldc, struct context *newc, void *p)
/* Check the validity of the new context. */ /* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, newc)) { if (!policydb_context_isvalid(args->newp, newc)) {
rc = convert_context_handle_invalid_context(args->state, oldc); rc = convert_context_handle_invalid_context(args->state,
args->oldp,
oldc);
if (rc) if (rc)
goto bad; goto bad;
} }
...@@ -2092,15 +2113,14 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) ...@@ -2092,15 +2113,14 @@ static int convert_context(struct context *oldc, struct context *newc, void *p)
return 0; return 0;
} }
static void security_load_policycaps(struct selinux_state *state) static void security_load_policycaps(struct selinux_state *state,
struct selinux_policy *policy)
{ {
struct policydb *p; struct policydb *p;
unsigned int i; unsigned int i;
struct ebitmap_node *node; struct ebitmap_node *node;
read_lock(&state->ss->policy_rwlock); p = &policy->policydb;
p = &state->ss->policy->policydb;
for (i = 0; i < ARRAY_SIZE(state->policycap); i++) for (i = 0; i < ARRAY_SIZE(state->policycap); i++)
state->policycap[i] = ebitmap_get_bit(&p->policycaps, i); state->policycap[i] = ebitmap_get_bit(&p->policycaps, i);
...@@ -2115,12 +2135,10 @@ static void security_load_policycaps(struct selinux_state *state) ...@@ -2115,12 +2135,10 @@ static void security_load_policycaps(struct selinux_state *state)
pr_info("SELinux: unknown policy capability %u\n", pr_info("SELinux: unknown policy capability %u\n",
i); i);
} }
read_unlock(&state->ss->policy_rwlock);
} }
static int security_preserve_bools(struct selinux_state *state, static int security_preserve_bools(struct selinux_policy *oldpolicy,
struct policydb *newpolicydb); struct selinux_policy *newpolicy);
static void selinux_policy_free(struct selinux_policy *policy) static void selinux_policy_free(struct selinux_policy *policy)
{ {
...@@ -2134,10 +2152,26 @@ static void selinux_policy_free(struct selinux_policy *policy) ...@@ -2134,10 +2152,26 @@ static void selinux_policy_free(struct selinux_policy *policy)
kfree(policy); kfree(policy);
} }
static void selinux_policy_cond_free(struct selinux_policy *policy)
{
cond_policydb_destroy_dup(&policy->policydb);
kfree(policy);
}
void selinux_policy_cancel(struct selinux_state *state, void selinux_policy_cancel(struct selinux_state *state,
struct selinux_policy *policy) struct selinux_policy *policy)
{ {
sidtab_cancel_convert(state->ss->policy->sidtab); struct selinux_policy *oldpolicy;
/*
* NOTE: We do not need to take the rcu read lock
* around the code below because other policy-modifying
* operations are already excluded by selinuxfs via
* fsi->mutex.
*/
oldpolicy = rcu_dereference_check(state->policy, 1);
sidtab_cancel_convert(oldpolicy->sidtab);
selinux_policy_free(policy); selinux_policy_free(policy);
} }
...@@ -2159,14 +2193,14 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2159,14 +2193,14 @@ void selinux_policy_commit(struct selinux_state *state,
u32 seqno; u32 seqno;
/* /*
* NOTE: We do not need to take the policy read-lock * NOTE: We do not need to take the rcu read lock
* around the code below because other policy-modifying * around the code below because other policy-modifying
* operations are already excluded by selinuxfs via * operations are already excluded by selinuxfs via
* fsi->mutex. * fsi->mutex.
*/ */
oldpolicy = rcu_dereference_check(state->policy, 1);
/* If switching between different policy types, log MLS status */ /* If switching between different policy types, log MLS status */
oldpolicy = state->ss->policy;
if (oldpolicy) { if (oldpolicy) {
if (oldpolicy->policydb.mls_enabled && !newpolicy->policydb.mls_enabled) if (oldpolicy->policydb.mls_enabled && !newpolicy->policydb.mls_enabled)
pr_info("SELinux: Disabling MLS support...\n"); pr_info("SELinux: Disabling MLS support...\n");
...@@ -2174,14 +2208,18 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2174,14 +2208,18 @@ void selinux_policy_commit(struct selinux_state *state,
pr_info("SELinux: Enabling MLS support...\n"); pr_info("SELinux: Enabling MLS support...\n");
} }
/* Set latest granting seqno for new policy. */
if (oldpolicy)
newpolicy->latest_granting = oldpolicy->latest_granting + 1;
else
newpolicy->latest_granting = 1;
seqno = newpolicy->latest_granting;
/* Install the new policy. */ /* Install the new policy. */
write_lock_irq(&state->ss->policy_rwlock); rcu_assign_pointer(state->policy, newpolicy);
state->ss->policy = newpolicy;
seqno = ++state->ss->latest_granting;
write_unlock_irq(&state->ss->policy_rwlock);
/* Load the policycaps from the new policy */ /* Load the policycaps from the new policy */
security_load_policycaps(state); security_load_policycaps(state, newpolicy);
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
/* /*
...@@ -2194,6 +2232,7 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2194,6 +2232,7 @@ void selinux_policy_commit(struct selinux_state *state,
} }
/* Free the old policy */ /* Free the old policy */
synchronize_rcu();
selinux_policy_free(oldpolicy); selinux_policy_free(oldpolicy);
/* Notify others of the policy change */ /* Notify others of the policy change */
...@@ -2213,7 +2252,7 @@ void selinux_policy_commit(struct selinux_state *state, ...@@ -2213,7 +2252,7 @@ void selinux_policy_commit(struct selinux_state *state,
int security_load_policy(struct selinux_state *state, void *data, size_t len, int security_load_policy(struct selinux_state *state, void *data, size_t len,
struct selinux_policy **newpolicyp) struct selinux_policy **newpolicyp)
{ {
struct selinux_policy *newpolicy; struct selinux_policy *newpolicy, *oldpolicy;
struct sidtab_convert_params convert_params; struct sidtab_convert_params convert_params;
struct convert_context_args args; struct convert_context_args args;
int rc = 0; int rc = 0;
...@@ -2250,8 +2289,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, ...@@ -2250,8 +2289,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
return 0; return 0;
} }
/*
* NOTE: We do not need to take the rcu read lock
* around the code below because other policy-modifying
* operations are already excluded by selinuxfs via
* fsi->mutex.
*/
oldpolicy = rcu_dereference_check(state->policy, 1);
/* Preserve active boolean values from the old policy */ /* Preserve active boolean values from the old policy */
rc = security_preserve_bools(state, &newpolicy->policydb); rc = security_preserve_bools(oldpolicy, newpolicy);
if (rc) { if (rc) {
pr_err("SELinux: unable to preserve booleans\n"); pr_err("SELinux: unable to preserve booleans\n");
goto err; goto err;
...@@ -2260,21 +2307,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, ...@@ -2260,21 +2307,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
/* /*
* Convert the internal representations of contexts * Convert the internal representations of contexts
* in the new SID table. * in the new SID table.
*
* 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.
*/ */
args.state = state; args.state = state;
args.oldp = &state->ss->policy->policydb; args.oldp = &oldpolicy->policydb;
args.newp = &newpolicy->policydb; args.newp = &newpolicy->policydb;
convert_params.func = convert_context; convert_params.func = convert_context;
convert_params.args = &args; 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(oldpolicy->sidtab, &convert_params);
if (rc) { if (rc) {
pr_err("SELinux: unable to convert the internal" pr_err("SELinux: unable to convert the internal"
" representation of contexts in the new SID" " representation of contexts in the new SID"
...@@ -2291,14 +2333,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, ...@@ -2291,14 +2333,16 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
size_t security_policydb_len(struct selinux_state *state) size_t security_policydb_len(struct selinux_state *state)
{ {
struct selinux_policy *policy;
size_t len; size_t len;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
len = state->ss->policy->policydb.len; policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); len = policy->policydb.len;
rcu_read_unlock();
return len; return len;
} }
...@@ -2312,6 +2356,7 @@ size_t security_policydb_len(struct selinux_state *state) ...@@ -2312,6 +2356,7 @@ size_t security_policydb_len(struct selinux_state *state)
int security_port_sid(struct selinux_state *state, int security_port_sid(struct selinux_state *state,
u8 protocol, u16 port, u32 *out_sid) u8 protocol, u16 port, u32 *out_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
...@@ -2322,10 +2367,10 @@ int security_port_sid(struct selinux_state *state, ...@@ -2322,10 +2367,10 @@ int security_port_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
c = policydb->ocontexts[OCON_PORT]; c = policydb->ocontexts[OCON_PORT];
while (c) { while (c) {
...@@ -2349,7 +2394,7 @@ int security_port_sid(struct selinux_state *state, ...@@ -2349,7 +2394,7 @@ int security_port_sid(struct selinux_state *state,
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2362,6 +2407,7 @@ int security_port_sid(struct selinux_state *state, ...@@ -2362,6 +2407,7 @@ int security_port_sid(struct selinux_state *state,
int security_ib_pkey_sid(struct selinux_state *state, int security_ib_pkey_sid(struct selinux_state *state,
u64 subnet_prefix, u16 pkey_num, u32 *out_sid) u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
...@@ -2372,10 +2418,10 @@ int security_ib_pkey_sid(struct selinux_state *state, ...@@ -2372,10 +2418,10 @@ int security_ib_pkey_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
c = policydb->ocontexts[OCON_IBPKEY]; c = policydb->ocontexts[OCON_IBPKEY];
while (c) { while (c) {
...@@ -2400,7 +2446,7 @@ int security_ib_pkey_sid(struct selinux_state *state, ...@@ -2400,7 +2446,7 @@ int security_ib_pkey_sid(struct selinux_state *state,
*out_sid = SECINITSID_UNLABELED; *out_sid = SECINITSID_UNLABELED;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2413,6 +2459,7 @@ int security_ib_pkey_sid(struct selinux_state *state, ...@@ -2413,6 +2459,7 @@ int security_ib_pkey_sid(struct selinux_state *state,
int security_ib_endport_sid(struct selinux_state *state, int security_ib_endport_sid(struct selinux_state *state,
const char *dev_name, u8 port_num, u32 *out_sid) const char *dev_name, u8 port_num, u32 *out_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct ocontext *c; struct ocontext *c;
...@@ -2423,10 +2470,10 @@ int security_ib_endport_sid(struct selinux_state *state, ...@@ -2423,10 +2470,10 @@ int security_ib_endport_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
c = policydb->ocontexts[OCON_IBENDPORT]; c = policydb->ocontexts[OCON_IBENDPORT];
while (c) { while (c) {
...@@ -2451,7 +2498,7 @@ int security_ib_endport_sid(struct selinux_state *state, ...@@ -2451,7 +2498,7 @@ int security_ib_endport_sid(struct selinux_state *state,
*out_sid = SECINITSID_UNLABELED; *out_sid = SECINITSID_UNLABELED;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2463,6 +2510,7 @@ int security_ib_endport_sid(struct selinux_state *state, ...@@ -2463,6 +2510,7 @@ int security_ib_endport_sid(struct selinux_state *state,
int security_netif_sid(struct selinux_state *state, int security_netif_sid(struct selinux_state *state,
char *name, u32 *if_sid) char *name, u32 *if_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc = 0; int rc = 0;
...@@ -2473,10 +2521,10 @@ int security_netif_sid(struct selinux_state *state, ...@@ -2473,10 +2521,10 @@ int security_netif_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
c = policydb->ocontexts[OCON_NETIF]; c = policydb->ocontexts[OCON_NETIF];
while (c) { while (c) {
...@@ -2501,7 +2549,7 @@ int security_netif_sid(struct selinux_state *state, ...@@ -2501,7 +2549,7 @@ int security_netif_sid(struct selinux_state *state,
*if_sid = SECINITSID_NETIF; *if_sid = SECINITSID_NETIF;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2531,6 +2579,7 @@ int security_node_sid(struct selinux_state *state, ...@@ -2531,6 +2579,7 @@ int security_node_sid(struct selinux_state *state,
u32 addrlen, u32 addrlen,
u32 *out_sid) u32 *out_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc; int rc;
...@@ -2541,10 +2590,10 @@ int security_node_sid(struct selinux_state *state, ...@@ -2541,10 +2590,10 @@ int security_node_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
switch (domain) { switch (domain) {
case AF_INET: { case AF_INET: {
...@@ -2599,7 +2648,7 @@ int security_node_sid(struct selinux_state *state, ...@@ -2599,7 +2648,7 @@ int security_node_sid(struct selinux_state *state,
rc = 0; rc = 0;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2625,6 +2674,7 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2625,6 +2674,7 @@ int security_get_user_sids(struct selinux_state *state,
u32 **sids, u32 **sids,
u32 *nel) u32 *nel)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct context *fromcon, usercon; struct context *fromcon, usercon;
...@@ -2641,10 +2691,10 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2641,10 +2691,10 @@ int security_get_user_sids(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
goto out; goto out;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
context_init(&usercon); context_init(&usercon);
...@@ -2695,7 +2745,7 @@ int security_get_user_sids(struct selinux_state *state, ...@@ -2695,7 +2745,7 @@ int security_get_user_sids(struct selinux_state *state,
} }
rc = 0; rc = 0;
out_unlock: out_unlock:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
if (rc || !mynel) { if (rc || !mynel) {
kfree(mysids); kfree(mysids);
goto out; goto out;
...@@ -2806,6 +2856,7 @@ int security_genfs_sid(struct selinux_state *state, ...@@ -2806,6 +2856,7 @@ int security_genfs_sid(struct selinux_state *state,
u16 orig_sclass, u16 orig_sclass,
u32 *sid) u32 *sid)
{ {
struct selinux_policy *policy;
int retval; int retval;
if (!selinux_initialized(state)) { if (!selinux_initialized(state)) {
...@@ -2813,10 +2864,11 @@ int security_genfs_sid(struct selinux_state *state, ...@@ -2813,10 +2864,11 @@ int security_genfs_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
retval = __security_genfs_sid(state->ss->policy, policy = rcu_dereference(state->policy);
retval = __security_genfs_sid(policy,
fstype, path, orig_sclass, sid); fstype, path, orig_sclass, sid);
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return retval; return retval;
} }
...@@ -2836,6 +2888,7 @@ int selinux_policy_genfs_sid(struct selinux_policy *policy, ...@@ -2836,6 +2888,7 @@ int selinux_policy_genfs_sid(struct selinux_policy *policy,
*/ */
int security_fs_use(struct selinux_state *state, struct super_block *sb) int security_fs_use(struct selinux_state *state, struct super_block *sb)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc = 0; int rc = 0;
...@@ -2849,10 +2902,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2849,10 +2902,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
c = policydb->ocontexts[OCON_FSUSE]; c = policydb->ocontexts[OCON_FSUSE];
while (c) { while (c) {
...@@ -2871,7 +2924,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2871,7 +2924,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
} }
sbsec->sid = c->sid[0]; sbsec->sid = c->sid[0];
} else { } else {
rc = __security_genfs_sid(state->ss->policy, fstype, "/", rc = __security_genfs_sid(policy, fstype, "/",
SECCLASS_DIR, &sbsec->sid); SECCLASS_DIR, &sbsec->sid);
if (rc) { if (rc) {
sbsec->behavior = SECURITY_FS_USE_NONE; sbsec->behavior = SECURITY_FS_USE_NONE;
...@@ -2882,7 +2935,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) ...@@ -2882,7 +2935,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -2949,23 +3002,22 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values) ...@@ -2949,23 +3002,22 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
return -EINVAL; return -EINVAL;
/* /*
* NOTE: We do not need to take the policy read-lock * NOTE: We do not need to take the rcu read lock
* around the code below because other policy-modifying * around the code below because other policy-modifying
* operations are already excluded by selinuxfs via * operations are already excluded by selinuxfs via
* fsi->mutex. * fsi->mutex.
*/ */
oldpolicy = rcu_dereference_check(state->policy, 1);
/* Consistency check on number of booleans, should never fail */ /* Consistency check on number of booleans, should never fail */
if (WARN_ON(len != state->ss->policy->policydb.p_bools.nprim)) if (WARN_ON(len != oldpolicy->policydb.p_bools.nprim))
return -EINVAL; return -EINVAL;
newpolicy = kmemdup(state->ss->policy, sizeof(*newpolicy), newpolicy = kmemdup(oldpolicy, sizeof(*newpolicy), GFP_KERNEL);
GFP_KERNEL);
if (!newpolicy) if (!newpolicy)
return -ENOMEM; return -ENOMEM;
oldpolicy = state->ss->policy;
/* /*
* Deep copy only the parts of the policydb that might be * Deep copy only the parts of the policydb that might be
* modified as a result of changing booleans. * modified as a result of changing booleans.
...@@ -2997,20 +3049,20 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values) ...@@ -2997,20 +3049,20 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
/* Re-evaluate the conditional rules in the copy */ /* Re-evaluate the conditional rules in the copy */
evaluate_cond_nodes(&newpolicy->policydb); evaluate_cond_nodes(&newpolicy->policydb);
/* Set latest granting seqno for new policy */
newpolicy->latest_granting = oldpolicy->latest_granting + 1;
seqno = newpolicy->latest_granting;
/* Install the new policy */ /* Install the new policy */
write_lock_irq(&state->ss->policy_rwlock); rcu_assign_pointer(state->policy, newpolicy);
state->ss->policy = newpolicy;
seqno = ++state->ss->latest_granting;
write_unlock_irq(&state->ss->policy_rwlock);
/* /*
* Free the conditional portions of the old policydb * Free the conditional portions of the old policydb
* that were copied for the new policy. * that were copied for the new policy, and the oldpolicy
* structure itself but not what it references.
*/ */
cond_policydb_destroy_dup(&oldpolicy->policydb); synchronize_rcu();
selinux_policy_cond_free(oldpolicy);
/* Free the old policy structure but not what it references. */
kfree(oldpolicy);
/* Notify others of the policy change */ /* Notify others of the policy change */
selinux_notify_policy_change(state, seqno); selinux_notify_policy_change(state, seqno);
...@@ -3020,6 +3072,7 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values) ...@@ -3020,6 +3072,7 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
int security_get_bool_value(struct selinux_state *state, int security_get_bool_value(struct selinux_state *state,
u32 index) u32 index)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
int rc; int rc;
u32 len; u32 len;
...@@ -3027,9 +3080,9 @@ int security_get_bool_value(struct selinux_state *state, ...@@ -3027,9 +3080,9 @@ int security_get_bool_value(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
rc = -EFAULT; rc = -EFAULT;
len = policydb->p_bools.nprim; len = policydb->p_bools.nprim;
...@@ -3038,29 +3091,28 @@ int security_get_bool_value(struct selinux_state *state, ...@@ -3038,29 +3091,28 @@ int security_get_bool_value(struct selinux_state *state,
rc = policydb->bool_val_to_struct[index]->state; rc = policydb->bool_val_to_struct[index]->state;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
static int security_preserve_bools(struct selinux_state *state, static int security_preserve_bools(struct selinux_policy *oldpolicy,
struct policydb *policydb) struct selinux_policy *newpolicy)
{ {
int rc, *bvalues = NULL; int rc, *bvalues = NULL;
char **bnames = NULL; char **bnames = NULL;
struct cond_bool_datum *booldatum; struct cond_bool_datum *booldatum;
u32 i, nbools = 0; u32 i, nbools = 0;
read_lock(&state->ss->policy_rwlock); rc = security_get_bools(oldpolicy, &nbools, &bnames, &bvalues);
rc = security_get_bools(state->ss->policy, &nbools, &bnames, &bvalues);
read_unlock(&state->ss->policy_rwlock);
if (rc) if (rc)
goto out; goto out;
for (i = 0; i < nbools; i++) { for (i = 0; i < nbools; i++) {
booldatum = symtab_search(&policydb->p_bools, bnames[i]); booldatum = symtab_search(&newpolicy->policydb.p_bools,
bnames[i]);
if (booldatum) if (booldatum)
booldatum->state = bvalues[i]; booldatum->state = bvalues[i];
} }
evaluate_cond_nodes(policydb); evaluate_cond_nodes(&newpolicy->policydb);
out: out:
if (bnames) { if (bnames) {
...@@ -3079,6 +3131,7 @@ static int security_preserve_bools(struct selinux_state *state, ...@@ -3079,6 +3131,7 @@ static int security_preserve_bools(struct selinux_state *state,
int security_sid_mls_copy(struct selinux_state *state, int security_sid_mls_copy(struct selinux_state *state,
u32 sid, u32 mls_sid, u32 *new_sid) u32 sid, u32 mls_sid, u32 *new_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
struct context *context1; struct context *context1;
...@@ -3096,10 +3149,10 @@ int security_sid_mls_copy(struct selinux_state *state, ...@@ -3096,10 +3149,10 @@ int security_sid_mls_copy(struct selinux_state *state,
context_init(&newcon); context_init(&newcon);
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
if (!policydb->mls_enabled) { if (!policydb->mls_enabled) {
*new_sid = sid; *new_sid = sid;
...@@ -3131,7 +3184,8 @@ int security_sid_mls_copy(struct selinux_state *state, ...@@ -3131,7 +3184,8 @@ int security_sid_mls_copy(struct selinux_state *state,
/* Check the validity of the new context. */ /* Check the validity of the new context. */
if (!policydb_context_isvalid(policydb, &newcon)) { if (!policydb_context_isvalid(policydb, &newcon)) {
rc = convert_context_handle_invalid_context(state, &newcon); rc = convert_context_handle_invalid_context(state, policydb,
&newcon);
if (rc) { if (rc) {
if (!context_struct_to_string(policydb, &newcon, &s, if (!context_struct_to_string(policydb, &newcon, &s,
&len)) { &len)) {
...@@ -3152,7 +3206,7 @@ int security_sid_mls_copy(struct selinux_state *state, ...@@ -3152,7 +3206,7 @@ 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);
out_unlock: out_unlock:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
context_destroy(&newcon); context_destroy(&newcon);
out: out:
return rc; return rc;
...@@ -3183,6 +3237,7 @@ int security_net_peersid_resolve(struct selinux_state *state, ...@@ -3183,6 +3237,7 @@ int security_net_peersid_resolve(struct selinux_state *state,
u32 xfrm_sid, u32 xfrm_sid,
u32 *peer_sid) u32 *peer_sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc; int rc;
...@@ -3209,10 +3264,10 @@ int security_net_peersid_resolve(struct selinux_state *state, ...@@ -3209,10 +3264,10 @@ int security_net_peersid_resolve(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
/* /*
* We don't need to check initialized here since the only way both * We don't need to check initialized here since the only way both
...@@ -3249,7 +3304,7 @@ int security_net_peersid_resolve(struct selinux_state *state, ...@@ -3249,7 +3304,7 @@ int security_net_peersid_resolve(struct selinux_state *state,
* expressive */ * expressive */
*peer_sid = xfrm_sid; *peer_sid = xfrm_sid;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -3353,27 +3408,31 @@ int security_get_permissions(struct selinux_policy *policy, ...@@ -3353,27 +3408,31 @@ int security_get_permissions(struct selinux_policy *policy,
int security_get_reject_unknown(struct selinux_state *state) int security_get_reject_unknown(struct selinux_state *state)
{ {
struct selinux_policy *policy;
int value; int value;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
value = state->ss->policy->policydb.reject_unknown; policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); value = policy->policydb.reject_unknown;
rcu_read_unlock();
return value; return value;
} }
int security_get_allow_unknown(struct selinux_state *state) int security_get_allow_unknown(struct selinux_state *state)
{ {
struct selinux_policy *policy;
int value; int value;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
value = state->ss->policy->policydb.allow_unknown; policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); value = policy->policydb.allow_unknown;
rcu_read_unlock();
return value; return value;
} }
...@@ -3390,14 +3449,16 @@ int security_get_allow_unknown(struct selinux_state *state) ...@@ -3390,14 +3449,16 @@ int security_get_allow_unknown(struct selinux_state *state)
int security_policycap_supported(struct selinux_state *state, int security_policycap_supported(struct selinux_state *state,
unsigned int req_cap) unsigned int req_cap)
{ {
struct selinux_policy *policy;
int rc; int rc;
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
rc = ebitmap_get_bit(&state->ss->policy->policydb.policycaps, req_cap); policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); rc = ebitmap_get_bit(&policy->policydb.policycaps, req_cap);
rcu_read_unlock();
return rc; return rc;
} }
...@@ -3420,6 +3481,7 @@ void selinux_audit_rule_free(void *vrule) ...@@ -3420,6 +3481,7 @@ void selinux_audit_rule_free(void *vrule)
int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
{ {
struct selinux_state *state = &selinux_state; struct selinux_state *state = &selinux_state;
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct selinux_audit_rule *tmprule; struct selinux_audit_rule *tmprule;
struct role_datum *roledatum; struct role_datum *roledatum;
...@@ -3463,11 +3525,11 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) ...@@ -3463,11 +3525,11 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
context_init(&tmprule->au_ctxt); context_init(&tmprule->au_ctxt);
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
tmprule->au_seqno = state->ss->latest_granting; tmprule->au_seqno = policy->latest_granting;
switch (field) { switch (field) {
case AUDIT_SUBJ_USER: case AUDIT_SUBJ_USER:
...@@ -3506,7 +3568,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) ...@@ -3506,7 +3568,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
} }
rc = 0; rc = 0;
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
if (rc) { if (rc) {
selinux_audit_rule_free(tmprule); selinux_audit_rule_free(tmprule);
...@@ -3546,6 +3608,7 @@ int selinux_audit_rule_known(struct audit_krule *rule) ...@@ -3546,6 +3608,7 @@ int selinux_audit_rule_known(struct audit_krule *rule)
int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
{ {
struct selinux_state *state = &selinux_state; struct selinux_state *state = &selinux_state;
struct selinux_policy *policy;
struct context *ctxt; struct context *ctxt;
struct mls_level *level; struct mls_level *level;
struct selinux_audit_rule *rule = vrule; struct selinux_audit_rule *rule = vrule;
...@@ -3559,14 +3622,16 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) ...@@ -3559,14 +3622,16 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
if (rule->au_seqno < state->ss->latest_granting) { if (rule->au_seqno < policy->latest_granting) {
match = -ESTALE; match = -ESTALE;
goto out; goto out;
} }
ctxt = sidtab_search(state->ss->policy->sidtab, sid); ctxt = sidtab_search(policy->sidtab, sid);
if (unlikely(!ctxt)) { if (unlikely(!ctxt)) {
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
sid); sid);
...@@ -3650,7 +3715,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) ...@@ -3650,7 +3715,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
} }
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return match; return match;
} }
...@@ -3728,6 +3793,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3728,6 +3793,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
struct netlbl_lsm_secattr *secattr, struct netlbl_lsm_secattr *secattr,
u32 *sid) u32 *sid)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
struct sidtab *sidtab; struct sidtab *sidtab;
int rc; int rc;
...@@ -3739,10 +3805,10 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3739,10 +3805,10 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
return 0; return 0;
} }
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
sidtab = state->ss->policy->sidtab; sidtab = policy->sidtab;
if (secattr->flags & NETLBL_SECATTR_CACHE) if (secattr->flags & NETLBL_SECATTR_CACHE)
*sid = *(u32 *)secattr->cache->data; *sid = *(u32 *)secattr->cache->data;
...@@ -3778,12 +3844,12 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3778,12 +3844,12 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
} else } else
*sid = SECSID_NULL; *sid = SECSID_NULL;
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return 0; return 0;
out_free: out_free:
ebitmap_destroy(&ctx_new.range.level[0].cat); ebitmap_destroy(&ctx_new.range.level[0].cat);
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
...@@ -3800,6 +3866,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, ...@@ -3800,6 +3866,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
int security_netlbl_sid_to_secattr(struct selinux_state *state, int security_netlbl_sid_to_secattr(struct selinux_state *state,
u32 sid, struct netlbl_lsm_secattr *secattr) u32 sid, struct netlbl_lsm_secattr *secattr)
{ {
struct selinux_policy *policy;
struct policydb *policydb; struct policydb *policydb;
int rc; int rc;
struct context *ctx; struct context *ctx;
...@@ -3807,12 +3874,12 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, ...@@ -3807,12 +3874,12 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
if (!selinux_initialized(state)) if (!selinux_initialized(state))
return 0; return 0;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
policy = rcu_dereference(state->policy);
policydb = &state->ss->policy->policydb; policydb = &policy->policydb;
rc = -ENOENT; rc = -ENOENT;
ctx = sidtab_search(state->ss->policy->sidtab, sid); ctx = sidtab_search(policy->sidtab, sid);
if (ctx == NULL) if (ctx == NULL)
goto out; goto out;
...@@ -3827,7 +3894,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, ...@@ -3827,7 +3894,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
mls_export_netlbl_lvl(policydb, ctx, secattr); mls_export_netlbl_lvl(policydb, ctx, secattr);
rc = mls_export_netlbl_cat(policydb, ctx, secattr); rc = mls_export_netlbl_cat(policydb, ctx, secattr);
out: out:
read_unlock(&state->ss->policy_rwlock); rcu_read_unlock();
return rc; return rc;
} }
#endif /* CONFIG_NETLABEL */ #endif /* CONFIG_NETLABEL */
...@@ -3841,6 +3908,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, ...@@ -3841,6 +3908,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
int security_read_policy(struct selinux_state *state, int security_read_policy(struct selinux_state *state,
void **data, size_t *len) void **data, size_t *len)
{ {
struct selinux_policy *policy;
int rc; int rc;
struct policy_file fp; struct policy_file fp;
...@@ -3856,9 +3924,10 @@ int security_read_policy(struct selinux_state *state, ...@@ -3856,9 +3924,10 @@ int security_read_policy(struct selinux_state *state,
fp.data = *data; fp.data = *data;
fp.len = *len; fp.len = *len;
read_lock(&state->ss->policy_rwlock); rcu_read_lock();
rc = policydb_write(&state->ss->policy->policydb, &fp); policy = rcu_dereference(state->policy);
read_unlock(&state->ss->policy_rwlock); rc = policydb_write(&policy->policydb, &fp);
rcu_read_unlock();
if (rc) if (rc)
return rc; return rc;
......
...@@ -26,12 +26,7 @@ struct selinux_policy { ...@@ -26,12 +26,7 @@ struct selinux_policy {
struct sidtab *sidtab; struct sidtab *sidtab;
struct policydb policydb; struct policydb policydb;
struct selinux_map map; struct selinux_map map;
};
struct selinux_ss {
rwlock_t policy_rwlock;
u32 latest_granting; u32 latest_granting;
struct selinux_policy *policy;
} __randomize_layout; } __randomize_layout;
void services_compute_xperms_drivers(struct extended_perms *xperms, void services_compute_xperms_drivers(struct extended_perms *xperms,
......
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