selinux: fix variable scope issue in live sidtab conversion
commit 6406887a12ee5dcdaffff1a8508d91113d545559 upstream. Commit02a52c5c8c
("selinux: move policy commit after updating selinuxfs") moved the selinux_policy_commit() call out of security_load_policy() into sel_write_load(), which caused a subtle yet rather serious bug. The problem is that security_load_policy() passes a reference to the convert_params local variable to sidtab_convert(), which stores it in the sidtab, where it may be accessed until the policy is swapped over and RCU synchronized. Before02a52c5c8c
, selinux_policy_commit() was called directly from security_load_policy(), so the convert_params pointer remained valid all the way until the old sidtab was destroyed, but now that's no longer the case and calls to sidtab_context_to_sid() on the old sidtab after security_load_policy() returns may cause invalid memory accesses. This can be easily triggered using the stress test from commitee1a84fdfe
("selinux: overhaul sidtab to fix bug and improve performance"): ``` function rand_cat() { echo $(( $RANDOM % 1024 )) } function do_work() { while true; do echo -n "system_u:system_r:kernel_t:s0:c$(rand_cat),c$(rand_cat)" \ >/sys/fs/selinux/context 2>/dev/null || true done } do_work >/dev/null & do_work >/dev/null & do_work >/dev/null & while load_policy; do echo -n .; sleep 0.1; done kill %1 kill %2 kill %3 ``` Fix this by allocating the temporary sidtab convert structures dynamically and passing them among the selinux_policy_{load,cancel,commit} functions. Fixes:02a52c5c8c
("selinux: move policy commit after updating selinuxfs") Cc: stable@vger.kernel.org Tested-by: Tyler Hicks <tyhicks@linux.microsoft.com> Reviewed-by: Tyler Hicks <tyhicks@linux.microsoft.com> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com> [PM: merge fuzz in security.h and services.c] Signed-off-by: Paul Moore <paul@paul-moore.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
9731e08a33
commit
19c9967e49
@@ -616,7 +616,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
||||
|
||||
{
|
||||
struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
|
||||
struct selinux_policy *newpolicy;
|
||||
struct selinux_load_state load_state;
|
||||
ssize_t length;
|
||||
void *data = NULL;
|
||||
|
||||
@@ -642,19 +642,19 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
||||
if (copy_from_user(data, buf, count) != 0)
|
||||
goto out;
|
||||
|
||||
length = security_load_policy(fsi->state, data, count, &newpolicy);
|
||||
length = security_load_policy(fsi->state, data, count, &load_state);
|
||||
if (length) {
|
||||
pr_warn_ratelimited("SELinux: failed to load policy\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
length = sel_make_policy_nodes(fsi, newpolicy);
|
||||
length = sel_make_policy_nodes(fsi, load_state.policy);
|
||||
if (length) {
|
||||
selinux_policy_cancel(fsi->state, newpolicy);
|
||||
selinux_policy_cancel(fsi->state, &load_state);
|
||||
goto out;
|
||||
}
|
||||
|
||||
selinux_policy_commit(fsi->state, newpolicy);
|
||||
selinux_policy_commit(fsi->state, &load_state);
|
||||
|
||||
length = count;
|
||||
|
||||
|
Reference in New Issue
Block a user