All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tyler Hicks <tyhicks@linux.microsoft.com>
To: Ondrej Mosnacek <omosnace@redhat.com>
Cc: selinux@vger.kernel.org, Paul Moore <paul@paul-moore.com>,
	Stephen Smalley <stephen.smalley.work@gmail.com>
Subject: Re: [PATCH v2 2/2] selinux: fix variable scope issue in live sidtab conversion
Date: Tue, 2 Mar 2021 20:57:30 -0600	[thread overview]
Message-ID: <20210303025730.GI6000@sequoia> (raw)
In-Reply-To: <20210212185930.130477-3-omosnace@redhat.com>

On 2021-02-12 19:59:30, Ondrej Mosnacek wrote:
> 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.
> 
> Note that this commit also fixes the minor issue of logging a
> MAC_POLICY_LOAD audit record in case sel_make_policy_nodes() fails (in
> which case the new policy isn't actually loaded).
> 
> Fixes: 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>

Tested-by: Tyler Hicks <tyhicks@linux.microsoft.com>
Reviewed-by: Tyler Hicks <tyhicks@linux.microsoft.com>

Feel free to leave those tags on your v3 submission after making the two
small changes requested by Paul.

Tyler

> ---
>  security/selinux/include/security.h | 15 ++++++---
>  security/selinux/selinuxfs.c        | 10 +++---
>  security/selinux/ss/services.c      | 51 +++++++++++++++++++----------
>  3 files changed, 49 insertions(+), 27 deletions(-)
> 
> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> index 765a258a899e..25db66e0ac51 100644
> --- a/security/selinux/include/security.h
> +++ b/security/selinux/include/security.h
> @@ -219,14 +219,21 @@ static inline bool selinux_policycap_genfs_seclabel_symlinks(void)
>  	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);
>  
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 340711e3dc9a..158d44ea93f4 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -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;
>  
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index 5e08ce2c5994..fada4ebc7ef8 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2157,8 +2157,13 @@ static void selinux_policy_cond_free(struct selinux_policy *policy)
>  	kfree(policy);
>  }
>  
> +struct selinux_policy_convert_data {
> +	struct convert_context_args args;
> +	struct sidtab_convert_params sidtab_params;
> +};
> +
>  void selinux_policy_cancel(struct selinux_state *state,
> -			struct selinux_policy *policy)
> +			   struct selinux_load_state *load_state)
>  {
>  	struct selinux_policy *oldpolicy;
>  
> @@ -2166,7 +2171,8 @@ void selinux_policy_cancel(struct selinux_state *state,
>  					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,
> @@ -2181,9 +2187,9 @@ static void selinux_notify_policy_change(struct selinux_state *state,
>  }
>  
>  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,
> @@ -2223,6 +2229,7 @@ void selinux_policy_commit(struct selinux_state *state,
>  	/* 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);
> @@ -2239,11 +2246,10 @@ void selinux_policy_commit(struct selinux_state *state,
>   * 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;
>  
> @@ -2273,10 +2279,10 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
>  		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;
>  	}
>  
> @@ -2290,29 +2296,38 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
>  		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_data->args.state = state;
> +	convert_data->args.oldp = &oldpolicy->policydb;
> +	convert_data->args.newp = &newpolicy->policydb;
>  
> -	convert_params.func = convert_context;
> -	convert_params.args = &args;
> -	convert_params.target = newpolicy->sidtab;
> +	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:
> -- 
> 2.29.2
> 

  parent reply	other threads:[~2021-03-03  4:35 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-12 18:59 [PATCH v2 0/2] selinux: policy load fixes Ondrej Mosnacek
2021-02-12 18:59 ` [PATCH v2 1/2] selinux: don't log MAC_POLICY_LOAD record on failed policy load Ondrej Mosnacek
2021-02-25 18:14   ` Paul Moore
2021-02-26 14:46     ` Ondrej Mosnacek
2021-02-28 18:52       ` Paul Moore
2021-03-03  2:55         ` Tyler Hicks
2021-03-03  8:54           ` Ondrej Mosnacek
2021-03-18 14:48             ` Paul Moore
2021-03-18 15:12               ` Stephen Smalley
2021-02-12 18:59 ` [PATCH v2 2/2] selinux: fix variable scope issue in live sidtab conversion Ondrej Mosnacek
2021-02-25 19:20   ` Paul Moore
2021-02-26 14:47     ` Ondrej Mosnacek
2021-03-03  2:57   ` Tyler Hicks [this message]
2021-03-03  9:01     ` Ondrej Mosnacek
2021-03-18 11:22       ` Stephen Smalley
2021-03-18 11:45         ` Ondrej Mosnacek
2021-03-18 14:49           ` Paul Moore

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210303025730.GI6000@sequoia \
    --to=tyhicks@linux.microsoft.com \
    --cc=omosnace@redhat.com \
    --cc=paul@paul-moore.com \
    --cc=selinux@vger.kernel.org \
    --cc=stephen.smalley.work@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.