162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AppArmor security module
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file contains AppArmor label definitions
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2017 Canonical Ltd.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/audit.h>
1162306a36Sopenharmony_ci#include <linux/seq_file.h>
1262306a36Sopenharmony_ci#include <linux/sort.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "include/apparmor.h"
1562306a36Sopenharmony_ci#include "include/cred.h"
1662306a36Sopenharmony_ci#include "include/label.h"
1762306a36Sopenharmony_ci#include "include/policy.h"
1862306a36Sopenharmony_ci#include "include/secid.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * the aa_label represents the set of profiles confining an object
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Labels maintain a reference count to the set of pointers they reference
2562306a36Sopenharmony_ci * Labels are ref counted by
2662306a36Sopenharmony_ci *   tasks and object via the security field/security context off the field
2762306a36Sopenharmony_ci *   code - will take a ref count on a label if it needs the label
2862306a36Sopenharmony_ci *          beyond what is possible with an rcu_read_lock.
2962306a36Sopenharmony_ci *   profiles - each profile is a label
3062306a36Sopenharmony_ci *   secids - a pinned secid will keep a refcount of the label it is
3162306a36Sopenharmony_ci *          referencing
3262306a36Sopenharmony_ci *   objects - inode, files, sockets, ...
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Labels are not ref counted by the label set, so they maybe removed and
3562306a36Sopenharmony_ci * freed when no longer in use.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define PROXY_POISON 97
4062306a36Sopenharmony_ci#define LABEL_POISON 100
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void free_proxy(struct aa_proxy *proxy)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	if (proxy) {
4562306a36Sopenharmony_ci		/* p->label will not updated any more as p is dead */
4662306a36Sopenharmony_ci		aa_put_label(rcu_dereference_protected(proxy->label, true));
4762306a36Sopenharmony_ci		memset(proxy, 0, sizeof(*proxy));
4862306a36Sopenharmony_ci		RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
4962306a36Sopenharmony_ci		kfree(proxy);
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_civoid aa_proxy_kref(struct kref *kref)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	free_proxy(proxy);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct aa_proxy *new;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	new = kzalloc(sizeof(struct aa_proxy), gfp);
6562306a36Sopenharmony_ci	if (new) {
6662306a36Sopenharmony_ci		kref_init(&new->count);
6762306a36Sopenharmony_ci		rcu_assign_pointer(new->label, aa_get_label(label));
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	return new;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* requires profile list write lock held */
7362306a36Sopenharmony_civoid __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct aa_label *tmp;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	AA_BUG(!orig);
7862306a36Sopenharmony_ci	AA_BUG(!new);
7962306a36Sopenharmony_ci	lockdep_assert_held_write(&labels_set(orig)->lock);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	tmp = rcu_dereference_protected(orig->proxy->label,
8262306a36Sopenharmony_ci					&labels_ns(orig)->lock);
8362306a36Sopenharmony_ci	rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
8462306a36Sopenharmony_ci	orig->flags |= FLAG_STALE;
8562306a36Sopenharmony_ci	aa_put_label(tmp);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void __proxy_share(struct aa_label *old, struct aa_label *new)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct aa_proxy *proxy = new->proxy;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	new->proxy = aa_get_proxy(old->proxy);
9362306a36Sopenharmony_ci	__aa_proxy_redirect(old, new);
9462306a36Sopenharmony_ci	aa_put_proxy(proxy);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/**
9962306a36Sopenharmony_ci * ns_cmp - compare ns for label set ordering
10062306a36Sopenharmony_ci * @a: ns to compare (NOT NULL)
10162306a36Sopenharmony_ci * @b: ns to compare (NOT NULL)
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * Returns: <0 if a < b
10462306a36Sopenharmony_ci *          ==0 if a == b
10562306a36Sopenharmony_ci *          >0  if a > b
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_cistatic int ns_cmp(struct aa_ns *a, struct aa_ns *b)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int res;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	AA_BUG(!a);
11262306a36Sopenharmony_ci	AA_BUG(!b);
11362306a36Sopenharmony_ci	AA_BUG(!a->base.hname);
11462306a36Sopenharmony_ci	AA_BUG(!b->base.hname);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (a == b)
11762306a36Sopenharmony_ci		return 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	res = a->level - b->level;
12062306a36Sopenharmony_ci	if (res)
12162306a36Sopenharmony_ci		return res;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return strcmp(a->base.hname, b->base.hname);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * profile_cmp - profile comparison for set ordering
12862306a36Sopenharmony_ci * @a: profile to compare (NOT NULL)
12962306a36Sopenharmony_ci * @b: profile to compare (NOT NULL)
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * Returns: <0  if a < b
13262306a36Sopenharmony_ci *          ==0 if a == b
13362306a36Sopenharmony_ci *          >0  if a > b
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistatic int profile_cmp(struct aa_profile *a, struct aa_profile *b)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	int res;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	AA_BUG(!a);
14062306a36Sopenharmony_ci	AA_BUG(!b);
14162306a36Sopenharmony_ci	AA_BUG(!a->ns);
14262306a36Sopenharmony_ci	AA_BUG(!b->ns);
14362306a36Sopenharmony_ci	AA_BUG(!a->base.hname);
14462306a36Sopenharmony_ci	AA_BUG(!b->base.hname);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (a == b || a->base.hname == b->base.hname)
14762306a36Sopenharmony_ci		return 0;
14862306a36Sopenharmony_ci	res = ns_cmp(a->ns, b->ns);
14962306a36Sopenharmony_ci	if (res)
15062306a36Sopenharmony_ci		return res;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return strcmp(a->base.hname, b->base.hname);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * vec_cmp - label comparison for set ordering
15762306a36Sopenharmony_ci * @a: label to compare (NOT NULL)
15862306a36Sopenharmony_ci * @vec: vector of profiles to compare (NOT NULL)
15962306a36Sopenharmony_ci * @n: length of @vec
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Returns: <0  if a < vec
16262306a36Sopenharmony_ci *          ==0 if a == vec
16362306a36Sopenharmony_ci *          >0  if a > vec
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic int vec_cmp(struct aa_profile **a, int an, struct aa_profile **b, int bn)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	int i;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	AA_BUG(!a);
17062306a36Sopenharmony_ci	AA_BUG(!*a);
17162306a36Sopenharmony_ci	AA_BUG(!b);
17262306a36Sopenharmony_ci	AA_BUG(!*b);
17362306a36Sopenharmony_ci	AA_BUG(an <= 0);
17462306a36Sopenharmony_ci	AA_BUG(bn <= 0);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	for (i = 0; i < an && i < bn; i++) {
17762306a36Sopenharmony_ci		int res = profile_cmp(a[i], b[i]);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		if (res != 0)
18062306a36Sopenharmony_ci			return res;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return an - bn;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic bool vec_is_stale(struct aa_profile **vec, int n)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	AA_BUG(!vec);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
19362306a36Sopenharmony_ci		if (profile_is_stale(vec[i]))
19462306a36Sopenharmony_ci			return true;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return false;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic long accum_vec_flags(struct aa_profile **vec, int n)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	long u = FLAG_UNCONFINED;
20362306a36Sopenharmony_ci	int i;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	AA_BUG(!vec);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
20862306a36Sopenharmony_ci		u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 |
20962306a36Sopenharmony_ci					    FLAG_STALE);
21062306a36Sopenharmony_ci		if (!(u & vec[i]->label.flags & FLAG_UNCONFINED))
21162306a36Sopenharmony_ci			u &= ~FLAG_UNCONFINED;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return u;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int sort_cmp(const void *a, const void *b)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return profile_cmp(*(struct aa_profile **)a, *(struct aa_profile **)b);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * assumes vec is sorted
22462306a36Sopenharmony_ci * Assumes @vec has null terminator at vec[n], and will null terminate
22562306a36Sopenharmony_ci * vec[n - dups]
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_cistatic inline int unique(struct aa_profile **vec, int n)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	int i, pos, dups = 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	AA_BUG(n < 1);
23262306a36Sopenharmony_ci	AA_BUG(!vec);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	pos = 0;
23562306a36Sopenharmony_ci	for (i = 1; i < n; i++) {
23662306a36Sopenharmony_ci		int res = profile_cmp(vec[pos], vec[i]);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		AA_BUG(res > 0, "vec not sorted");
23962306a36Sopenharmony_ci		if (res == 0) {
24062306a36Sopenharmony_ci			/* drop duplicate */
24162306a36Sopenharmony_ci			aa_put_profile(vec[i]);
24262306a36Sopenharmony_ci			dups++;
24362306a36Sopenharmony_ci			continue;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci		pos++;
24662306a36Sopenharmony_ci		if (dups)
24762306a36Sopenharmony_ci			vec[pos] = vec[i];
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	AA_BUG(dups < 0);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return dups;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/**
25662306a36Sopenharmony_ci * aa_vec_unique - canonical sort and unique a list of profiles
25762306a36Sopenharmony_ci * @n: number of refcounted profiles in the list (@n > 0)
25862306a36Sopenharmony_ci * @vec: list of profiles to sort and merge
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * Returns: the number of duplicates eliminated == references put
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * If @flags & VEC_FLAG_TERMINATE @vec has null terminator at vec[n], and will
26362306a36Sopenharmony_ci * null terminate vec[n - dups]
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ciint aa_vec_unique(struct aa_profile **vec, int n, int flags)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int i, dups = 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	AA_BUG(n < 1);
27062306a36Sopenharmony_ci	AA_BUG(!vec);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* vecs are usually small and inorder, have a fallback for larger */
27362306a36Sopenharmony_ci	if (n > 8) {
27462306a36Sopenharmony_ci		sort(vec, n, sizeof(struct aa_profile *), sort_cmp, NULL);
27562306a36Sopenharmony_ci		dups = unique(vec, n);
27662306a36Sopenharmony_ci		goto out;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* insertion sort + unique in one */
28062306a36Sopenharmony_ci	for (i = 1; i < n; i++) {
28162306a36Sopenharmony_ci		struct aa_profile *tmp = vec[i];
28262306a36Sopenharmony_ci		int pos, j;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		for (pos = i - 1 - dups; pos >= 0; pos--) {
28562306a36Sopenharmony_ci			int res = profile_cmp(vec[pos], tmp);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci			if (res == 0) {
28862306a36Sopenharmony_ci				/* drop duplicate entry */
28962306a36Sopenharmony_ci				aa_put_profile(tmp);
29062306a36Sopenharmony_ci				dups++;
29162306a36Sopenharmony_ci				goto continue_outer;
29262306a36Sopenharmony_ci			} else if (res < 0)
29362306a36Sopenharmony_ci				break;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		/* pos is at entry < tmp, or index -1. Set to insert pos */
29662306a36Sopenharmony_ci		pos++;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		for (j = i - dups; j > pos; j--)
29962306a36Sopenharmony_ci			vec[j] = vec[j - 1];
30062306a36Sopenharmony_ci		vec[pos] = tmp;
30162306a36Sopenharmony_cicontinue_outer:
30262306a36Sopenharmony_ci		;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	AA_BUG(dups < 0);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciout:
30862306a36Sopenharmony_ci	if (flags & VEC_FLAG_TERMINATE)
30962306a36Sopenharmony_ci		vec[n - dups] = NULL;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return dups;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_civoid aa_label_destroy(struct aa_label *label)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	AA_BUG(!label);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (!label_isprofile(label)) {
32062306a36Sopenharmony_ci		struct aa_profile *profile;
32162306a36Sopenharmony_ci		struct label_it i;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		aa_put_str(label->hname);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		label_for_each(i, label, profile) {
32662306a36Sopenharmony_ci			aa_put_profile(profile);
32762306a36Sopenharmony_ci			label->vec[i.i] = (struct aa_profile *)
32862306a36Sopenharmony_ci					   (LABEL_POISON + (long) i.i);
32962306a36Sopenharmony_ci		}
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (label->proxy) {
33362306a36Sopenharmony_ci		if (rcu_dereference_protected(label->proxy->label, true) == label)
33462306a36Sopenharmony_ci			rcu_assign_pointer(label->proxy->label, NULL);
33562306a36Sopenharmony_ci		aa_put_proxy(label->proxy);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	aa_free_secid(label->secid);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	label->proxy = (struct aa_proxy *) PROXY_POISON + 1;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_civoid aa_label_free(struct aa_label *label)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	if (!label)
34562306a36Sopenharmony_ci		return;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	aa_label_destroy(label);
34862306a36Sopenharmony_ci	kfree(label);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void label_free_switch(struct aa_label *label)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	if (label->flags & FLAG_NS_COUNT)
35462306a36Sopenharmony_ci		aa_free_ns(labels_ns(label));
35562306a36Sopenharmony_ci	else if (label_isprofile(label))
35662306a36Sopenharmony_ci		aa_free_profile(labels_profile(label));
35762306a36Sopenharmony_ci	else
35862306a36Sopenharmony_ci		aa_label_free(label);
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic void label_free_rcu(struct rcu_head *head)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct aa_label *label = container_of(head, struct aa_label, rcu);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (label->flags & FLAG_IN_TREE)
36662306a36Sopenharmony_ci		(void) aa_label_remove(label);
36762306a36Sopenharmony_ci	label_free_switch(label);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_civoid aa_label_kref(struct kref *kref)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct aa_label *label = container_of(kref, struct aa_label, count);
37362306a36Sopenharmony_ci	struct aa_ns *ns = labels_ns(label);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!ns) {
37662306a36Sopenharmony_ci		/* never live, no rcu callback needed, just using the fn */
37762306a36Sopenharmony_ci		label_free_switch(label);
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	/* TODO: update labels_profile macro so it works here */
38162306a36Sopenharmony_ci	AA_BUG(label_isprofile(label) &&
38262306a36Sopenharmony_ci	       on_list_rcu(&label->vec[0]->base.profiles));
38362306a36Sopenharmony_ci	AA_BUG(label_isprofile(label) &&
38462306a36Sopenharmony_ci	       on_list_rcu(&label->vec[0]->base.list));
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* TODO: if compound label and not stale add to reclaim cache */
38762306a36Sopenharmony_ci	call_rcu(&label->rcu, label_free_rcu);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void label_free_or_put_new(struct aa_label *label, struct aa_label *new)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	if (label != new)
39362306a36Sopenharmony_ci		/* need to free directly to break circular ref with proxy */
39462306a36Sopenharmony_ci		aa_label_free(new);
39562306a36Sopenharmony_ci	else
39662306a36Sopenharmony_ci		aa_put_label(new);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cibool aa_label_init(struct aa_label *label, int size, gfp_t gfp)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	AA_BUG(!label);
40262306a36Sopenharmony_ci	AA_BUG(size < 1);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (aa_alloc_secid(label, gfp) < 0)
40562306a36Sopenharmony_ci		return false;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	label->size = size;			/* doesn't include null */
40862306a36Sopenharmony_ci	label->vec[size] = NULL;		/* null terminate */
40962306a36Sopenharmony_ci	kref_init(&label->count);
41062306a36Sopenharmony_ci	RB_CLEAR_NODE(&label->node);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return true;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * aa_label_alloc - allocate a label with a profile vector of @size length
41762306a36Sopenharmony_ci * @size: size of profile vector in the label
41862306a36Sopenharmony_ci * @proxy: proxy to use OR null if to allocate a new one
41962306a36Sopenharmony_ci * @gfp: memory allocation type
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci * Returns: new label
42262306a36Sopenharmony_ci *     else NULL if failed
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistruct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct aa_label *new;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	AA_BUG(size < 1);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/*  + 1 for null terminator entry on vec */
43162306a36Sopenharmony_ci	new = kzalloc(struct_size(new, vec, size + 1), gfp);
43262306a36Sopenharmony_ci	AA_DEBUG("%s (%p)\n", __func__, new);
43362306a36Sopenharmony_ci	if (!new)
43462306a36Sopenharmony_ci		goto fail;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!aa_label_init(new, size, gfp))
43762306a36Sopenharmony_ci		goto fail;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (!proxy) {
44062306a36Sopenharmony_ci		proxy = aa_alloc_proxy(new, gfp);
44162306a36Sopenharmony_ci		if (!proxy)
44262306a36Sopenharmony_ci			goto fail;
44362306a36Sopenharmony_ci	} else
44462306a36Sopenharmony_ci		aa_get_proxy(proxy);
44562306a36Sopenharmony_ci	/* just set new's proxy, don't redirect proxy here if it was passed in*/
44662306a36Sopenharmony_ci	new->proxy = proxy;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return new;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cifail:
45162306a36Sopenharmony_ci	kfree(new);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return NULL;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/**
45862306a36Sopenharmony_ci * label_cmp - label comparison for set ordering
45962306a36Sopenharmony_ci * @a: label to compare (NOT NULL)
46062306a36Sopenharmony_ci * @b: label to compare (NOT NULL)
46162306a36Sopenharmony_ci *
46262306a36Sopenharmony_ci * Returns: <0  if a < b
46362306a36Sopenharmony_ci *          ==0 if a == b
46462306a36Sopenharmony_ci *          >0  if a > b
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_cistatic int label_cmp(struct aa_label *a, struct aa_label *b)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	AA_BUG(!b);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (a == b)
47162306a36Sopenharmony_ci		return 0;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return vec_cmp(a->vec, a->size, b->vec, b->size);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/* helper fn for label_for_each_confined */
47762306a36Sopenharmony_ciint aa_label_next_confined(struct aa_label *label, int i)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	AA_BUG(!label);
48062306a36Sopenharmony_ci	AA_BUG(i < 0);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	for (; i < label->size; i++) {
48362306a36Sopenharmony_ci		if (!profile_unconfined(label->vec[i]))
48462306a36Sopenharmony_ci			return i;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return i;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/**
49162306a36Sopenharmony_ci * __aa_label_next_not_in_set - return the next profile of @sub not in @set
49262306a36Sopenharmony_ci * @I: label iterator
49362306a36Sopenharmony_ci * @set: label to test against
49462306a36Sopenharmony_ci * @sub: label to if is subset of @set
49562306a36Sopenharmony_ci *
49662306a36Sopenharmony_ci * Returns: profile in @sub that is not in @set, with iterator set pos after
49762306a36Sopenharmony_ci *     else NULL if @sub is a subset of @set
49862306a36Sopenharmony_ci */
49962306a36Sopenharmony_cistruct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
50062306a36Sopenharmony_ci					      struct aa_label *set,
50162306a36Sopenharmony_ci					      struct aa_label *sub)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	AA_BUG(!set);
50462306a36Sopenharmony_ci	AA_BUG(!I);
50562306a36Sopenharmony_ci	AA_BUG(I->i < 0);
50662306a36Sopenharmony_ci	AA_BUG(I->i > set->size);
50762306a36Sopenharmony_ci	AA_BUG(!sub);
50862306a36Sopenharmony_ci	AA_BUG(I->j < 0);
50962306a36Sopenharmony_ci	AA_BUG(I->j > sub->size);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	while (I->j < sub->size && I->i < set->size) {
51262306a36Sopenharmony_ci		int res = profile_cmp(sub->vec[I->j], set->vec[I->i]);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (res == 0) {
51562306a36Sopenharmony_ci			(I->j)++;
51662306a36Sopenharmony_ci			(I->i)++;
51762306a36Sopenharmony_ci		} else if (res > 0)
51862306a36Sopenharmony_ci			(I->i)++;
51962306a36Sopenharmony_ci		else
52062306a36Sopenharmony_ci			return sub->vec[(I->j)++];
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (I->j < sub->size)
52462306a36Sopenharmony_ci		return sub->vec[(I->j)++];
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return NULL;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/**
53062306a36Sopenharmony_ci * aa_label_is_subset - test if @sub is a subset of @set
53162306a36Sopenharmony_ci * @set: label to test against
53262306a36Sopenharmony_ci * @sub: label to test if is subset of @set
53362306a36Sopenharmony_ci *
53462306a36Sopenharmony_ci * Returns: true if @sub is subset of @set
53562306a36Sopenharmony_ci *     else false
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_cibool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct label_it i = { };
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	AA_BUG(!set);
54262306a36Sopenharmony_ci	AA_BUG(!sub);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (sub == set)
54562306a36Sopenharmony_ci		return true;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return __aa_label_next_not_in_set(&i, set, sub) == NULL;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/**
55162306a36Sopenharmony_ci * aa_label_is_unconfined_subset - test if @sub is a subset of @set
55262306a36Sopenharmony_ci * @set: label to test against
55362306a36Sopenharmony_ci * @sub: label to test if is subset of @set
55462306a36Sopenharmony_ci *
55562306a36Sopenharmony_ci * This checks for subset but taking into account unconfined. IF
55662306a36Sopenharmony_ci * @sub contains an unconfined profile that does not have a matching
55762306a36Sopenharmony_ci * unconfined in @set then this will not cause the test to fail.
55862306a36Sopenharmony_ci * Conversely we don't care about an unconfined in @set that is not in
55962306a36Sopenharmony_ci * @sub
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * Returns: true if @sub is special_subset of @set
56262306a36Sopenharmony_ci *     else false
56362306a36Sopenharmony_ci */
56462306a36Sopenharmony_cibool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct label_it i = { };
56762306a36Sopenharmony_ci	struct aa_profile *p;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	AA_BUG(!set);
57062306a36Sopenharmony_ci	AA_BUG(!sub);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (sub == set)
57362306a36Sopenharmony_ci		return true;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	do {
57662306a36Sopenharmony_ci		p = __aa_label_next_not_in_set(&i, set, sub);
57762306a36Sopenharmony_ci		if (p && !profile_unconfined(p))
57862306a36Sopenharmony_ci			break;
57962306a36Sopenharmony_ci	} while (p);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return p == NULL;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * __label_remove - remove @label from the label set
58762306a36Sopenharmony_ci * @l: label to remove
58862306a36Sopenharmony_ci * @new: label to redirect to
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * Requires: labels_set(@label)->lock write_lock
59162306a36Sopenharmony_ci * Returns:  true if the label was in the tree and removed
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic bool __label_remove(struct aa_label *label, struct aa_label *new)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct aa_labelset *ls = labels_set(label);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	AA_BUG(!ls);
59862306a36Sopenharmony_ci	AA_BUG(!label);
59962306a36Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (new)
60262306a36Sopenharmony_ci		__aa_proxy_redirect(label, new);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (!label_is_stale(label))
60562306a36Sopenharmony_ci		__label_make_stale(label);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (label->flags & FLAG_IN_TREE) {
60862306a36Sopenharmony_ci		rb_erase(&label->node, &ls->root);
60962306a36Sopenharmony_ci		label->flags &= ~FLAG_IN_TREE;
61062306a36Sopenharmony_ci		return true;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return false;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci/**
61762306a36Sopenharmony_ci * __label_replace - replace @old with @new in label set
61862306a36Sopenharmony_ci * @old: label to remove from label set
61962306a36Sopenharmony_ci * @new: label to replace @old with
62062306a36Sopenharmony_ci *
62162306a36Sopenharmony_ci * Requires: labels_set(@old)->lock write_lock
62262306a36Sopenharmony_ci *           valid ref count be held on @new
62362306a36Sopenharmony_ci * Returns: true if @old was in set and replaced by @new
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci * Note: current implementation requires label set be order in such a way
62662306a36Sopenharmony_ci *       that @new directly replaces @old position in the set (ie.
62762306a36Sopenharmony_ci *       using pointer comparison of the label address would not work)
62862306a36Sopenharmony_ci */
62962306a36Sopenharmony_cistatic bool __label_replace(struct aa_label *old, struct aa_label *new)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct aa_labelset *ls = labels_set(old);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	AA_BUG(!ls);
63462306a36Sopenharmony_ci	AA_BUG(!old);
63562306a36Sopenharmony_ci	AA_BUG(!new);
63662306a36Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
63762306a36Sopenharmony_ci	AA_BUG(new->flags & FLAG_IN_TREE);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (!label_is_stale(old))
64062306a36Sopenharmony_ci		__label_make_stale(old);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (old->flags & FLAG_IN_TREE) {
64362306a36Sopenharmony_ci		rb_replace_node(&old->node, &new->node, &ls->root);
64462306a36Sopenharmony_ci		old->flags &= ~FLAG_IN_TREE;
64562306a36Sopenharmony_ci		new->flags |= FLAG_IN_TREE;
64662306a36Sopenharmony_ci		return true;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return false;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/**
65362306a36Sopenharmony_ci * __label_insert - attempt to insert @l into a label set
65462306a36Sopenharmony_ci * @ls: set of labels to insert @l into (NOT NULL)
65562306a36Sopenharmony_ci * @label: new label to insert (NOT NULL)
65662306a36Sopenharmony_ci * @replace: whether insertion should replace existing entry that is not stale
65762306a36Sopenharmony_ci *
65862306a36Sopenharmony_ci * Requires: @ls->lock
65962306a36Sopenharmony_ci *           caller to hold a valid ref on l
66062306a36Sopenharmony_ci *           if @replace is true l has a preallocated proxy associated
66162306a36Sopenharmony_ci * Returns: @l if successful in inserting @l - with additional refcount
66262306a36Sopenharmony_ci *          else ref counted equivalent label that is already in the set,
66362306a36Sopenharmony_ci *          the else condition only happens if @replace is false
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_cistatic struct aa_label *__label_insert(struct aa_labelset *ls,
66662306a36Sopenharmony_ci				       struct aa_label *label, bool replace)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct rb_node **new, *parent = NULL;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	AA_BUG(!ls);
67162306a36Sopenharmony_ci	AA_BUG(!label);
67262306a36Sopenharmony_ci	AA_BUG(labels_set(label) != ls);
67362306a36Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
67462306a36Sopenharmony_ci	AA_BUG(label->flags & FLAG_IN_TREE);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Figure out where to put new node */
67762306a36Sopenharmony_ci	new = &ls->root.rb_node;
67862306a36Sopenharmony_ci	while (*new) {
67962306a36Sopenharmony_ci		struct aa_label *this = rb_entry(*new, struct aa_label, node);
68062306a36Sopenharmony_ci		int result = label_cmp(label, this);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		parent = *new;
68362306a36Sopenharmony_ci		if (result == 0) {
68462306a36Sopenharmony_ci			/* !__aa_get_label means queued for destruction,
68562306a36Sopenharmony_ci			 * so replace in place, however the label has
68662306a36Sopenharmony_ci			 * died before the replacement so do not share
68762306a36Sopenharmony_ci			 * the proxy
68862306a36Sopenharmony_ci			 */
68962306a36Sopenharmony_ci			if (!replace && !label_is_stale(this)) {
69062306a36Sopenharmony_ci				if (__aa_get_label(this))
69162306a36Sopenharmony_ci					return this;
69262306a36Sopenharmony_ci			} else
69362306a36Sopenharmony_ci				__proxy_share(this, label);
69462306a36Sopenharmony_ci			AA_BUG(!__label_replace(this, label));
69562306a36Sopenharmony_ci			return aa_get_label(label);
69662306a36Sopenharmony_ci		} else if (result < 0)
69762306a36Sopenharmony_ci			new = &((*new)->rb_left);
69862306a36Sopenharmony_ci		else /* (result > 0) */
69962306a36Sopenharmony_ci			new = &((*new)->rb_right);
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* Add new node and rebalance tree. */
70362306a36Sopenharmony_ci	rb_link_node(&label->node, parent, new);
70462306a36Sopenharmony_ci	rb_insert_color(&label->node, &ls->root);
70562306a36Sopenharmony_ci	label->flags |= FLAG_IN_TREE;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return aa_get_label(label);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/**
71162306a36Sopenharmony_ci * __vec_find - find label that matches @vec in label set
71262306a36Sopenharmony_ci * @vec: vec of profiles to find matching label for (NOT NULL)
71362306a36Sopenharmony_ci * @n: length of @vec
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * Requires: @vec_labelset(vec) lock held
71662306a36Sopenharmony_ci *           caller to hold a valid ref on l
71762306a36Sopenharmony_ci *
71862306a36Sopenharmony_ci * Returns: ref counted @label if matching label is in tree
71962306a36Sopenharmony_ci *          ref counted label that is equiv to @l in tree
72062306a36Sopenharmony_ci *     else NULL if @vec equiv is not in tree
72162306a36Sopenharmony_ci */
72262306a36Sopenharmony_cistatic struct aa_label *__vec_find(struct aa_profile **vec, int n)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct rb_node *node;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	AA_BUG(!vec);
72762306a36Sopenharmony_ci	AA_BUG(!*vec);
72862306a36Sopenharmony_ci	AA_BUG(n <= 0);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	node = vec_labelset(vec, n)->root.rb_node;
73162306a36Sopenharmony_ci	while (node) {
73262306a36Sopenharmony_ci		struct aa_label *this = rb_entry(node, struct aa_label, node);
73362306a36Sopenharmony_ci		int result = vec_cmp(this->vec, this->size, vec, n);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (result > 0)
73662306a36Sopenharmony_ci			node = node->rb_left;
73762306a36Sopenharmony_ci		else if (result < 0)
73862306a36Sopenharmony_ci			node = node->rb_right;
73962306a36Sopenharmony_ci		else
74062306a36Sopenharmony_ci			return __aa_get_label(this);
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return NULL;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci/**
74762306a36Sopenharmony_ci * __label_find - find label @label in label set
74862306a36Sopenharmony_ci * @label: label to find (NOT NULL)
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * Requires: labels_set(@label)->lock held
75162306a36Sopenharmony_ci *           caller to hold a valid ref on l
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * Returns: ref counted @label if @label is in tree OR
75462306a36Sopenharmony_ci *          ref counted label that is equiv to @label in tree
75562306a36Sopenharmony_ci *     else NULL if @label or equiv is not in tree
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_cistatic struct aa_label *__label_find(struct aa_label *label)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	AA_BUG(!label);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return __vec_find(label->vec, label->size);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/**
76662306a36Sopenharmony_ci * aa_label_remove - remove a label from the labelset
76762306a36Sopenharmony_ci * @label: label to remove
76862306a36Sopenharmony_ci *
76962306a36Sopenharmony_ci * Returns: true if @label was removed from the tree
77062306a36Sopenharmony_ci *     else @label was not in tree so it could not be removed
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cibool aa_label_remove(struct aa_label *label)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct aa_labelset *ls = labels_set(label);
77562306a36Sopenharmony_ci	unsigned long flags;
77662306a36Sopenharmony_ci	bool res;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	AA_BUG(!ls);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
78162306a36Sopenharmony_ci	res = __label_remove(label, ns_unconfined(labels_ns(label)));
78262306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return res;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/**
78862306a36Sopenharmony_ci * aa_label_replace - replace a label @old with a new version @new
78962306a36Sopenharmony_ci * @old: label to replace
79062306a36Sopenharmony_ci * @new: label replacing @old
79162306a36Sopenharmony_ci *
79262306a36Sopenharmony_ci * Returns: true if @old was in tree and replaced
79362306a36Sopenharmony_ci *     else @old was not in tree, and @new was not inserted
79462306a36Sopenharmony_ci */
79562306a36Sopenharmony_cibool aa_label_replace(struct aa_label *old, struct aa_label *new)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	unsigned long flags;
79862306a36Sopenharmony_ci	bool res;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (name_is_shared(old, new) && labels_ns(old) == labels_ns(new)) {
80162306a36Sopenharmony_ci		write_lock_irqsave(&labels_set(old)->lock, flags);
80262306a36Sopenharmony_ci		if (old->proxy != new->proxy)
80362306a36Sopenharmony_ci			__proxy_share(old, new);
80462306a36Sopenharmony_ci		else
80562306a36Sopenharmony_ci			__aa_proxy_redirect(old, new);
80662306a36Sopenharmony_ci		res = __label_replace(old, new);
80762306a36Sopenharmony_ci		write_unlock_irqrestore(&labels_set(old)->lock, flags);
80862306a36Sopenharmony_ci	} else {
80962306a36Sopenharmony_ci		struct aa_label *l;
81062306a36Sopenharmony_ci		struct aa_labelset *ls = labels_set(old);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		write_lock_irqsave(&ls->lock, flags);
81362306a36Sopenharmony_ci		res = __label_remove(old, new);
81462306a36Sopenharmony_ci		if (labels_ns(old) != labels_ns(new)) {
81562306a36Sopenharmony_ci			write_unlock_irqrestore(&ls->lock, flags);
81662306a36Sopenharmony_ci			ls = labels_set(new);
81762306a36Sopenharmony_ci			write_lock_irqsave(&ls->lock, flags);
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci		l = __label_insert(ls, new, true);
82062306a36Sopenharmony_ci		res = (l == new);
82162306a36Sopenharmony_ci		write_unlock_irqrestore(&ls->lock, flags);
82262306a36Sopenharmony_ci		aa_put_label(l);
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return res;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci/**
82962306a36Sopenharmony_ci * vec_find - find label @l in label set
83062306a36Sopenharmony_ci * @vec: array of profiles to find equiv label for (NOT NULL)
83162306a36Sopenharmony_ci * @n: length of @vec
83262306a36Sopenharmony_ci *
83362306a36Sopenharmony_ci * Returns: refcounted label if @vec equiv is in tree
83462306a36Sopenharmony_ci *     else NULL if @vec equiv is not in tree
83562306a36Sopenharmony_ci */
83662306a36Sopenharmony_cistatic struct aa_label *vec_find(struct aa_profile **vec, int n)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct aa_labelset *ls;
83962306a36Sopenharmony_ci	struct aa_label *label;
84062306a36Sopenharmony_ci	unsigned long flags;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	AA_BUG(!vec);
84362306a36Sopenharmony_ci	AA_BUG(!*vec);
84462306a36Sopenharmony_ci	AA_BUG(n <= 0);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ls = vec_labelset(vec, n);
84762306a36Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
84862306a36Sopenharmony_ci	label = __vec_find(vec, n);
84962306a36Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return label;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/* requires sort and merge done first */
85562306a36Sopenharmony_cistatic struct aa_label *vec_create_and_insert_label(struct aa_profile **vec,
85662306a36Sopenharmony_ci						    int len, gfp_t gfp)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct aa_label *label = NULL;
85962306a36Sopenharmony_ci	struct aa_labelset *ls;
86062306a36Sopenharmony_ci	unsigned long flags;
86162306a36Sopenharmony_ci	struct aa_label *new;
86262306a36Sopenharmony_ci	int i;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	AA_BUG(!vec);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (len == 1)
86762306a36Sopenharmony_ci		return aa_get_label(&vec[0]->label);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	ls = labels_set(&vec[len - 1]->label);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* TODO: enable when read side is lockless
87262306a36Sopenharmony_ci	 * check if label exists before taking locks
87362306a36Sopenharmony_ci	 */
87462306a36Sopenharmony_ci	new = aa_label_alloc(len, NULL, gfp);
87562306a36Sopenharmony_ci	if (!new)
87662306a36Sopenharmony_ci		return NULL;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	for (i = 0; i < len; i++)
87962306a36Sopenharmony_ci		new->vec[i] = aa_get_profile(vec[i]);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
88262306a36Sopenharmony_ci	label = __label_insert(ls, new, false);
88362306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
88462306a36Sopenharmony_ci	label_free_or_put_new(label, new);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return label;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistruct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
89062306a36Sopenharmony_ci					     gfp_t gfp)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct aa_label *label = vec_find(vec, len);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (label)
89562306a36Sopenharmony_ci		return label;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return vec_create_and_insert_label(vec, len, gfp);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/**
90162306a36Sopenharmony_ci * aa_label_find - find label @label in label set
90262306a36Sopenharmony_ci * @label: label to find (NOT NULL)
90362306a36Sopenharmony_ci *
90462306a36Sopenharmony_ci * Requires: caller to hold a valid ref on l
90562306a36Sopenharmony_ci *
90662306a36Sopenharmony_ci * Returns: refcounted @label if @label is in tree
90762306a36Sopenharmony_ci *          refcounted label that is equiv to @label in tree
90862306a36Sopenharmony_ci *     else NULL if @label or equiv is not in tree
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_cistruct aa_label *aa_label_find(struct aa_label *label)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	AA_BUG(!label);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return vec_find(label->vec, label->size);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * aa_label_insert - insert label @label into @ls or return existing label
92062306a36Sopenharmony_ci * @ls - labelset to insert @label into
92162306a36Sopenharmony_ci * @label - label to insert
92262306a36Sopenharmony_ci *
92362306a36Sopenharmony_ci * Requires: caller to hold a valid ref on @label
92462306a36Sopenharmony_ci *
92562306a36Sopenharmony_ci * Returns: ref counted @label if successful in inserting @label
92662306a36Sopenharmony_ci *     else ref counted equivalent label that is already in the set
92762306a36Sopenharmony_ci */
92862306a36Sopenharmony_cistruct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *label)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct aa_label *l;
93162306a36Sopenharmony_ci	unsigned long flags;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	AA_BUG(!ls);
93462306a36Sopenharmony_ci	AA_BUG(!label);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* check if label exists before taking lock */
93762306a36Sopenharmony_ci	if (!label_is_stale(label)) {
93862306a36Sopenharmony_ci		read_lock_irqsave(&ls->lock, flags);
93962306a36Sopenharmony_ci		l = __label_find(label);
94062306a36Sopenharmony_ci		read_unlock_irqrestore(&ls->lock, flags);
94162306a36Sopenharmony_ci		if (l)
94262306a36Sopenharmony_ci			return l;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
94662306a36Sopenharmony_ci	l = __label_insert(ls, label, false);
94762306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	return l;
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci/**
95462306a36Sopenharmony_ci * aa_label_next_in_merge - find the next profile when merging @a and @b
95562306a36Sopenharmony_ci * @I: label iterator
95662306a36Sopenharmony_ci * @a: label to merge
95762306a36Sopenharmony_ci * @b: label to merge
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * Returns: next profile
96062306a36Sopenharmony_ci *     else null if no more profiles
96162306a36Sopenharmony_ci */
96262306a36Sopenharmony_cistruct aa_profile *aa_label_next_in_merge(struct label_it *I,
96362306a36Sopenharmony_ci					  struct aa_label *a,
96462306a36Sopenharmony_ci					  struct aa_label *b)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	AA_BUG(!a);
96762306a36Sopenharmony_ci	AA_BUG(!b);
96862306a36Sopenharmony_ci	AA_BUG(!I);
96962306a36Sopenharmony_ci	AA_BUG(I->i < 0);
97062306a36Sopenharmony_ci	AA_BUG(I->i > a->size);
97162306a36Sopenharmony_ci	AA_BUG(I->j < 0);
97262306a36Sopenharmony_ci	AA_BUG(I->j > b->size);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (I->i < a->size) {
97562306a36Sopenharmony_ci		if (I->j < b->size) {
97662306a36Sopenharmony_ci			int res = profile_cmp(a->vec[I->i], b->vec[I->j]);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci			if (res > 0)
97962306a36Sopenharmony_ci				return b->vec[(I->j)++];
98062306a36Sopenharmony_ci			if (res == 0)
98162306a36Sopenharmony_ci				(I->j)++;
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		return a->vec[(I->i)++];
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (I->j < b->size)
98862306a36Sopenharmony_ci		return b->vec[(I->j)++];
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	return NULL;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci/**
99462306a36Sopenharmony_ci * label_merge_cmp - cmp of @a merging with @b against @z for set ordering
99562306a36Sopenharmony_ci * @a: label to merge then compare (NOT NULL)
99662306a36Sopenharmony_ci * @b: label to merge then compare (NOT NULL)
99762306a36Sopenharmony_ci * @z: label to compare merge against (NOT NULL)
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * Assumes: using the most recent versions of @a, @b, and @z
100062306a36Sopenharmony_ci *
100162306a36Sopenharmony_ci * Returns: <0  if a < b
100262306a36Sopenharmony_ci *          ==0 if a == b
100362306a36Sopenharmony_ci *          >0  if a > b
100462306a36Sopenharmony_ci */
100562306a36Sopenharmony_cistatic int label_merge_cmp(struct aa_label *a, struct aa_label *b,
100662306a36Sopenharmony_ci			   struct aa_label *z)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct aa_profile *p = NULL;
100962306a36Sopenharmony_ci	struct label_it i = { };
101062306a36Sopenharmony_ci	int k;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	AA_BUG(!a);
101362306a36Sopenharmony_ci	AA_BUG(!b);
101462306a36Sopenharmony_ci	AA_BUG(!z);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	for (k = 0;
101762306a36Sopenharmony_ci	     k < z->size && (p = aa_label_next_in_merge(&i, a, b));
101862306a36Sopenharmony_ci	     k++) {
101962306a36Sopenharmony_ci		int res = profile_cmp(p, z->vec[k]);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		if (res != 0)
102262306a36Sopenharmony_ci			return res;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (p)
102662306a36Sopenharmony_ci		return 1;
102762306a36Sopenharmony_ci	else if (k < z->size)
102862306a36Sopenharmony_ci		return -1;
102962306a36Sopenharmony_ci	return 0;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci/**
103362306a36Sopenharmony_ci * label_merge_insert - create a new label by merging @a and @b
103462306a36Sopenharmony_ci * @new: preallocated label to merge into (NOT NULL)
103562306a36Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
103662306a36Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
103762306a36Sopenharmony_ci *
103862306a36Sopenharmony_ci * Requires: preallocated proxy
103962306a36Sopenharmony_ci *
104062306a36Sopenharmony_ci * Returns: ref counted label either @new if merge is unique
104162306a36Sopenharmony_ci *          @a if @b is a subset of @a
104262306a36Sopenharmony_ci *          @b if @a is a subset of @b
104362306a36Sopenharmony_ci *
104462306a36Sopenharmony_ci * NOTE: will not use @new if the merge results in @new == @a or @b
104562306a36Sopenharmony_ci *
104662306a36Sopenharmony_ci *       Must be used within labelset write lock to avoid racing with
104762306a36Sopenharmony_ci *       setting labels stale.
104862306a36Sopenharmony_ci */
104962306a36Sopenharmony_cistatic struct aa_label *label_merge_insert(struct aa_label *new,
105062306a36Sopenharmony_ci					   struct aa_label *a,
105162306a36Sopenharmony_ci					   struct aa_label *b)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct aa_label *label;
105462306a36Sopenharmony_ci	struct aa_labelset *ls;
105562306a36Sopenharmony_ci	struct aa_profile *next;
105662306a36Sopenharmony_ci	struct label_it i;
105762306a36Sopenharmony_ci	unsigned long flags;
105862306a36Sopenharmony_ci	int k = 0, invcount = 0;
105962306a36Sopenharmony_ci	bool stale = false;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	AA_BUG(!a);
106262306a36Sopenharmony_ci	AA_BUG(a->size < 0);
106362306a36Sopenharmony_ci	AA_BUG(!b);
106462306a36Sopenharmony_ci	AA_BUG(b->size < 0);
106562306a36Sopenharmony_ci	AA_BUG(!new);
106662306a36Sopenharmony_ci	AA_BUG(new->size < a->size + b->size);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	label_for_each_in_merge(i, a, b, next) {
106962306a36Sopenharmony_ci		AA_BUG(!next);
107062306a36Sopenharmony_ci		if (profile_is_stale(next)) {
107162306a36Sopenharmony_ci			new->vec[k] = aa_get_newest_profile(next);
107262306a36Sopenharmony_ci			AA_BUG(!new->vec[k]->label.proxy);
107362306a36Sopenharmony_ci			AA_BUG(!new->vec[k]->label.proxy->label);
107462306a36Sopenharmony_ci			if (next->label.proxy != new->vec[k]->label.proxy)
107562306a36Sopenharmony_ci				invcount++;
107662306a36Sopenharmony_ci			k++;
107762306a36Sopenharmony_ci			stale = true;
107862306a36Sopenharmony_ci		} else
107962306a36Sopenharmony_ci			new->vec[k++] = aa_get_profile(next);
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci	/* set to actual size which is <= allocated len */
108262306a36Sopenharmony_ci	new->size = k;
108362306a36Sopenharmony_ci	new->vec[k] = NULL;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (invcount) {
108662306a36Sopenharmony_ci		new->size -= aa_vec_unique(&new->vec[0], new->size,
108762306a36Sopenharmony_ci					   VEC_FLAG_TERMINATE);
108862306a36Sopenharmony_ci		/* TODO: deal with reference labels */
108962306a36Sopenharmony_ci		if (new->size == 1) {
109062306a36Sopenharmony_ci			label = aa_get_label(&new->vec[0]->label);
109162306a36Sopenharmony_ci			return label;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci	} else if (!stale) {
109462306a36Sopenharmony_ci		/*
109562306a36Sopenharmony_ci		 * merge could be same as a || b, note: it is not possible
109662306a36Sopenharmony_ci		 * for new->size == a->size == b->size unless a == b
109762306a36Sopenharmony_ci		 */
109862306a36Sopenharmony_ci		if (k == a->size)
109962306a36Sopenharmony_ci			return aa_get_label(a);
110062306a36Sopenharmony_ci		else if (k == b->size)
110162306a36Sopenharmony_ci			return aa_get_label(b);
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci	new->flags |= accum_vec_flags(new->vec, new->size);
110462306a36Sopenharmony_ci	ls = labels_set(new);
110562306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
110662306a36Sopenharmony_ci	label = __label_insert(labels_set(new), new, false);
110762306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return label;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci/**
111362306a36Sopenharmony_ci * labelset_of_merge - find which labelset a merged label should be inserted
111462306a36Sopenharmony_ci * @a: label to merge and insert
111562306a36Sopenharmony_ci * @b: label to merge and insert
111662306a36Sopenharmony_ci *
111762306a36Sopenharmony_ci * Returns: labelset that the merged label should be inserted into
111862306a36Sopenharmony_ci */
111962306a36Sopenharmony_cistatic struct aa_labelset *labelset_of_merge(struct aa_label *a,
112062306a36Sopenharmony_ci					     struct aa_label *b)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	struct aa_ns *nsa = labels_ns(a);
112362306a36Sopenharmony_ci	struct aa_ns *nsb = labels_ns(b);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (ns_cmp(nsa, nsb) <= 0)
112662306a36Sopenharmony_ci		return &nsa->labels;
112762306a36Sopenharmony_ci	return &nsb->labels;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/**
113162306a36Sopenharmony_ci * __label_find_merge - find label that is equiv to merge of @a and @b
113262306a36Sopenharmony_ci * @ls: set of labels to search (NOT NULL)
113362306a36Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
113462306a36Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
113562306a36Sopenharmony_ci *
113662306a36Sopenharmony_ci * Requires: ls->lock read_lock held
113762306a36Sopenharmony_ci *
113862306a36Sopenharmony_ci * Returns: ref counted label that is equiv to merge of @a and @b
113962306a36Sopenharmony_ci *     else NULL if merge of @a and @b is not in set
114062306a36Sopenharmony_ci */
114162306a36Sopenharmony_cistatic struct aa_label *__label_find_merge(struct aa_labelset *ls,
114262306a36Sopenharmony_ci					   struct aa_label *a,
114362306a36Sopenharmony_ci					   struct aa_label *b)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct rb_node *node;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	AA_BUG(!ls);
114862306a36Sopenharmony_ci	AA_BUG(!a);
114962306a36Sopenharmony_ci	AA_BUG(!b);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (a == b)
115262306a36Sopenharmony_ci		return __label_find(a);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	node  = ls->root.rb_node;
115562306a36Sopenharmony_ci	while (node) {
115662306a36Sopenharmony_ci		struct aa_label *this = container_of(node, struct aa_label,
115762306a36Sopenharmony_ci						     node);
115862306a36Sopenharmony_ci		int result = label_merge_cmp(a, b, this);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci		if (result < 0)
116162306a36Sopenharmony_ci			node = node->rb_left;
116262306a36Sopenharmony_ci		else if (result > 0)
116362306a36Sopenharmony_ci			node = node->rb_right;
116462306a36Sopenharmony_ci		else
116562306a36Sopenharmony_ci			return __aa_get_label(this);
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	return NULL;
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci/**
117362306a36Sopenharmony_ci * aa_label_find_merge - find label that is equiv to merge of @a and @b
117462306a36Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
117562306a36Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
117662306a36Sopenharmony_ci *
117762306a36Sopenharmony_ci * Requires: labels be fully constructed with a valid ns
117862306a36Sopenharmony_ci *
117962306a36Sopenharmony_ci * Returns: ref counted label that is equiv to merge of @a and @b
118062306a36Sopenharmony_ci *     else NULL if merge of @a and @b is not in set
118162306a36Sopenharmony_ci */
118262306a36Sopenharmony_cistruct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct aa_labelset *ls;
118562306a36Sopenharmony_ci	struct aa_label *label, *ar = NULL, *br = NULL;
118662306a36Sopenharmony_ci	unsigned long flags;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	AA_BUG(!a);
118962306a36Sopenharmony_ci	AA_BUG(!b);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (label_is_stale(a))
119262306a36Sopenharmony_ci		a = ar = aa_get_newest_label(a);
119362306a36Sopenharmony_ci	if (label_is_stale(b))
119462306a36Sopenharmony_ci		b = br = aa_get_newest_label(b);
119562306a36Sopenharmony_ci	ls = labelset_of_merge(a, b);
119662306a36Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
119762306a36Sopenharmony_ci	label = __label_find_merge(ls, a, b);
119862306a36Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
119962306a36Sopenharmony_ci	aa_put_label(ar);
120062306a36Sopenharmony_ci	aa_put_label(br);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	return label;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci/**
120662306a36Sopenharmony_ci * aa_label_merge - attempt to insert new merged label of @a and @b
120762306a36Sopenharmony_ci * @ls: set of labels to insert label into (NOT NULL)
120862306a36Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
120962306a36Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
121062306a36Sopenharmony_ci * @gfp: memory allocation type
121162306a36Sopenharmony_ci *
121262306a36Sopenharmony_ci * Requires: caller to hold valid refs on @a and @b
121362306a36Sopenharmony_ci *           labels be fully constructed with a valid ns
121462306a36Sopenharmony_ci *
121562306a36Sopenharmony_ci * Returns: ref counted new label if successful in inserting merge of a & b
121662306a36Sopenharmony_ci *     else ref counted equivalent label that is already in the set.
121762306a36Sopenharmony_ci *     else NULL if could not create label (-ENOMEM)
121862306a36Sopenharmony_ci */
121962306a36Sopenharmony_cistruct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
122062306a36Sopenharmony_ci				gfp_t gfp)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct aa_label *label = NULL;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	AA_BUG(!a);
122562306a36Sopenharmony_ci	AA_BUG(!b);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (a == b)
122862306a36Sopenharmony_ci		return aa_get_newest_label(a);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* TODO: enable when read side is lockless
123162306a36Sopenharmony_ci	 * check if label exists before taking locks
123262306a36Sopenharmony_ci	if (!label_is_stale(a) && !label_is_stale(b))
123362306a36Sopenharmony_ci		label = aa_label_find_merge(a, b);
123462306a36Sopenharmony_ci	*/
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (!label) {
123762306a36Sopenharmony_ci		struct aa_label *new;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		a = aa_get_newest_label(a);
124062306a36Sopenharmony_ci		b = aa_get_newest_label(b);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		/* could use label_merge_len(a, b), but requires double
124362306a36Sopenharmony_ci		 * comparison for small savings
124462306a36Sopenharmony_ci		 */
124562306a36Sopenharmony_ci		new = aa_label_alloc(a->size + b->size, NULL, gfp);
124662306a36Sopenharmony_ci		if (!new)
124762306a36Sopenharmony_ci			goto out;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		label = label_merge_insert(new, a, b);
125062306a36Sopenharmony_ci		label_free_or_put_new(label, new);
125162306a36Sopenharmony_ciout:
125262306a36Sopenharmony_ci		aa_put_label(a);
125362306a36Sopenharmony_ci		aa_put_label(b);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return label;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci/* match a profile and its associated ns component if needed
126062306a36Sopenharmony_ci * Assumes visibility test has already been done.
126162306a36Sopenharmony_ci * If a subns profile is not to be matched should be prescreened with
126262306a36Sopenharmony_ci * visibility test.
126362306a36Sopenharmony_ci */
126462306a36Sopenharmony_cistatic inline aa_state_t match_component(struct aa_profile *profile,
126562306a36Sopenharmony_ci					 struct aa_ruleset *rules,
126662306a36Sopenharmony_ci					 struct aa_profile *tp,
126762306a36Sopenharmony_ci					 aa_state_t state)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	const char *ns_name;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (profile->ns == tp->ns)
127262306a36Sopenharmony_ci		return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	/* try matching with namespace name and then profile */
127562306a36Sopenharmony_ci	ns_name = aa_ns_name(profile->ns, tp->ns, true);
127662306a36Sopenharmony_ci	state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
127762306a36Sopenharmony_ci	state = aa_dfa_match(rules->policy.dfa, state, ns_name);
127862306a36Sopenharmony_ci	state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
127962306a36Sopenharmony_ci	return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci/**
128362306a36Sopenharmony_ci * label_compound_match - find perms for full compound label
128462306a36Sopenharmony_ci * @profile: profile to find perms for
128562306a36Sopenharmony_ci * @label: label to check access permissions for
128662306a36Sopenharmony_ci * @start: state to start match in
128762306a36Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
128862306a36Sopenharmony_ci * @request: permissions to request
128962306a36Sopenharmony_ci * @perms: perms struct to set
129062306a36Sopenharmony_ci *
129162306a36Sopenharmony_ci * Returns: 0 on success else ERROR
129262306a36Sopenharmony_ci *
129362306a36Sopenharmony_ci * For the label A//&B//&C this does the perm match for A//&B//&C
129462306a36Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
129562306a36Sopenharmony_ci *        check to be stacked.
129662306a36Sopenharmony_ci */
129762306a36Sopenharmony_cistatic int label_compound_match(struct aa_profile *profile,
129862306a36Sopenharmony_ci				struct aa_ruleset *rules,
129962306a36Sopenharmony_ci				struct aa_label *label,
130062306a36Sopenharmony_ci				aa_state_t state, bool subns, u32 request,
130162306a36Sopenharmony_ci				struct aa_perms *perms)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	struct aa_profile *tp;
130462306a36Sopenharmony_ci	struct label_it i;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	/* find first subcomponent that is visible */
130762306a36Sopenharmony_ci	label_for_each(i, label, tp) {
130862306a36Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
130962306a36Sopenharmony_ci			continue;
131062306a36Sopenharmony_ci		state = match_component(profile, rules, tp, state);
131162306a36Sopenharmony_ci		if (!state)
131262306a36Sopenharmony_ci			goto fail;
131362306a36Sopenharmony_ci		goto next;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* no component visible */
131762306a36Sopenharmony_ci	*perms = allperms;
131862306a36Sopenharmony_ci	return 0;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cinext:
132162306a36Sopenharmony_ci	label_for_each_cont(i, label, tp) {
132262306a36Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
132362306a36Sopenharmony_ci			continue;
132462306a36Sopenharmony_ci		state = aa_dfa_match(rules->policy.dfa, state, "//&");
132562306a36Sopenharmony_ci		state = match_component(profile, rules, tp, state);
132662306a36Sopenharmony_ci		if (!state)
132762306a36Sopenharmony_ci			goto fail;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci	*perms = *aa_lookup_perms(&rules->policy, state);
133062306a36Sopenharmony_ci	aa_apply_modes_to_perms(profile, perms);
133162306a36Sopenharmony_ci	if ((perms->allow & request) != request)
133262306a36Sopenharmony_ci		return -EACCES;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	return 0;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cifail:
133762306a36Sopenharmony_ci	*perms = nullperms;
133862306a36Sopenharmony_ci	return state;
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci/**
134262306a36Sopenharmony_ci * label_components_match - find perms for all subcomponents of a label
134362306a36Sopenharmony_ci * @profile: profile to find perms for
134462306a36Sopenharmony_ci * @rules: ruleset to search
134562306a36Sopenharmony_ci * @label: label to check access permissions for
134662306a36Sopenharmony_ci * @start: state to start match in
134762306a36Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
134862306a36Sopenharmony_ci * @request: permissions to request
134962306a36Sopenharmony_ci * @perms: an initialized perms struct to add accumulation to
135062306a36Sopenharmony_ci *
135162306a36Sopenharmony_ci * Returns: 0 on success else ERROR
135262306a36Sopenharmony_ci *
135362306a36Sopenharmony_ci * For the label A//&B//&C this does the perm match for each of A and B and C
135462306a36Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
135562306a36Sopenharmony_ci *        check to be stacked.
135662306a36Sopenharmony_ci */
135762306a36Sopenharmony_cistatic int label_components_match(struct aa_profile *profile,
135862306a36Sopenharmony_ci				  struct aa_ruleset *rules,
135962306a36Sopenharmony_ci				  struct aa_label *label, aa_state_t start,
136062306a36Sopenharmony_ci				  bool subns, u32 request,
136162306a36Sopenharmony_ci				  struct aa_perms *perms)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct aa_profile *tp;
136462306a36Sopenharmony_ci	struct label_it i;
136562306a36Sopenharmony_ci	struct aa_perms tmp;
136662306a36Sopenharmony_ci	aa_state_t state = 0;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	/* find first subcomponent to test */
136962306a36Sopenharmony_ci	label_for_each(i, label, tp) {
137062306a36Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
137162306a36Sopenharmony_ci			continue;
137262306a36Sopenharmony_ci		state = match_component(profile, rules, tp, start);
137362306a36Sopenharmony_ci		if (!state)
137462306a36Sopenharmony_ci			goto fail;
137562306a36Sopenharmony_ci		goto next;
137662306a36Sopenharmony_ci	}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/* no subcomponents visible - no change in perms */
137962306a36Sopenharmony_ci	return 0;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cinext:
138262306a36Sopenharmony_ci	tmp = *aa_lookup_perms(&rules->policy, state);
138362306a36Sopenharmony_ci	aa_apply_modes_to_perms(profile, &tmp);
138462306a36Sopenharmony_ci	aa_perms_accum(perms, &tmp);
138562306a36Sopenharmony_ci	label_for_each_cont(i, label, tp) {
138662306a36Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
138762306a36Sopenharmony_ci			continue;
138862306a36Sopenharmony_ci		state = match_component(profile, rules, tp, start);
138962306a36Sopenharmony_ci		if (!state)
139062306a36Sopenharmony_ci			goto fail;
139162306a36Sopenharmony_ci		tmp = *aa_lookup_perms(&rules->policy, state);
139262306a36Sopenharmony_ci		aa_apply_modes_to_perms(profile, &tmp);
139362306a36Sopenharmony_ci		aa_perms_accum(perms, &tmp);
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if ((perms->allow & request) != request)
139762306a36Sopenharmony_ci		return -EACCES;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	return 0;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cifail:
140262306a36Sopenharmony_ci	*perms = nullperms;
140362306a36Sopenharmony_ci	return -EACCES;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci/**
140762306a36Sopenharmony_ci * aa_label_match - do a multi-component label match
140862306a36Sopenharmony_ci * @profile: profile to match against (NOT NULL)
140962306a36Sopenharmony_ci * @rules: ruleset to search
141062306a36Sopenharmony_ci * @label: label to match (NOT NULL)
141162306a36Sopenharmony_ci * @state: state to start in
141262306a36Sopenharmony_ci * @subns: whether to match subns components
141362306a36Sopenharmony_ci * @request: permission request
141462306a36Sopenharmony_ci * @perms: Returns computed perms (NOT NULL)
141562306a36Sopenharmony_ci *
141662306a36Sopenharmony_ci * Returns: the state the match finished in, may be the none matching state
141762306a36Sopenharmony_ci */
141862306a36Sopenharmony_ciint aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
141962306a36Sopenharmony_ci		   struct aa_label *label, aa_state_t state, bool subns,
142062306a36Sopenharmony_ci		   u32 request, struct aa_perms *perms)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	int error = label_compound_match(profile, rules, label, state, subns,
142362306a36Sopenharmony_ci					 request, perms);
142462306a36Sopenharmony_ci	if (!error)
142562306a36Sopenharmony_ci		return error;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	*perms = allperms;
142862306a36Sopenharmony_ci	return label_components_match(profile, rules, label, state, subns,
142962306a36Sopenharmony_ci				      request, perms);
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci/**
143462306a36Sopenharmony_ci * aa_update_label_name - update a label to have a stored name
143562306a36Sopenharmony_ci * @ns: ns being viewed from (NOT NULL)
143662306a36Sopenharmony_ci * @label: label to update (NOT NULL)
143762306a36Sopenharmony_ci * @gfp: type of memory allocation
143862306a36Sopenharmony_ci *
143962306a36Sopenharmony_ci * Requires: labels_set(label) not locked in caller
144062306a36Sopenharmony_ci *
144162306a36Sopenharmony_ci * note: only updates the label name if it does not have a name already
144262306a36Sopenharmony_ci *       and if it is in the labelset
144362306a36Sopenharmony_ci */
144462306a36Sopenharmony_cibool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct aa_labelset *ls;
144762306a36Sopenharmony_ci	unsigned long flags;
144862306a36Sopenharmony_ci	char __counted *name;
144962306a36Sopenharmony_ci	bool res = false;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	AA_BUG(!ns);
145262306a36Sopenharmony_ci	AA_BUG(!label);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (label->hname || labels_ns(label) != ns)
145562306a36Sopenharmony_ci		return res;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (aa_label_acntsxprint(&name, ns, label, FLAGS_NONE, gfp) < 0)
145862306a36Sopenharmony_ci		return res;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	ls = labels_set(label);
146162306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
146262306a36Sopenharmony_ci	if (!label->hname && label->flags & FLAG_IN_TREE) {
146362306a36Sopenharmony_ci		label->hname = name;
146462306a36Sopenharmony_ci		res = true;
146562306a36Sopenharmony_ci	} else
146662306a36Sopenharmony_ci		aa_put_str(name);
146762306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	return res;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci/*
147362306a36Sopenharmony_ci * cached label name is present and visible
147462306a36Sopenharmony_ci * @label->hname only exists if label is namespace hierachical
147562306a36Sopenharmony_ci */
147662306a36Sopenharmony_cistatic inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
147762306a36Sopenharmony_ci				   int flags)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	if (label->hname && (!ns || labels_ns(label) == ns) &&
148062306a36Sopenharmony_ci	    !(flags & ~FLAG_SHOW_MODE))
148162306a36Sopenharmony_ci		return true;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	return false;
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/* helper macro for snprint routines */
148762306a36Sopenharmony_ci#define update_for_len(total, len, size, str)	\
148862306a36Sopenharmony_cido {					\
148962306a36Sopenharmony_ci	size_t ulen = len;		\
149062306a36Sopenharmony_ci					\
149162306a36Sopenharmony_ci	AA_BUG(len < 0);		\
149262306a36Sopenharmony_ci	total += ulen;			\
149362306a36Sopenharmony_ci	ulen = min(ulen, size);		\
149462306a36Sopenharmony_ci	size -= ulen;			\
149562306a36Sopenharmony_ci	str += ulen;			\
149662306a36Sopenharmony_ci} while (0)
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci/**
149962306a36Sopenharmony_ci * aa_profile_snxprint - print a profile name to a buffer
150062306a36Sopenharmony_ci * @str: buffer to write to. (MAY BE NULL if @size == 0)
150162306a36Sopenharmony_ci * @size: size of buffer
150262306a36Sopenharmony_ci * @view: namespace profile is being viewed from
150362306a36Sopenharmony_ci * @profile: profile to view (NOT NULL)
150462306a36Sopenharmony_ci * @flags: whether to include the mode string
150562306a36Sopenharmony_ci * @prev_ns: last ns printed when used in compound print
150662306a36Sopenharmony_ci *
150762306a36Sopenharmony_ci * Returns: size of name written or would be written if larger than
150862306a36Sopenharmony_ci *          available buffer
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * Note: will not print anything if the profile is not visible
151162306a36Sopenharmony_ci */
151262306a36Sopenharmony_cistatic int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
151362306a36Sopenharmony_ci			       struct aa_profile *profile, int flags,
151462306a36Sopenharmony_ci			       struct aa_ns **prev_ns)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	const char *ns_name = NULL;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	AA_BUG(!str && size != 0);
151962306a36Sopenharmony_ci	AA_BUG(!profile);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	if (!view)
152262306a36Sopenharmony_ci		view = profiles_ns(profile);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	if (view != profile->ns &&
152562306a36Sopenharmony_ci	    (!prev_ns || (*prev_ns != profile->ns))) {
152662306a36Sopenharmony_ci		if (prev_ns)
152762306a36Sopenharmony_ci			*prev_ns = profile->ns;
152862306a36Sopenharmony_ci		ns_name = aa_ns_name(view, profile->ns,
152962306a36Sopenharmony_ci				     flags & FLAG_VIEW_SUBNS);
153062306a36Sopenharmony_ci		if (ns_name == aa_hidden_ns_name) {
153162306a36Sopenharmony_ci			if (flags & FLAG_HIDDEN_UNCONFINED)
153262306a36Sopenharmony_ci				return snprintf(str, size, "%s", "unconfined");
153362306a36Sopenharmony_ci			return snprintf(str, size, "%s", ns_name);
153462306a36Sopenharmony_ci		}
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if ((flags & FLAG_SHOW_MODE) && profile != profile->ns->unconfined) {
153862306a36Sopenharmony_ci		const char *modestr = aa_profile_mode_names[profile->mode];
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		if (ns_name)
154162306a36Sopenharmony_ci			return snprintf(str, size, ":%s:%s (%s)", ns_name,
154262306a36Sopenharmony_ci					profile->base.hname, modestr);
154362306a36Sopenharmony_ci		return snprintf(str, size, "%s (%s)", profile->base.hname,
154462306a36Sopenharmony_ci				modestr);
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	if (ns_name)
154862306a36Sopenharmony_ci		return snprintf(str, size, ":%s:%s", ns_name,
154962306a36Sopenharmony_ci				profile->base.hname);
155062306a36Sopenharmony_ci	return snprintf(str, size, "%s", profile->base.hname);
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic const char *label_modename(struct aa_ns *ns, struct aa_label *label,
155462306a36Sopenharmony_ci				  int flags)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct aa_profile *profile;
155762306a36Sopenharmony_ci	struct label_it i;
155862306a36Sopenharmony_ci	int mode = -1, count = 0;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	label_for_each(i, label, profile) {
156162306a36Sopenharmony_ci		if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
156262306a36Sopenharmony_ci			count++;
156362306a36Sopenharmony_ci			if (profile == profile->ns->unconfined)
156462306a36Sopenharmony_ci				/* special case unconfined so stacks with
156562306a36Sopenharmony_ci				 * unconfined don't report as mixed. ie.
156662306a36Sopenharmony_ci				 * profile_foo//&:ns1:unconfined (mixed)
156762306a36Sopenharmony_ci				 */
156862306a36Sopenharmony_ci				continue;
156962306a36Sopenharmony_ci			if (mode == -1)
157062306a36Sopenharmony_ci				mode = profile->mode;
157162306a36Sopenharmony_ci			else if (mode != profile->mode)
157262306a36Sopenharmony_ci				return "mixed";
157362306a36Sopenharmony_ci		}
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (count == 0)
157762306a36Sopenharmony_ci		return "-";
157862306a36Sopenharmony_ci	if (mode == -1)
157962306a36Sopenharmony_ci		/* everything was unconfined */
158062306a36Sopenharmony_ci		mode = APPARMOR_UNCONFINED;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	return aa_profile_mode_names[mode];
158362306a36Sopenharmony_ci}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci/* if any visible label is not unconfined the display_mode returns true */
158662306a36Sopenharmony_cistatic inline bool display_mode(struct aa_ns *ns, struct aa_label *label,
158762306a36Sopenharmony_ci				int flags)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	if ((flags & FLAG_SHOW_MODE)) {
159062306a36Sopenharmony_ci		struct aa_profile *profile;
159162306a36Sopenharmony_ci		struct label_it i;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci		label_for_each(i, label, profile) {
159462306a36Sopenharmony_ci			if (aa_ns_visible(ns, profile->ns,
159562306a36Sopenharmony_ci					  flags & FLAG_VIEW_SUBNS) &&
159662306a36Sopenharmony_ci			    profile != profile->ns->unconfined)
159762306a36Sopenharmony_ci				return true;
159862306a36Sopenharmony_ci		}
159962306a36Sopenharmony_ci		/* only ns->unconfined in set of profiles in ns */
160062306a36Sopenharmony_ci		return false;
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return false;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci/**
160762306a36Sopenharmony_ci * aa_label_snxprint - print a label name to a string buffer
160862306a36Sopenharmony_ci * @str: buffer to write to. (MAY BE NULL if @size == 0)
160962306a36Sopenharmony_ci * @size: size of buffer
161062306a36Sopenharmony_ci * @ns: namespace profile is being viewed from
161162306a36Sopenharmony_ci * @label: label to view (NOT NULL)
161262306a36Sopenharmony_ci * @flags: whether to include the mode string
161362306a36Sopenharmony_ci *
161462306a36Sopenharmony_ci * Returns: size of name written or would be written if larger than
161562306a36Sopenharmony_ci *          available buffer
161662306a36Sopenharmony_ci *
161762306a36Sopenharmony_ci * Note: labels do not have to be strictly hierarchical to the ns as
161862306a36Sopenharmony_ci *       objects may be shared across different namespaces and thus
161962306a36Sopenharmony_ci *       pickup labeling from each ns.  If a particular part of the
162062306a36Sopenharmony_ci *       label is not visible it will just be excluded.  And if none
162162306a36Sopenharmony_ci *       of the label is visible "---" will be used.
162262306a36Sopenharmony_ci */
162362306a36Sopenharmony_ciint aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
162462306a36Sopenharmony_ci		      struct aa_label *label, int flags)
162562306a36Sopenharmony_ci{
162662306a36Sopenharmony_ci	struct aa_profile *profile;
162762306a36Sopenharmony_ci	struct aa_ns *prev_ns = NULL;
162862306a36Sopenharmony_ci	struct label_it i;
162962306a36Sopenharmony_ci	int count = 0, total = 0;
163062306a36Sopenharmony_ci	ssize_t len;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	AA_BUG(!str && size != 0);
163362306a36Sopenharmony_ci	AA_BUG(!label);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) {
163662306a36Sopenharmony_ci		ns = root_ns;
163762306a36Sopenharmony_ci		len = snprintf(str, size, "_");
163862306a36Sopenharmony_ci		update_for_len(total, len, size, str);
163962306a36Sopenharmony_ci	} else if (!ns) {
164062306a36Sopenharmony_ci		ns = labels_ns(label);
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	label_for_each(i, label, profile) {
164462306a36Sopenharmony_ci		if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
164562306a36Sopenharmony_ci			if (count > 0) {
164662306a36Sopenharmony_ci				len = snprintf(str, size, "//&");
164762306a36Sopenharmony_ci				update_for_len(total, len, size, str);
164862306a36Sopenharmony_ci			}
164962306a36Sopenharmony_ci			len = aa_profile_snxprint(str, size, ns, profile,
165062306a36Sopenharmony_ci						  flags & FLAG_VIEW_SUBNS,
165162306a36Sopenharmony_ci						  &prev_ns);
165262306a36Sopenharmony_ci			update_for_len(total, len, size, str);
165362306a36Sopenharmony_ci			count++;
165462306a36Sopenharmony_ci		}
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (count == 0) {
165862306a36Sopenharmony_ci		if (flags & FLAG_HIDDEN_UNCONFINED)
165962306a36Sopenharmony_ci			return snprintf(str, size, "%s", "unconfined");
166062306a36Sopenharmony_ci		return snprintf(str, size, "%s", aa_hidden_ns_name);
166162306a36Sopenharmony_ci	}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	/* count == 1 && ... is for backwards compat where the mode
166462306a36Sopenharmony_ci	 * is not displayed for 'unconfined' in the current ns
166562306a36Sopenharmony_ci	 */
166662306a36Sopenharmony_ci	if (display_mode(ns, label, flags)) {
166762306a36Sopenharmony_ci		len = snprintf(str, size, " (%s)",
166862306a36Sopenharmony_ci			       label_modename(ns, label, flags));
166962306a36Sopenharmony_ci		update_for_len(total, len, size, str);
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	return total;
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci#undef update_for_len
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci/**
167762306a36Sopenharmony_ci * aa_label_asxprint - allocate a string buffer and print label into it
167862306a36Sopenharmony_ci * @strp: Returns - the allocated buffer with the label name. (NOT NULL)
167962306a36Sopenharmony_ci * @ns: namespace profile is being viewed from
168062306a36Sopenharmony_ci * @label: label to view (NOT NULL)
168162306a36Sopenharmony_ci * @flags: flags controlling what label info is printed
168262306a36Sopenharmony_ci * @gfp: kernel memory allocation type
168362306a36Sopenharmony_ci *
168462306a36Sopenharmony_ci * Returns: size of name written or would be written if larger than
168562306a36Sopenharmony_ci *          available buffer
168662306a36Sopenharmony_ci */
168762306a36Sopenharmony_ciint aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
168862306a36Sopenharmony_ci		      int flags, gfp_t gfp)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	int size;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	AA_BUG(!strp);
169362306a36Sopenharmony_ci	AA_BUG(!label);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	size = aa_label_snxprint(NULL, 0, ns, label, flags);
169662306a36Sopenharmony_ci	if (size < 0)
169762306a36Sopenharmony_ci		return size;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	*strp = kmalloc(size + 1, gfp);
170062306a36Sopenharmony_ci	if (!*strp)
170162306a36Sopenharmony_ci		return -ENOMEM;
170262306a36Sopenharmony_ci	return aa_label_snxprint(*strp, size + 1, ns, label, flags);
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci/**
170662306a36Sopenharmony_ci * aa_label_acntsxprint - allocate a __counted string buffer and print label
170762306a36Sopenharmony_ci * @strp: buffer to write to.
170862306a36Sopenharmony_ci * @ns: namespace profile is being viewed from
170962306a36Sopenharmony_ci * @label: label to view (NOT NULL)
171062306a36Sopenharmony_ci * @flags: flags controlling what label info is printed
171162306a36Sopenharmony_ci * @gfp: kernel memory allocation type
171262306a36Sopenharmony_ci *
171362306a36Sopenharmony_ci * Returns: size of name written or would be written if larger than
171462306a36Sopenharmony_ci *          available buffer
171562306a36Sopenharmony_ci */
171662306a36Sopenharmony_ciint aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
171762306a36Sopenharmony_ci			 struct aa_label *label, int flags, gfp_t gfp)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	int size;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	AA_BUG(!strp);
172262306a36Sopenharmony_ci	AA_BUG(!label);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	size = aa_label_snxprint(NULL, 0, ns, label, flags);
172562306a36Sopenharmony_ci	if (size < 0)
172662306a36Sopenharmony_ci		return size;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	*strp = aa_str_alloc(size + 1, gfp);
172962306a36Sopenharmony_ci	if (!*strp)
173062306a36Sopenharmony_ci		return -ENOMEM;
173162306a36Sopenharmony_ci	return aa_label_snxprint(*strp, size + 1, ns, label, flags);
173262306a36Sopenharmony_ci}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_civoid aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
173662306a36Sopenharmony_ci		     struct aa_label *label, int flags, gfp_t gfp)
173762306a36Sopenharmony_ci{
173862306a36Sopenharmony_ci	const char *str;
173962306a36Sopenharmony_ci	char *name = NULL;
174062306a36Sopenharmony_ci	int len;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	AA_BUG(!ab);
174362306a36Sopenharmony_ci	AA_BUG(!label);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (!use_label_hname(ns, label, flags) ||
174662306a36Sopenharmony_ci	    display_mode(ns, label, flags)) {
174762306a36Sopenharmony_ci		len  = aa_label_asxprint(&name, ns, label, flags, gfp);
174862306a36Sopenharmony_ci		if (len < 0) {
174962306a36Sopenharmony_ci			AA_DEBUG("label print error");
175062306a36Sopenharmony_ci			return;
175162306a36Sopenharmony_ci		}
175262306a36Sopenharmony_ci		str = name;
175362306a36Sopenharmony_ci	} else {
175462306a36Sopenharmony_ci		str = (char *) label->hname;
175562306a36Sopenharmony_ci		len = strlen(str);
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci	if (audit_string_contains_control(str, len))
175862306a36Sopenharmony_ci		audit_log_n_hex(ab, str, len);
175962306a36Sopenharmony_ci	else
176062306a36Sopenharmony_ci		audit_log_n_string(ab, str, len);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	kfree(name);
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_civoid aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
176662306a36Sopenharmony_ci			 struct aa_label *label, int flags, gfp_t gfp)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	AA_BUG(!f);
176962306a36Sopenharmony_ci	AA_BUG(!label);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (!use_label_hname(ns, label, flags)) {
177262306a36Sopenharmony_ci		char *str;
177362306a36Sopenharmony_ci		int len;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		len = aa_label_asxprint(&str, ns, label, flags, gfp);
177662306a36Sopenharmony_ci		if (len < 0) {
177762306a36Sopenharmony_ci			AA_DEBUG("label print error");
177862306a36Sopenharmony_ci			return;
177962306a36Sopenharmony_ci		}
178062306a36Sopenharmony_ci		seq_puts(f, str);
178162306a36Sopenharmony_ci		kfree(str);
178262306a36Sopenharmony_ci	} else if (display_mode(ns, label, flags))
178362306a36Sopenharmony_ci		seq_printf(f, "%s (%s)", label->hname,
178462306a36Sopenharmony_ci			   label_modename(ns, label, flags));
178562306a36Sopenharmony_ci	else
178662306a36Sopenharmony_ci		seq_puts(f, label->hname);
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_civoid aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
179062306a36Sopenharmony_ci		      gfp_t gfp)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	AA_BUG(!label);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	if (!use_label_hname(ns, label, flags)) {
179562306a36Sopenharmony_ci		char *str;
179662306a36Sopenharmony_ci		int len;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci		len = aa_label_asxprint(&str, ns, label, flags, gfp);
179962306a36Sopenharmony_ci		if (len < 0) {
180062306a36Sopenharmony_ci			AA_DEBUG("label print error");
180162306a36Sopenharmony_ci			return;
180262306a36Sopenharmony_ci		}
180362306a36Sopenharmony_ci		pr_info("%s", str);
180462306a36Sopenharmony_ci		kfree(str);
180562306a36Sopenharmony_ci	} else if (display_mode(ns, label, flags))
180662306a36Sopenharmony_ci		pr_info("%s (%s)", label->hname,
180762306a36Sopenharmony_ci		       label_modename(ns, label, flags));
180862306a36Sopenharmony_ci	else
180962306a36Sopenharmony_ci		pr_info("%s", label->hname);
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_civoid aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
181362306a36Sopenharmony_ci{
181462306a36Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
181762306a36Sopenharmony_ci	aa_put_ns(ns);
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_civoid aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
182162306a36Sopenharmony_ci{
182262306a36Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
182562306a36Sopenharmony_ci	aa_put_ns(ns);
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_civoid aa_label_printk(struct aa_label *label, gfp_t gfp)
182962306a36Sopenharmony_ci{
183062306a36Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	aa_label_xprintk(ns, label, FLAG_VIEW_SUBNS, gfp);
183362306a36Sopenharmony_ci	aa_put_ns(ns);
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic int label_count_strn_entries(const char *str, size_t n)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	const char *end = str + n;
183962306a36Sopenharmony_ci	const char *split;
184062306a36Sopenharmony_ci	int count = 1;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	AA_BUG(!str);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	for (split = aa_label_strn_split(str, end - str);
184562306a36Sopenharmony_ci	     split;
184662306a36Sopenharmony_ci	     split = aa_label_strn_split(str, end - str)) {
184762306a36Sopenharmony_ci		count++;
184862306a36Sopenharmony_ci		str = split + 3;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	return count;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci/*
185562306a36Sopenharmony_ci * ensure stacks with components like
185662306a36Sopenharmony_ci *   :ns:A//&B
185762306a36Sopenharmony_ci * have :ns: applied to both 'A' and 'B' by making the lookup relative
185862306a36Sopenharmony_ci * to the base if the lookup specifies an ns, else making the stacked lookup
185962306a36Sopenharmony_ci * relative to the last embedded ns in the string.
186062306a36Sopenharmony_ci */
186162306a36Sopenharmony_cistatic struct aa_profile *fqlookupn_profile(struct aa_label *base,
186262306a36Sopenharmony_ci					    struct aa_label *currentbase,
186362306a36Sopenharmony_ci					    const char *str, size_t n)
186462306a36Sopenharmony_ci{
186562306a36Sopenharmony_ci	const char *first = skipn_spaces(str, n);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (first && *first == ':')
186862306a36Sopenharmony_ci		return aa_fqlookupn_profile(base, str, n);
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	return aa_fqlookupn_profile(currentbase, str, n);
187162306a36Sopenharmony_ci}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci/**
187462306a36Sopenharmony_ci * aa_label_strn_parse - parse, validate and convert a text string to a label
187562306a36Sopenharmony_ci * @base: base label to use for lookups (NOT NULL)
187662306a36Sopenharmony_ci * @str: null terminated text string (NOT NULL)
187762306a36Sopenharmony_ci * @n: length of str to parse, will stop at \0 if encountered before n
187862306a36Sopenharmony_ci * @gfp: allocation type
187962306a36Sopenharmony_ci * @create: true if should create compound labels if they don't exist
188062306a36Sopenharmony_ci * @force_stack: true if should stack even if no leading &
188162306a36Sopenharmony_ci *
188262306a36Sopenharmony_ci * Returns: the matching refcounted label if present
188362306a36Sopenharmony_ci *     else ERRPTR
188462306a36Sopenharmony_ci */
188562306a36Sopenharmony_cistruct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
188662306a36Sopenharmony_ci				     size_t n, gfp_t gfp, bool create,
188762306a36Sopenharmony_ci				     bool force_stack)
188862306a36Sopenharmony_ci{
188962306a36Sopenharmony_ci	DEFINE_VEC(profile, vec);
189062306a36Sopenharmony_ci	struct aa_label *label, *currbase = base;
189162306a36Sopenharmony_ci	int i, len, stack = 0, error;
189262306a36Sopenharmony_ci	const char *end = str + n;
189362306a36Sopenharmony_ci	const char *split;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	AA_BUG(!base);
189662306a36Sopenharmony_ci	AA_BUG(!str);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	str = skipn_spaces(str, n);
189962306a36Sopenharmony_ci	if (str == NULL || (AA_DEBUG_LABEL && *str == '_' &&
190062306a36Sopenharmony_ci			    base != &root_ns->unconfined->label))
190162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	len = label_count_strn_entries(str, end - str);
190462306a36Sopenharmony_ci	if (*str == '&' || force_stack) {
190562306a36Sopenharmony_ci		/* stack on top of base */
190662306a36Sopenharmony_ci		stack = base->size;
190762306a36Sopenharmony_ci		len += stack;
190862306a36Sopenharmony_ci		if (*str == '&')
190962306a36Sopenharmony_ci			str++;
191062306a36Sopenharmony_ci	}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	error = vec_setup(profile, vec, len, gfp);
191362306a36Sopenharmony_ci	if (error)
191462306a36Sopenharmony_ci		return ERR_PTR(error);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	for (i = 0; i < stack; i++)
191762306a36Sopenharmony_ci		vec[i] = aa_get_profile(base->vec[i]);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	for (split = aa_label_strn_split(str, end - str), i = stack;
192062306a36Sopenharmony_ci	     split && i < len; i++) {
192162306a36Sopenharmony_ci		vec[i] = fqlookupn_profile(base, currbase, str, split - str);
192262306a36Sopenharmony_ci		if (!vec[i])
192362306a36Sopenharmony_ci			goto fail;
192462306a36Sopenharmony_ci		/*
192562306a36Sopenharmony_ci		 * if component specified a new ns it becomes the new base
192662306a36Sopenharmony_ci		 * so that subsequent lookups are relative to it
192762306a36Sopenharmony_ci		 */
192862306a36Sopenharmony_ci		if (vec[i]->ns != labels_ns(currbase))
192962306a36Sopenharmony_ci			currbase = &vec[i]->label;
193062306a36Sopenharmony_ci		str = split + 3;
193162306a36Sopenharmony_ci		split = aa_label_strn_split(str, end - str);
193262306a36Sopenharmony_ci	}
193362306a36Sopenharmony_ci	/* last element doesn't have a split */
193462306a36Sopenharmony_ci	if (i < len) {
193562306a36Sopenharmony_ci		vec[i] = fqlookupn_profile(base, currbase, str, end - str);
193662306a36Sopenharmony_ci		if (!vec[i])
193762306a36Sopenharmony_ci			goto fail;
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci	if (len == 1)
194062306a36Sopenharmony_ci		/* no need to free vec as len < LOCAL_VEC_ENTRIES */
194162306a36Sopenharmony_ci		return &vec[0]->label;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	len -= aa_vec_unique(vec, len, VEC_FLAG_TERMINATE);
194462306a36Sopenharmony_ci	/* TODO: deal with reference labels */
194562306a36Sopenharmony_ci	if (len == 1) {
194662306a36Sopenharmony_ci		label = aa_get_label(&vec[0]->label);
194762306a36Sopenharmony_ci		goto out;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (create)
195162306a36Sopenharmony_ci		label = aa_vec_find_or_create_label(vec, len, gfp);
195262306a36Sopenharmony_ci	else
195362306a36Sopenharmony_ci		label = vec_find(vec, len);
195462306a36Sopenharmony_ci	if (!label)
195562306a36Sopenharmony_ci		goto fail;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ciout:
195862306a36Sopenharmony_ci	/* use adjusted len from after vec_unique, not original */
195962306a36Sopenharmony_ci	vec_cleanup(profile, vec, len);
196062306a36Sopenharmony_ci	return label;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_cifail:
196362306a36Sopenharmony_ci	label = ERR_PTR(-ENOENT);
196462306a36Sopenharmony_ci	goto out;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistruct aa_label *aa_label_parse(struct aa_label *base, const char *str,
196862306a36Sopenharmony_ci				gfp_t gfp, bool create, bool force_stack)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	return aa_label_strn_parse(base, str, strlen(str), gfp, create,
197162306a36Sopenharmony_ci				   force_stack);
197262306a36Sopenharmony_ci}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci/**
197562306a36Sopenharmony_ci * aa_labelset_destroy - remove all labels from the label set
197662306a36Sopenharmony_ci * @ls: label set to cleanup (NOT NULL)
197762306a36Sopenharmony_ci *
197862306a36Sopenharmony_ci * Labels that are removed from the set may still exist beyond the set
197962306a36Sopenharmony_ci * being destroyed depending on their reference counting
198062306a36Sopenharmony_ci */
198162306a36Sopenharmony_civoid aa_labelset_destroy(struct aa_labelset *ls)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	struct rb_node *node;
198462306a36Sopenharmony_ci	unsigned long flags;
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	AA_BUG(!ls);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
198962306a36Sopenharmony_ci	for (node = rb_first(&ls->root); node; node = rb_first(&ls->root)) {
199062306a36Sopenharmony_ci		struct aa_label *this = rb_entry(node, struct aa_label, node);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci		if (labels_ns(this) != root_ns)
199362306a36Sopenharmony_ci			__label_remove(this,
199462306a36Sopenharmony_ci				       ns_unconfined(labels_ns(this)->parent));
199562306a36Sopenharmony_ci		else
199662306a36Sopenharmony_ci			__label_remove(this, NULL);
199762306a36Sopenharmony_ci	}
199862306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci/*
200262306a36Sopenharmony_ci * @ls: labelset to init (NOT NULL)
200362306a36Sopenharmony_ci */
200462306a36Sopenharmony_civoid aa_labelset_init(struct aa_labelset *ls)
200562306a36Sopenharmony_ci{
200662306a36Sopenharmony_ci	AA_BUG(!ls);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	rwlock_init(&ls->lock);
200962306a36Sopenharmony_ci	ls->root = RB_ROOT;
201062306a36Sopenharmony_ci}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_cistatic struct aa_label *labelset_next_stale(struct aa_labelset *ls)
201362306a36Sopenharmony_ci{
201462306a36Sopenharmony_ci	struct aa_label *label;
201562306a36Sopenharmony_ci	struct rb_node *node;
201662306a36Sopenharmony_ci	unsigned long flags;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	AA_BUG(!ls);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	__labelset_for_each(ls, node) {
202362306a36Sopenharmony_ci		label = rb_entry(node, struct aa_label, node);
202462306a36Sopenharmony_ci		if ((label_is_stale(label) ||
202562306a36Sopenharmony_ci		     vec_is_stale(label->vec, label->size)) &&
202662306a36Sopenharmony_ci		    __aa_get_label(label))
202762306a36Sopenharmony_ci			goto out;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci	label = NULL;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ciout:
203362306a36Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	return label;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci/**
203962306a36Sopenharmony_ci * __label_update - insert updated version of @label into labelset
204062306a36Sopenharmony_ci * @label - the label to update/replace
204162306a36Sopenharmony_ci *
204262306a36Sopenharmony_ci * Returns: new label that is up to date
204362306a36Sopenharmony_ci *     else NULL on failure
204462306a36Sopenharmony_ci *
204562306a36Sopenharmony_ci * Requires: @ns lock be held
204662306a36Sopenharmony_ci *
204762306a36Sopenharmony_ci * Note: worst case is the stale @label does not get updated and has
204862306a36Sopenharmony_ci *       to be updated at a later time.
204962306a36Sopenharmony_ci */
205062306a36Sopenharmony_cistatic struct aa_label *__label_update(struct aa_label *label)
205162306a36Sopenharmony_ci{
205262306a36Sopenharmony_ci	struct aa_label *new, *tmp;
205362306a36Sopenharmony_ci	struct aa_labelset *ls;
205462306a36Sopenharmony_ci	unsigned long flags;
205562306a36Sopenharmony_ci	int i, invcount = 0;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	AA_BUG(!label);
205862306a36Sopenharmony_ci	AA_BUG(!mutex_is_locked(&labels_ns(label)->lock));
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	new = aa_label_alloc(label->size, label->proxy, GFP_KERNEL);
206162306a36Sopenharmony_ci	if (!new)
206262306a36Sopenharmony_ci		return NULL;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	/*
206562306a36Sopenharmony_ci	 * while holding the ns_lock will stop profile replacement, removal,
206662306a36Sopenharmony_ci	 * and label updates, label merging and removal can be occurring
206762306a36Sopenharmony_ci	 */
206862306a36Sopenharmony_ci	ls = labels_set(label);
206962306a36Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
207062306a36Sopenharmony_ci	for (i = 0; i < label->size; i++) {
207162306a36Sopenharmony_ci		AA_BUG(!label->vec[i]);
207262306a36Sopenharmony_ci		new->vec[i] = aa_get_newest_profile(label->vec[i]);
207362306a36Sopenharmony_ci		AA_BUG(!new->vec[i]);
207462306a36Sopenharmony_ci		AA_BUG(!new->vec[i]->label.proxy);
207562306a36Sopenharmony_ci		AA_BUG(!new->vec[i]->label.proxy->label);
207662306a36Sopenharmony_ci		if (new->vec[i]->label.proxy != label->vec[i]->label.proxy)
207762306a36Sopenharmony_ci			invcount++;
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	/* updated stale label by being removed/renamed from labelset */
208162306a36Sopenharmony_ci	if (invcount) {
208262306a36Sopenharmony_ci		new->size -= aa_vec_unique(&new->vec[0], new->size,
208362306a36Sopenharmony_ci					   VEC_FLAG_TERMINATE);
208462306a36Sopenharmony_ci		/* TODO: deal with reference labels */
208562306a36Sopenharmony_ci		if (new->size == 1) {
208662306a36Sopenharmony_ci			tmp = aa_get_label(&new->vec[0]->label);
208762306a36Sopenharmony_ci			AA_BUG(tmp == label);
208862306a36Sopenharmony_ci			goto remove;
208962306a36Sopenharmony_ci		}
209062306a36Sopenharmony_ci		if (labels_set(label) != labels_set(new)) {
209162306a36Sopenharmony_ci			write_unlock_irqrestore(&ls->lock, flags);
209262306a36Sopenharmony_ci			tmp = aa_label_insert(labels_set(new), new);
209362306a36Sopenharmony_ci			write_lock_irqsave(&ls->lock, flags);
209462306a36Sopenharmony_ci			goto remove;
209562306a36Sopenharmony_ci		}
209662306a36Sopenharmony_ci	} else
209762306a36Sopenharmony_ci		AA_BUG(labels_ns(label) != labels_ns(new));
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	tmp = __label_insert(labels_set(label), new, true);
210062306a36Sopenharmony_ciremove:
210162306a36Sopenharmony_ci	/* ensure label is removed, and redirected correctly */
210262306a36Sopenharmony_ci	__label_remove(label, tmp);
210362306a36Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
210462306a36Sopenharmony_ci	label_free_or_put_new(tmp, new);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	return tmp;
210762306a36Sopenharmony_ci}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci/**
211062306a36Sopenharmony_ci * __labelset_update - update labels in @ns
211162306a36Sopenharmony_ci * @ns: namespace to update labels in  (NOT NULL)
211262306a36Sopenharmony_ci *
211362306a36Sopenharmony_ci * Requires: @ns lock be held
211462306a36Sopenharmony_ci *
211562306a36Sopenharmony_ci * Walk the labelset ensuring that all labels are up to date and valid
211662306a36Sopenharmony_ci * Any label that has a stale component is marked stale and replaced and
211762306a36Sopenharmony_ci * by an updated version.
211862306a36Sopenharmony_ci *
211962306a36Sopenharmony_ci * If failures happen due to memory pressures then stale labels will
212062306a36Sopenharmony_ci * be left in place until the next pass.
212162306a36Sopenharmony_ci */
212262306a36Sopenharmony_cistatic void __labelset_update(struct aa_ns *ns)
212362306a36Sopenharmony_ci{
212462306a36Sopenharmony_ci	struct aa_label *label;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	AA_BUG(!ns);
212762306a36Sopenharmony_ci	AA_BUG(!mutex_is_locked(&ns->lock));
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	do {
213062306a36Sopenharmony_ci		label = labelset_next_stale(&ns->labels);
213162306a36Sopenharmony_ci		if (label) {
213262306a36Sopenharmony_ci			struct aa_label *l = __label_update(label);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci			aa_put_label(l);
213562306a36Sopenharmony_ci			aa_put_label(label);
213662306a36Sopenharmony_ci		}
213762306a36Sopenharmony_ci	} while (label);
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/**
214162306a36Sopenharmony_ci * __aa_labelset_update_subtree - update all labels with a stale component
214262306a36Sopenharmony_ci * @ns: ns to start update at (NOT NULL)
214362306a36Sopenharmony_ci *
214462306a36Sopenharmony_ci * Requires: @ns lock be held
214562306a36Sopenharmony_ci *
214662306a36Sopenharmony_ci * Invalidates labels based on @p in @ns and any children namespaces.
214762306a36Sopenharmony_ci */
214862306a36Sopenharmony_civoid __aa_labelset_update_subtree(struct aa_ns *ns)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct aa_ns *child;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	AA_BUG(!ns);
215362306a36Sopenharmony_ci	AA_BUG(!mutex_is_locked(&ns->lock));
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	__labelset_update(ns);
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	list_for_each_entry(child, &ns->sub_ns, base.list) {
215862306a36Sopenharmony_ci		mutex_lock_nested(&child->lock, child->level);
215962306a36Sopenharmony_ci		__aa_labelset_update_subtree(child);
216062306a36Sopenharmony_ci		mutex_unlock(&child->lock);
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci}
2163