Move from static allocation of a single selinux namespace to dynamic allocation. Include necessary support for lifecycle management of the selinux namespace, modeled after the user namespace support. Signed-off-by: Stephen Smalley <stephen.smalley.work@xxxxxxxxx> --- security/selinux/avc.c | 32 ++++++++++---- security/selinux/hooks.c | 65 +++++++++++++++++++++++++---- security/selinux/include/security.h | 26 +++++++++++- security/selinux/selinuxfs.c | 3 +- security/selinux/ss/services.c | 2 +- 5 files changed, 109 insertions(+), 19 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index effa0accefc5..cc20c5d8b63a 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -91,20 +91,34 @@ struct selinux_avc { struct avc_cache avc_cache; }; -static struct selinux_avc selinux_avc; - -void selinux_avc_init(struct selinux_avc **avc) +int selinux_avc_create(struct selinux_avc **avc) { + struct selinux_avc *newavc; int i; - selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; + newavc = kzalloc(sizeof(*newavc), GFP_KERNEL); + if (!newavc) + return -ENOMEM; + + newavc->avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; + for (i = 0; i < AVC_CACHE_SLOTS; i++) { - INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]); - spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]); + INIT_HLIST_HEAD(&newavc->avc_cache.slots[i]); + spin_lock_init(&newavc->avc_cache.slots_lock[i]); } - atomic_set(&selinux_avc.avc_cache.active_nodes, 0); - atomic_set(&selinux_avc.avc_cache.lru_hint, 0); - *avc = &selinux_avc; + atomic_set(&newavc->avc_cache.active_nodes, 0); + atomic_set(&newavc->avc_cache.lru_hint, 0); + + *avc = newavc; + return 0; +} + +static void avc_flush(struct selinux_avc *avc); + +void selinux_avc_free(struct selinux_avc *avc) +{ + avc_flush(avc); + kfree(avc); } unsigned int avc_get_cache_threshold(struct selinux_avc *avc) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 400e4dec90a5..ad8172ae7fda 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -108,7 +108,7 @@ #define SELINUX_INODE_INIT_XATTRS 1 -static struct selinux_state init_selinux_state; +static struct selinux_state *init_selinux_state; struct selinux_state *current_selinux_state; /* SECMARK reference count */ @@ -7562,16 +7562,67 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { #endif }; +static void selinux_state_free(struct work_struct *work); + +int selinux_state_create(struct selinux_state *parent, + struct selinux_state **state) +{ + struct selinux_state *newstate; + int rc; + + newstate = kzalloc(sizeof(*newstate), GFP_KERNEL); + if (!newstate) + return -ENOMEM; + + refcount_set(&newstate->count, 1); + INIT_WORK(&newstate->work, selinux_state_free); + + mutex_init(&newstate->status_lock); + mutex_init(&newstate->policy_mutex); + + rc = selinux_avc_create(&newstate->avc); + if (rc) + goto err; + + if (parent) + newstate->parent = get_selinux_state(parent); + + *state = newstate; + return 0; +err: + kfree(newstate); + return rc; +} + +static void selinux_state_free(struct work_struct *work) +{ + struct selinux_state *parent, *state = + container_of(work, struct selinux_state, work); + + do { + parent = state->parent; + if (state->status_page) + __free_page(state->status_page); + selinux_policy_free(state->policy); + selinux_avc_free(state->avc); + kfree(state); + state = parent; + } while (state && refcount_dec_and_test(&state->count)); +} + +void __put_selinux_state(struct selinux_state *state) +{ + schedule_work(&state->work); +} + static __init int selinux_init(void) { pr_info("SELinux: Initializing.\n"); - memset(&init_selinux_state, 0, sizeof(init_selinux_state)); - enforcing_set(&init_selinux_state, selinux_enforcing_boot); - selinux_avc_init(&init_selinux_state.avc); - mutex_init(&init_selinux_state.status_lock); - mutex_init(&init_selinux_state.policy_mutex); - current_selinux_state = &init_selinux_state; + if (selinux_state_create(NULL, &init_selinux_state)) + panic("SELinux: Could not create initial namespace\n"); + enforcing_set(init_selinux_state, selinux_enforcing_boot); + current_selinux_state = init_selinux_state; /* Set the security state for the initial task. */ cred_init_security(); diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 14aa2cbb391a..6df38c714d1f 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -104,9 +104,33 @@ struct selinux_state { struct selinux_avc *avc; struct selinux_policy __rcu *policy; struct mutex policy_mutex; + struct selinux_state *parent; + + refcount_t count; + struct work_struct work; } __randomize_layout; -void selinux_avc_init(struct selinux_avc **avc); +int selinux_state_create(struct selinux_state *parent, + struct selinux_state **state); +void __put_selinux_state(struct selinux_state *state); + +void selinux_policy_free(struct selinux_policy __rcu *policy); + +int selinux_avc_create(struct selinux_avc **avc); +void selinux_avc_free(struct selinux_avc *avc); + +static inline void put_selinux_state(struct selinux_state *state) +{ + if (state && refcount_dec_and_test(&state->count)) + __put_selinux_state(state); +} + +static inline struct selinux_state * +get_selinux_state(struct selinux_state *state) +{ + refcount_inc(&state->count); + return state; +} extern struct selinux_state *current_selinux_state; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index a72fc91f10ec..a59153c322cc 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -90,7 +90,7 @@ static int selinux_fs_info_create(struct super_block *sb) return -ENOMEM; fsi->last_ino = SEL_INO_NEXT - 1; - fsi->state = current_selinux_state; + fsi->state = get_selinux_state(current_selinux_state); fsi->sb = sb; sb->s_fs_info = fsi; return 0; @@ -102,6 +102,7 @@ static void selinux_fs_info_free(struct super_block *sb) unsigned int i; if (fsi) { + put_selinux_state(fsi->state); for (i = 0; i < fsi->bool_num; i++) kfree(fsi->bool_pending_names[i]); kfree(fsi->bool_pending_names); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index db6c17bb274d..bbac5f3f2fd3 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2183,7 +2183,7 @@ static void security_load_policycaps(struct selinux_state *state, static int security_preserve_bools(struct selinux_policy *oldpolicy, struct selinux_policy *newpolicy); -static void selinux_policy_free(struct selinux_policy *policy) +void selinux_policy_free(struct selinux_policy __rcu *policy) { if (!policy) return; -- 2.47.1