linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: sds@tycho.nsa.gov (Stephen Smalley)
To: linux-security-module@vger.kernel.org
Subject: [PATCH v2 4/5] selinux: Use pointer to switch policydb and sidtab
Date: Tue, 30 Jan 2018 09:37:27 -0500	[thread overview]
Message-ID: <1517323047.14420.14.camel@tycho.nsa.gov> (raw)
In-Reply-To: <20180126143241.23108-5-peter.enderborg@sony.com>

On Fri, 2018-01-26 at 15:32 +0100, peter.enderborg at sony.com wrote:
> From: Peter Enderborg <peter.enderborg@sony.com>
> 
> This i preparation for switching to RCU locks. To be able to use
> RCU we need atomic switched pointer. This adds the dynamic
> memory copying to be a single pointer. It copy all the
> data structures in to new ones. This is an overhead
> for writing rules but the benifit is RCU.
> 
> Signed-off-by: Peter Enderborg <peter.enderborg@sony.com>
> ---
>  security/selinux/ss/services.c | 139 +++++++++++++++++++++++------
> ------------
>  1 file changed, 78 insertions(+), 61 deletions(-)
> 
> diff --git a/security/selinux/ss/services.c
> b/security/selinux/ss/services.c
> index 2a8486c..81c5717 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2064,76 +2064,67 @@ static int security_preserve_bools(struct
> policydb *p);
>   */
>  int security_load_policy(void *data, size_t len)
>  {
> -	struct policydb *oldpolicydb, *newpolicydb;
> +	struct policydb *oldpolicydb;
>  	struct sidtab oldsidtab, newsidtab;
>  	struct selinux_mapping *oldmap = NULL, *map = NULL;
>  	struct convert_context_args args;
> -	struct shared_current_mapping *new_mapping;
>  	struct shared_current_mapping *next_rcu;
> -
> +	struct shared_current_mapping *old_rcu;
>  	u32 seqno;
>  	u16 map_size;
>  	int rc = 0;
>  	struct policy_file file = { data, len }, *fp = &file;
>  
> -	oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL);
> -	if (!oldpolicydb) {
> -		rc = -ENOMEM;
> -		goto out;
> -	}
> -	new_mapping = kzalloc(sizeof(struct shared_current_mapping),
> -			      GFP_KERNEL);
> -	if (!new_mapping) {
> -		rc = -ENOMEM;
> -		goto out;
> -	}
> -	newpolicydb = oldpolicydb + 1;
> -	next_rcu = kmalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> -	if (!next_rcu) {
> -		rc = -ENOMEM;
> -		goto out;
> -	}
> -
>  	if (!ss_initialized) {
> -		crm = kzalloc(sizeof(struct shared_current_mapping),
> -			      GFP_KERNEL);
> -		if (!crm) {
> +		struct shared_current_mapping *first_mapping;
> +
> +		first_mapping = kzalloc(sizeof(struct
> shared_current_mapping),
> +					GFP_KERNEL);
> +		if (!first_mapping) {
>  			rc = -ENOMEM;
>  			goto out;
>  		}
>  		avtab_cache_init();
>  		ebitmap_cache_init();
>  		hashtab_cache_init();
> -		rc = policydb_read(&crm->policydb, fp);
> +		rc = policydb_read(&first_mapping->policydb, fp);
>  		if (rc) {
>  			avtab_cache_destroy();
>  			ebitmap_cache_destroy();
>  			hashtab_cache_destroy();
> +			kfree(first_mapping);
>  			goto out;
>  		}
>  
> -		crm->policydb.len = len;
> -		rc = selinux_set_mapping(&crm->policydb,
> secclass_map,
> -					 &crm->current_mapping,
> -					 &crm-
> >current_mapping_size);
> +		first_mapping->policydb.len = len;
> +		rc = selinux_set_mapping(&first_mapping->policydb,
> secclass_map,
> +					 &first_mapping-
> >current_mapping,
> +					 &first_mapping-
> >current_mapping_size);
>  		if (rc) {
> -			policydb_destroy(&crm->policydb);
> +			policydb_destroy(&first_mapping->policydb);
>  			avtab_cache_destroy();
>  			ebitmap_cache_destroy();
>  			hashtab_cache_destroy();
> +			kfree(first_mapping);
>  			goto out;
>  		}
>  
> -		rc = policydb_load_isids(&crm->policydb, &crm-
> >sidtab);
> +		rc = policydb_load_isids(&first_mapping->policydb,
> +					 &first_mapping->sidtab);
>  		if (rc) {
> -			policydb_destroy(&crm->policydb);
> +			policydb_destroy(&first_mapping->policydb);
>  			avtab_cache_destroy();
>  			ebitmap_cache_destroy();
>  			hashtab_cache_destroy();
> +			kfree(first_mapping);
>  			goto out;
>  		}
>  
> -		security_load_policycaps(&crm->policydb);
> +		security_load_policycaps(&first_mapping->policydb);
> +		crm = first_mapping;
> +
> +		smp_mb(); /* make sure that crm exist before we */
> +			  /* switch ss_initialized */
>  		ss_initialized = 1;
>  		seqno = ++latest_granting;
>  		selinux_complete_init();
> @@ -2148,30 +2139,44 @@ int security_load_policy(void *data, size_t
> len)
>  #if 0
>  	sidtab_hash_eval(&crm->sidtab, "sids");
>  #endif
> +	oldpolicydb = kzalloc(sizeof(*oldpolicydb), GFP_KERNEL);
> +	if (!oldpolicydb) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +
> +	next_rcu = kzalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> +	if (!next_rcu) {
> +		kfree(oldpolicydb);
> +		rc = -ENOMEM;
> +		goto out;
> +	}
>  
> -	rc = policydb_read(newpolicydb, fp);
> +	rc = policydb_read(&next_rcu->policydb, fp);
>  	if (rc)
>  		goto out;
>  
> -	newpolicydb->len = len;
> +	next_rcu->policydb.len = len;
> +	read_lock(&policy_rwlock);
>  	/* If switching between different policy types, log MLS
> status */
> -	if (crm->policydb.mls_enabled && !newpolicydb->mls_enabled)
> +	if (crm->policydb.mls_enabled && !next_rcu-
> >policydb.mls_enabled)
>  		printk(KERN_INFO "SELinux: Disabling MLS
> support...\n");
> -	else if (!crm->policydb.mls_enabled && newpolicydb-
> >mls_enabled)
> +	else if (!crm->policydb.mls_enabled && next_rcu-
> >policydb.mls_enabled)
>  		printk(KERN_INFO "SELinux: Enabling MLS
> support...\n");
>  
> -	rc = policydb_load_isids(newpolicydb, &newsidtab);
> +	rc = policydb_load_isids(&next_rcu->policydb, &newsidtab);
>  	if (rc) {
>  		printk(KERN_ERR "SELinux:  unable to load the
> initial SIDs\n");
> -		policydb_destroy(newpolicydb);
> +		policydb_destroy(&next_rcu->policydb);
>  		goto out;
>  	}
>  
> -	rc = selinux_set_mapping(newpolicydb, secclass_map, &map,
> &map_size);
> +	rc = selinux_set_mapping(&next_rcu->policydb, secclass_map,
> +				 &map, &map_size);
>  	if (rc)
>  		goto err;
>  
> -	rc = security_preserve_bools(newpolicydb);
> +	rc = security_preserve_bools(&next_rcu->policydb);
>  	if (rc) {
>  		printk(KERN_ERR "SELinux:  unable to preserve
> booleans\n");
>  		goto err;

Most of this shouldn't need to be under the read lock.

> @@ -2189,7 +2194,7 @@ int security_load_policy(void *data, size_t
> len)
>  	 * in the new SID table.
>  	 */
>  	args.oldp = &crm->policydb;
> -	args.newp = newpolicydb;
> +	args.newp = &next_rcu->policydb;
>  	rc = sidtab_map(&newsidtab, convert_context, &args);
>  	if (rc) {
>  		printk(KERN_ERR "SELinux:  unable to convert the
> internal"
> @@ -2204,8 +2209,9 @@ int security_load_policy(void *data, size_t
> len)
>  
>  	/* Install the new policydb and SID table. */
>  	/* next */
> +	security_load_policycaps(&next_rcu->policydb);

This cannot be done outside of the write lock; it has to be atomic with
the policy switch.

> +	read_unlock(&policy_rwlock);
>  	write_lock_irq(&policy_rwlock);
> -	memcpy(&next_rcu->policydb, newpolicydb, sizeof(struct
> policydb));
>  	sidtab_set(&next_rcu->sidtab, &newsidtab);
>  	security_load_policycaps(&next_rcu->policydb);
>  	oldmap = crm->current_mapping;
> @@ -2213,8 +2219,9 @@ int security_load_policy(void *data, size_t
> len)
>  	next_rcu->current_mapping_size = map_size;
>  
>  	seqno = ++latest_granting;
> -	write_unlock_irq(&policy_rwlock);
> +	old_rcu = crm;
>  	crm = next_rcu;
> +	write_unlock_irq(&policy_rwlock);
>  
>  	/* Free the old policydb and SID table. */
>  	policydb_destroy(oldpolicydb);
> @@ -2226,17 +2233,16 @@ int security_load_policy(void *data, size_t
> len)
>  	selinux_status_update_policyload(seqno);
>  	selinux_netlbl_cache_invalidate();
>  	selinux_xfrm_notify_policyload();
> +	kfree(oldpolicydb);
> +	kfree(old_rcu);
>  
>  	rc = 0;
>  	goto out;
> -
>  err:
>  	kfree(map);
>  	sidtab_destroy(&newsidtab);
> -	policydb_destroy(newpolicydb);
> -
> +	policydb_destroy(&next_rcu->policydb);
>  out:
> -	kfree(oldpolicydb);
>  	return rc;
>  }
>  
> @@ -2795,54 +2801,65 @@ int security_get_bools(int *len, char
> ***names, int **values)
>  	goto out;
>  }
>  
> -
>  int security_set_bools(int len, int *values)
>  {
> +	struct shared_current_mapping *next_rcu, *old_rcu;
>  	int i, rc;
>  	int lenp, seqno = 0;
>  	struct cond_node *cur;
>  
> -	write_lock_irq(&policy_rwlock);
> -
> +	next_rcu = kzalloc(sizeof(struct shared_current_mapping),
> GFP_KERNEL);
> +	read_lock(&policy_rwlock);
> +	old_rcu = crm;
> +	memcpy(&next_rcu->policydb, &old_rcu->policydb,
> +	       sizeof(struct policydb));

You are only doing a "shallow" copy of the policydb here, which
contains pointers to other structures.  So then below when you modify
state, you are modifying the original, not just the copy.  And you'll
end up double freeing if you free them both.

For reference, attached is a very old attempt to convert the policy
rwlock to RCU from KaiGai Kohei.  It may provide some insight into what
is needed here.
 
>  	rc = -EFAULT;
> -	lenp = crm->policydb.p_bools.nprim;
> +	lenp = next_rcu->policydb.p_bools.nprim;
> +
>  	if (len != lenp)
>  		goto out;
>  
>  	for (i = 0; i < len; i++) {
>  		if (!!values[i] !=
> -		    crm->policydb.bool_val_to_struct[i]->state) {
> +		    next_rcu->policydb.bool_val_to_struct[i]->state) 
> {
>  			audit_log(current->audit_context,
> GFP_ATOMIC,
>  				AUDIT_MAC_CONFIG_CHANGE,
>  				"bool=%s val=%d old_val=%d auid=%u
> ses=%u",
> -				sym_name(&crm->policydb, SYM_BOOLS,
> i),
> +				sym_name(&next_rcu->policydb,
> SYM_BOOLS, i),
>  				!!values[i],
> -				crm->policydb.bool_val_to_struct[i]-
> >state,
> +				next_rcu-
> >policydb.bool_val_to_struct[i]->state,
>  				from_kuid(&init_user_ns,
> audit_get_loginuid(current)),
>  				audit_get_sessionid(current));
>  		}
>  		if (values[i])
> -			crm->policydb.bool_val_to_struct[i]->state =
> 1;
> +			next_rcu->policydb.bool_val_to_struct[i]-
> >state = 1;
>  		else
> -			crm->policydb.bool_val_to_struct[i]->state =
> 0;
> +			next_rcu->policydb.bool_val_to_struct[i]-
> >state = 0;
>  	}
>  
> -	for (cur = crm->policydb.cond_list; cur; cur = cur->next) {
> -		rc = evaluate_cond_node(&crm->policydb, cur);
> +	for (cur = next_rcu->policydb.cond_list; cur; cur = cur-
> >next) {
> +		rc = evaluate_cond_node(&next_rcu->policydb, cur);
>  		if (rc)
>  			goto out;
>  	}
> +	read_unlock(&policy_rwlock);
> +	rc = 0;
>  
> +	write_lock_irq(&policy_rwlock);
>  	seqno = ++latest_granting;
> -	rc = 0;
> -out:
> +	crm = next_rcu;
>  	write_unlock_irq(&policy_rwlock);
> +out:
>  	if (!rc) {
>  		avc_ss_reset(seqno);
>  		selnl_notify_policyload(seqno);
>  		selinux_status_update_policyload(seqno);
>  		selinux_xfrm_notify_policyload();
> +	} else {
> +		kfree(next_rcu);
>  	}
> +	kfree(old_rcu);
> +
>  	return rc;
>  }
>  

  reply	other threads:[~2018-01-30 14:37 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-26 14:32 [PATCH v2 0/5] selinux:Significant reduce of preempt_disable holds peter.enderborg at sony.com
2018-01-26 14:32 ` [PATCH v2 1/5] selinux:Remove direct references to policydb peter.enderborg at sony.com
2018-01-30 13:46   ` Stephen Smalley
2018-02-01 15:17     ` peter enderborg
2018-02-01 15:28       ` Stephen Smalley
2018-02-01 15:55       ` Paul Moore
2018-04-03 11:41         ` peter enderborg
2018-04-03 11:56           ` Paul Moore
2018-01-26 14:32 ` [PATCH v2 2/5] selinux: Move policydb to pointer structure peter.enderborg at sony.com
2018-01-26 14:32 ` [PATCH v2 3/5] selinux: Move sidtab " peter.enderborg at sony.com
2018-01-26 14:32 ` [PATCH v2 4/5] selinux: Use pointer to switch policydb and sidtab peter.enderborg at sony.com
2018-01-30 14:37   ` Stephen Smalley [this message]
2018-02-08  7:16     ` peter enderborg
2018-02-08 15:10       ` Stephen Smalley
2018-01-26 14:32 ` [PATCH v2 5/5] selinux: Switch locking to RCU peter.enderborg at sony.com
2018-01-30 13:37 ` [PATCH v2 0/5] selinux:Significant reduce of preempt_disable holds Stephen Smalley

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=1517323047.14420.14.camel@tycho.nsa.gov \
    --to=sds@tycho.nsa.gov \
    --cc=linux-security-module@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).