Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp3421377pxf; Mon, 29 Mar 2021 01:34:32 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwOrG9vHyd87HhBGXRv3y62KsZvm8NexT9QJs5YHoBuXcRGee9AZukzvnM34VwIRHp5ViBU X-Received: by 2002:a17:906:4ada:: with SMTP id u26mr27448752ejt.129.1617006872060; Mon, 29 Mar 2021 01:34:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1617006872; cv=none; d=google.com; s=arc-20160816; b=gxabYyEHNfn5Lnt3o2eiT7NKmd+px4+WybQ2NlRJDaL+8n623n+jAGdoYnG05kxCUE fdJRDagLOtyoLHr1n0B+uaWP6NFb8T5Oq0vFPiE3Y6X5SGD0vk5g2Y2YM3reljZ5ayga iS3FgVLeafOyvvat3h+mEwycfhEK1S+Oeltcri0hRAVz+cGq7dXK/HvxYtMpSnynf616 Rl76C4vkryZHYwoSXh8hXPHdd6ubJhg+KxSCfylYLwhjKgvZXKzq523ATtNvEMBnhw0N Pk0p90CWZsXnQqrYQpkBKFKTRN5NEtdowLtQE3FMV6N6y0SUro/W9GM3cPybrYQ64+mv S7Gw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=Uw8WIz7dOH7HeeW4DjhfgEB1iv2cvBdhmF5mQ5yfXJM=; b=I9zb3fj/LZsaqmdJ2lQHuC6EiSoX4NBDQcWwbBIaOtInPzrw+K8HagbVRmw3oiwVPA 2OEc+20TaXnqdIrAmml6QeSzAnqWX07stiijOBNNH0grxfuPO7OZMAhmo1E0HZDJ8Vj3 x8hCzo+uow5biRp99mDff5CZhJ8hi3gc9ky21CdAXsZtoa6b1bYm5C6XV6jvZ3NT8DsU uSttkyNUkPGvsfQkCQMb4+DfQq1ejhEYJEbZvjOcKaFCPrXj00vXVEU2DLKUM+A2YhPa 0+nqhGJ+T2xTAhW1Oqlc6mt7MQOF1JxIcV+y6zMy+OrlNdgSthEP6zL+fdVlkbsvrPj1 pnRw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=AoK1ri9t; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id c15si12055762ede.264.2021.03.29.01.34.09; Mon, 29 Mar 2021 01:34:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=AoK1ri9t; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234191AbhC2IcO (ORCPT + 99 others); Mon, 29 Mar 2021 04:32:14 -0400 Received: from mail.kernel.org ([198.145.29.99]:36014 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232256AbhC2IUL (ORCPT ); Mon, 29 Mar 2021 04:20:11 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 13556619CA; Mon, 29 Mar 2021 08:19:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1617006000; bh=ZMl16UAd9pVTrK0QjJ1Fi3BOD/lFDmcZlzwI6WsV3y0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AoK1ri9tuVkfQnqM+SaA+s9pNDaa21djjSio3sN1vbLP43NSJgzGND9oCbJ5Cu/TE 6F3yLRRYGaovbWP4r1hthqXOXYxd4af/Op8r4bOxverrx9xspbrS6c1qKcGZkLsGPR QisicGlEZRCNcbYq4dUClglA3kY/Ol5fbwggeoOM= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Tyler Hicks , Ondrej Mosnacek , Paul Moore Subject: [PATCH 5.10 064/221] selinux: fix variable scope issue in live sidtab conversion Date: Mon, 29 Mar 2021 09:56:35 +0200 Message-Id: <20210329075631.325895612@linuxfoundation.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210329075629.172032742@linuxfoundation.org> References: <20210329075629.172032742@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ondrej Mosnacek commit 6406887a12ee5dcdaffff1a8508d91113d545559 upstream. Commit 02a52c5c8c3b ("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. Before 02a52c5c8c3b, 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 commit ee1a84fdfeed ("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: 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs") Cc: stable@vger.kernel.org Tested-by: Tyler Hicks Reviewed-by: Tyler Hicks Signed-off-by: Ondrej Mosnacek [PM: merge fuzz in security.h and services.c] Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/include/security.h | 15 ++++++-- security/selinux/selinuxfs.c | 10 ++--- security/selinux/ss/services.c | 65 ++++++++++++++++++++++-------------- 3 files changed, 56 insertions(+), 34 deletions(-) --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -219,14 +219,21 @@ static inline bool selinux_policycap_gen return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS]); } +struct selinux_policy_convert_data; + +struct selinux_load_state { + struct selinux_policy *policy; + struct selinux_policy_convert_data *convert_data; +}; + int security_mls_enabled(struct selinux_state *state); int security_load_policy(struct selinux_state *state, - void *data, size_t len, - struct selinux_policy **newpolicyp); + void *data, size_t len, + struct selinux_load_state *load_state); void selinux_policy_commit(struct selinux_state *state, - struct selinux_policy *newpolicy); + struct selinux_load_state *load_state); void selinux_policy_cancel(struct selinux_state *state, - struct selinux_policy *policy); + struct selinux_load_state *load_state); int security_read_policy(struct selinux_state *state, void **data, size_t *len); --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -616,7 +616,7 @@ static ssize_t sel_write_load(struct fil { 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 fil 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; --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -66,6 +66,17 @@ #include "audit.h" #include "policycap_names.h" +struct convert_context_args { + struct selinux_state *state; + struct policydb *oldp; + struct policydb *newp; +}; + +struct selinux_policy_convert_data { + struct convert_context_args args; + struct sidtab_convert_params sidtab_params; +}; + /* Forward declaration. */ static int context_struct_to_string(struct policydb *policydb, struct context *context, @@ -1975,12 +1986,6 @@ static inline int convert_context_handle return 0; } -struct convert_context_args { - struct selinux_state *state; - struct policydb *oldp; - struct policydb *newp; -}; - /* * Convert the values in the security context * structure `oldc' from the values specified @@ -2160,7 +2165,7 @@ static void selinux_policy_cond_free(str } void selinux_policy_cancel(struct selinux_state *state, - struct selinux_policy *policy) + struct selinux_load_state *load_state) { struct selinux_policy *oldpolicy; @@ -2168,7 +2173,8 @@ void selinux_policy_cancel(struct selinu lockdep_is_held(&state->policy_mutex)); sidtab_cancel_convert(oldpolicy->sidtab); - selinux_policy_free(policy); + selinux_policy_free(load_state->policy); + kfree(load_state->convert_data); } static void selinux_notify_policy_change(struct selinux_state *state, @@ -2183,9 +2189,9 @@ static void selinux_notify_policy_change } void selinux_policy_commit(struct selinux_state *state, - struct selinux_policy *newpolicy) + struct selinux_load_state *load_state) { - struct selinux_policy *oldpolicy; + struct selinux_policy *oldpolicy, *newpolicy = load_state->policy; u32 seqno; oldpolicy = rcu_dereference_protected(state->policy, @@ -2225,6 +2231,7 @@ void selinux_policy_commit(struct selinu /* Free the old policy */ synchronize_rcu(); selinux_policy_free(oldpolicy); + kfree(load_state->convert_data); /* Notify others of the policy change */ selinux_notify_policy_change(state, seqno); @@ -2241,11 +2248,10 @@ void selinux_policy_commit(struct selinu * loading the new policy. */ int security_load_policy(struct selinux_state *state, void *data, size_t len, - struct selinux_policy **newpolicyp) + struct selinux_load_state *load_state) { struct selinux_policy *newpolicy, *oldpolicy; - struct sidtab_convert_params convert_params; - struct convert_context_args args; + struct selinux_policy_convert_data *convert_data; int rc = 0; struct policy_file file = { data, len }, *fp = &file; @@ -2275,10 +2281,10 @@ int security_load_policy(struct selinux_ goto err_mapping; } - if (!selinux_initialized(state)) { /* First policy load, so no need to preserve state from old policy */ - *newpolicyp = newpolicy; + load_state->policy = newpolicy; + load_state->convert_data = NULL; return 0; } @@ -2292,29 +2298,38 @@ int security_load_policy(struct selinux_ goto err_free_isids; } + convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL); + if (!convert_data) { + rc = -ENOMEM; + goto err_free_isids; + } + /* * Convert the internal representations of contexts * in the new SID table. */ - args.state = state; - args.oldp = &oldpolicy->policydb; - args.newp = &newpolicy->policydb; - - convert_params.func = convert_context; - convert_params.args = &args; - convert_params.target = newpolicy->sidtab; + convert_data->args.state = state; + convert_data->args.oldp = &oldpolicy->policydb; + convert_data->args.newp = &newpolicy->policydb; + + convert_data->sidtab_params.func = convert_context; + convert_data->sidtab_params.args = &convert_data->args; + convert_data->sidtab_params.target = newpolicy->sidtab; - rc = sidtab_convert(oldpolicy->sidtab, &convert_params); + rc = sidtab_convert(oldpolicy->sidtab, &convert_data->sidtab_params); if (rc) { pr_err("SELinux: unable to convert the internal" " representation of contexts in the new SID" " table\n"); - goto err_free_isids; + goto err_free_convert_data; } - *newpolicyp = newpolicy; + load_state->policy = newpolicy; + load_state->convert_data = convert_data; return 0; +err_free_convert_data: + kfree(convert_data); err_free_isids: sidtab_destroy(newpolicy->sidtab); err_mapping: