diff options
Diffstat (limited to 'security/selinux/ss')
| -rw-r--r-- | security/selinux/ss/avtab.c | 101 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.h | 2 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.c | 12 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 220 | ||||
| -rw-r--r-- | security/selinux/ss/sidtab.c | 21 | ||||
| -rw-r--r-- | security/selinux/ss/sidtab.h | 4 | 
6 files changed, 224 insertions, 136 deletions
| diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 6dcb6aa4db7f..75df32906055 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -109,7 +109,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat  	struct avtab_node *prev, *cur, *newnode;  	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); -	if (!h) +	if (!h || !h->nslot)  		return -EINVAL;  	hvalue = avtab_hash(key, h->mask); @@ -154,7 +154,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu  	struct avtab_node *prev, *cur;  	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); -	if (!h) +	if (!h || !h->nslot)  		return NULL;  	hvalue = avtab_hash(key, h->mask);  	for (prev = NULL, cur = h->htable[hvalue]; @@ -184,7 +184,7 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)  	struct avtab_node *cur;  	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); -	if (!h) +	if (!h || !h->nslot)  		return NULL;  	hvalue = avtab_hash(key, h->mask); @@ -220,7 +220,7 @@ avtab_search_node(struct avtab *h, struct avtab_key *key)  	struct avtab_node *cur;  	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); -	if (!h) +	if (!h || !h->nslot)  		return NULL;  	hvalue = avtab_hash(key, h->mask); @@ -295,6 +295,7 @@ void avtab_destroy(struct avtab *h)  	}  	kvfree(h->htable);  	h->htable = NULL; +	h->nel = 0;  	h->nslot = 0;  	h->mask = 0;  } @@ -303,88 +304,52 @@ void avtab_init(struct avtab *h)  {  	h->htable = NULL;  	h->nel = 0; +	h->nslot = 0; +	h->mask = 0;  } -int avtab_alloc(struct avtab *h, u32 nrules) +static int avtab_alloc_common(struct avtab *h, u32 nslot)  { -	u32 mask = 0; -	u32 shift = 0; -	u32 work = nrules; -	u32 nslot = 0; - -	if (nrules == 0) -		goto avtab_alloc_out; - -	while (work) { -		work  = work >> 1; -		shift++; -	} -	if (shift > 2) -		shift = shift - 2; -	nslot = 1 << shift; -	if (nslot > MAX_AVTAB_HASH_BUCKETS) -		nslot = MAX_AVTAB_HASH_BUCKETS; -	mask = nslot - 1; +	if (!nslot) +		return 0;  	h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);  	if (!h->htable)  		return -ENOMEM; - avtab_alloc_out: -	h->nel = 0;  	h->nslot = nslot; -	h->mask = mask; -	pr_debug("SELinux: %d avtab hash slots, %d rules.\n", -	       h->nslot, nrules); +	h->mask = nslot - 1;  	return 0;  } -int avtab_duplicate(struct avtab *new, struct avtab *orig) +int avtab_alloc(struct avtab *h, u32 nrules)  { -	int i; -	struct avtab_node *node, *tmp, *tail; - -	memset(new, 0, sizeof(*new)); +	int rc; +	u32 nslot = 0; -	new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL); -	if (!new->htable) -		return -ENOMEM; -	new->nslot = orig->nslot; -	new->mask = orig->mask; - -	for (i = 0; i < orig->nslot; i++) { -		tail = NULL; -		for (node = orig->htable[i]; node; node = node->next) { -			tmp = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); -			if (!tmp) -				goto error; -			tmp->key = node->key; -			if (tmp->key.specified & AVTAB_XPERMS) { -				tmp->datum.u.xperms = -					kmem_cache_zalloc(avtab_xperms_cachep, -							GFP_KERNEL); -				if (!tmp->datum.u.xperms) { -					kmem_cache_free(avtab_node_cachep, tmp); -					goto error; -				} -				tmp->datum.u.xperms = node->datum.u.xperms; -			} else -				tmp->datum.u.data = node->datum.u.data; - -			if (tail) -				tail->next = tmp; -			else -				new->htable[i] = tmp; - -			tail = tmp; -			new->nel++; +	if (nrules != 0) { +		u32 shift = 1; +		u32 work = nrules >> 3; +		while (work) { +			work >>= 1; +			shift++;  		} +		nslot = 1 << shift; +		if (nslot > MAX_AVTAB_HASH_BUCKETS) +			nslot = MAX_AVTAB_HASH_BUCKETS; + +		rc = avtab_alloc_common(h, nslot); +		if (rc) +			return rc;  	} +	pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules);  	return 0; -error: -	avtab_destroy(new); -	return -ENOMEM; +} + +int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) +{ +	return avtab_alloc_common(new, orig->nslot);  }  void avtab_hash_eval(struct avtab *h, char *tag) diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 4c4445ca9118..f2eeb36265d1 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -89,7 +89,7 @@ struct avtab {  void avtab_init(struct avtab *h);  int avtab_alloc(struct avtab *, u32); -int avtab_duplicate(struct avtab *new, struct avtab *orig); +int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);  struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);  void avtab_destroy(struct avtab *h);  void avtab_hash_eval(struct avtab *h, char *tag); diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 0b32f3ab025e..1ef74c085f2b 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -605,7 +605,6 @@ static int cond_dup_av_list(struct cond_av_list *new,  			struct cond_av_list *orig,  			struct avtab *avtab)  { -	struct avtab_node *avnode;  	u32 i;  	memset(new, 0, sizeof(*new)); @@ -615,10 +614,11 @@ static int cond_dup_av_list(struct cond_av_list *new,  		return -ENOMEM;  	for (i = 0; i < orig->len; i++) { -		avnode = avtab_search_node(avtab, &orig->nodes[i]->key); -		if (WARN_ON(!avnode)) -			return -EINVAL; -		new->nodes[i] = avnode; +		new->nodes[i] = avtab_insert_nonunique(avtab, +						       &orig->nodes[i]->key, +						       &orig->nodes[i]->datum); +		if (!new->nodes[i]) +			return -ENOMEM;  		new->len++;  	} @@ -630,7 +630,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp,  {  	int rc, i, j; -	rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab); +	rc = avtab_alloc_dup(&newp->te_cond_avtab, &origp->te_cond_avtab);  	if (rc)  		return rc; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3438d0130378..301633145040 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -67,6 +67,17 @@  #include "policycap_names.h"  #include "ima.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, @@ -1541,6 +1552,7 @@ static int security_context_to_sid_core(struct selinux_state *state,  		if (!str)  			goto out;  	} +retry:  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -1554,6 +1566,15 @@ static int security_context_to_sid_core(struct selinux_state *state,  	} else if (rc)  		goto out_unlock;  	rc = sidtab_context_to_sid(sidtab, &context, sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		if (context.str) { +			str = context.str; +			context.str = NULL; +		} +		context_destroy(&context); +		goto retry; +	}  	context_destroy(&context);  out_unlock:  	rcu_read_unlock(); @@ -1703,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state,  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	struct class_datum *cladatum = NULL; +	struct class_datum *cladatum;  	struct context *scontext, *tcontext, newcontext;  	struct sidtab_entry *sentry, *tentry;  	struct avtab_key avkey; @@ -1725,6 +1746,8 @@ static int security_compute_sid(struct selinux_state *state,  		goto out;  	} +retry: +	cladatum = NULL;  	context_init(&newcontext);  	rcu_read_lock(); @@ -1869,6 +1892,11 @@ static int security_compute_sid(struct selinux_state *state,  	}  	/* Obtain the sid for the context. */  	rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		context_destroy(&newcontext); +		goto retry; +	}  out_unlock:  	rcu_read_unlock();  	context_destroy(&newcontext); @@ -1974,12 +2002,6 @@ static inline int convert_context_handle_invalid_context(  	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 @@ -2159,7 +2181,7 @@ static void selinux_policy_cond_free(struct selinux_policy *policy)  }  void selinux_policy_cancel(struct selinux_state *state, -			struct selinux_policy *policy) +			   struct selinux_load_state *load_state)  {  	struct selinux_policy *oldpolicy; @@ -2167,7 +2189,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, @@ -2183,9 +2206,10 @@ 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; +	unsigned long flags;  	u32 seqno;  	oldpolicy = rcu_dereference_protected(state->policy, @@ -2207,7 +2231,13 @@ void selinux_policy_commit(struct selinux_state *state,  	seqno = newpolicy->latest_granting;  	/* Install the new policy. */ -	rcu_assign_pointer(state->policy, newpolicy); +	if (oldpolicy) { +		sidtab_freeze_begin(oldpolicy->sidtab, &flags); +		rcu_assign_pointer(state->policy, newpolicy); +		sidtab_freeze_end(oldpolicy->sidtab, &flags); +	} else { +		rcu_assign_pointer(state->policy, newpolicy); +	}  	/* Load the policycaps from the new policy */  	security_load_policycaps(state, newpolicy); @@ -2225,6 +2255,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); @@ -2241,11 +2272,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; @@ -2275,10 +2305,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;  	} @@ -2292,29 +2322,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: @@ -2342,13 +2381,15 @@ int security_port_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_PORT;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2367,6 +2408,10 @@ int security_port_sid(struct selinux_state *state,  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2393,13 +2438,15 @@ int security_ib_pkey_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_UNLABELED;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2420,6 +2467,10 @@ int security_ib_pkey_sid(struct selinux_state *state,  			rc = sidtab_context_to_sid(sidtab,  						   &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2445,13 +2496,15 @@ int security_ib_endport_sid(struct selinux_state *state,  	struct policydb *policydb;  	struct sidtab *sidtab;  	struct ocontext *c; -	int rc = 0; +	int rc;  	if (!selinux_initialized(state)) {  		*out_sid = SECINITSID_UNLABELED;  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2472,6 +2525,10 @@ int security_ib_endport_sid(struct selinux_state *state,  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2495,7 +2552,7 @@ int security_netif_sid(struct selinux_state *state,  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	int rc = 0; +	int rc;  	struct ocontext *c;  	if (!selinux_initialized(state)) { @@ -2503,6 +2560,8 @@ int security_netif_sid(struct selinux_state *state,  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2519,10 +2578,18 @@ int security_netif_sid(struct selinux_state *state,  		if (!c->sid[0] || !c->sid[1]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  			rc = sidtab_context_to_sid(sidtab, &c->context[1],  						   &c->sid[1]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2572,6 +2639,7 @@ int security_node_sid(struct selinux_state *state,  		return 0;  	} +retry:  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2620,6 +2688,10 @@ int security_node_sid(struct selinux_state *state,  			rc = sidtab_context_to_sid(sidtab,  						   &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2661,18 +2733,24 @@ int security_get_user_sids(struct selinux_state *state,  	struct sidtab *sidtab;  	struct context *fromcon, usercon;  	u32 *mysids = NULL, *mysids2, sid; -	u32 mynel = 0, maxnel = SIDS_NEL; +	u32 i, j, mynel, maxnel = SIDS_NEL;  	struct user_datum *user;  	struct role_datum *role;  	struct ebitmap_node *rnode, *tnode; -	int rc = 0, i, j; +	int rc;  	*sids = NULL;  	*nel = 0;  	if (!selinux_initialized(state)) -		goto out; +		return 0; + +	mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL); +	if (!mysids) +		return -ENOMEM; +retry: +	mynel = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2692,11 +2770,6 @@ int security_get_user_sids(struct selinux_state *state,  	usercon.user = user->value; -	rc = -ENOMEM; -	mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC); -	if (!mysids) -		goto out_unlock; -  	ebitmap_for_each_positive_bit(&user->roles, rnode, i) {  		role = policydb->role_val_to_struct[i];  		usercon.role = i + 1; @@ -2708,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state,  				continue;  			rc = sidtab_context_to_sid(sidtab, &usercon, &sid); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out_unlock;  			if (mynel < maxnel) { @@ -2730,14 +2807,14 @@ out_unlock:  	rcu_read_unlock();  	if (rc || !mynel) {  		kfree(mysids); -		goto out; +		return rc;  	}  	rc = -ENOMEM;  	mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);  	if (!mysids2) {  		kfree(mysids); -		goto out; +		return rc;  	}  	for (i = 0, j = 0; i < mynel; i++) {  		struct av_decision dummy_avd; @@ -2750,12 +2827,10 @@ out_unlock:  			mysids2[j++] = mysids[i];  		cond_resched();  	} -	rc = 0;  	kfree(mysids);  	*sids = mysids2;  	*nel = j; -out: -	return rc; +	return 0;  }  /** @@ -2768,6 +2843,9 @@ out:   * Obtain a SID to use for a file in a filesystem that   * cannot support xattr or use a fixed labeling behavior like   * transition SIDs or task SIDs. + * + * WARNING: This function may return -ESTALE, indicating that the caller + * must retry the operation after re-acquiring the policy pointer!   */  static inline int __security_genfs_sid(struct selinux_policy *policy,  				       const char *fstype, @@ -2846,11 +2924,13 @@ int security_genfs_sid(struct selinux_state *state,  		return 0;  	} -	rcu_read_lock(); -	policy = rcu_dereference(state->policy); -	retval = __security_genfs_sid(policy, -				fstype, path, orig_sclass, sid); -	rcu_read_unlock(); +	do { +		rcu_read_lock(); +		policy = rcu_dereference(state->policy); +		retval = __security_genfs_sid(policy, fstype, path, +					      orig_sclass, sid); +		rcu_read_unlock(); +	} while (retval == -ESTALE);  	return retval;  } @@ -2873,7 +2953,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  	struct selinux_policy *policy;  	struct policydb *policydb;  	struct sidtab *sidtab; -	int rc = 0; +	int rc;  	struct ocontext *c;  	struct superblock_security_struct *sbsec = sb->s_security;  	const char *fstype = sb->s_type->name; @@ -2884,6 +2964,8 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -2901,6 +2983,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(sidtab, &c->context[0],  						   &c->sid[0]); +			if (rc == -ESTALE) { +				rcu_read_unlock(); +				goto retry; +			}  			if (rc)  				goto out;  		} @@ -2908,6 +2994,10 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)  	} else {  		rc = __security_genfs_sid(policy, fstype, "/",  					SECCLASS_DIR, &sbsec->sid); +		if (rc == -ESTALE) { +			rcu_read_unlock(); +			goto retry; +		}  		if (rc) {  			sbsec->behavior = SECURITY_FS_USE_NONE;  			rc = 0; @@ -3117,12 +3207,13 @@ int security_sid_mls_copy(struct selinux_state *state,  	u32 len;  	int rc; -	rc = 0;  	if (!selinux_initialized(state)) {  		*new_sid = sid; -		goto out; +		return 0;  	} +retry: +	rc = 0;  	context_init(&newcon);  	rcu_read_lock(); @@ -3181,10 +3272,14 @@ int security_sid_mls_copy(struct selinux_state *state,  		}  	}  	rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); +	if (rc == -ESTALE) { +		rcu_read_unlock(); +		context_destroy(&newcon); +		goto retry; +	}  out_unlock:  	rcu_read_unlock();  	context_destroy(&newcon); -out:  	return rc;  } @@ -3777,6 +3872,8 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,  		return 0;  	} +retry: +	rc = 0;  	rcu_read_lock();  	policy = rcu_dereference(state->policy);  	policydb = &policy->policydb; @@ -3803,23 +3900,24 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,  				goto out;  		}  		rc = -EIDRM; -		if (!mls_context_isvalid(policydb, &ctx_new)) -			goto out_free; +		if (!mls_context_isvalid(policydb, &ctx_new)) { +			ebitmap_destroy(&ctx_new.range.level[0].cat); +			goto out; +		}  		rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); +		ebitmap_destroy(&ctx_new.range.level[0].cat); +		if (rc == -ESTALE) { +			rcu_read_unlock(); +			goto retry; +		}  		if (rc) -			goto out_free; +			goto out;  		security_netlbl_cache_add(secattr, *sid); - -		ebitmap_destroy(&ctx_new.range.level[0].cat);  	} else  		*sid = SECSID_NULL; -	rcu_read_unlock(); -	return 0; -out_free: -	ebitmap_destroy(&ctx_new.range.level[0].cat);  out:  	rcu_read_unlock();  	return rc; diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 5ee190bd30f5..656d50b09f76 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -39,6 +39,7 @@ int sidtab_init(struct sidtab *s)  	for (i = 0; i < SECINITSID_NUM; i++)  		s->isids[i].set = 0; +	s->frozen = false;  	s->count = 0;  	s->convert = NULL;  	hash_init(s->context_to_sid); @@ -281,6 +282,15 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,  	if (*sid)  		goto out_unlock; +	if (unlikely(s->frozen)) { +		/* +		 * This sidtab is now frozen - tell the caller to abort and +		 * get the new one. +		 */ +		rc = -ESTALE; +		goto out_unlock; +	} +  	count = s->count;  	convert = s->convert; @@ -474,6 +484,17 @@ void sidtab_cancel_convert(struct sidtab *s)  	spin_unlock_irqrestore(&s->lock, flags);  } +void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock) +{ +	spin_lock_irqsave(&s->lock, *flags); +	s->frozen = true; +	s->convert = NULL; +} +void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock) +{ +	spin_unlock_irqrestore(&s->lock, *flags); +} +  static void sidtab_destroy_entry(struct sidtab_entry *entry)  {  	context_destroy(&entry->context); diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 80c744d07ad6..4eff0e49dcb2 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -86,6 +86,7 @@ struct sidtab {  	u32 count;  	/* access only under spinlock */  	struct sidtab_convert_params *convert; +	bool frozen;  	spinlock_t lock;  #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 @@ -125,6 +126,9 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);  void sidtab_cancel_convert(struct sidtab *s); +void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock); +void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock); +  int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);  void sidtab_destroy(struct sidtab *s); |