18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AppArmor security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains AppArmor label definitions
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2017 Canonical Ltd.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/audit.h>
118c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
128c2ecf20Sopenharmony_ci#include <linux/sort.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "include/apparmor.h"
158c2ecf20Sopenharmony_ci#include "include/cred.h"
168c2ecf20Sopenharmony_ci#include "include/label.h"
178c2ecf20Sopenharmony_ci#include "include/policy.h"
188c2ecf20Sopenharmony_ci#include "include/secid.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * the aa_label represents the set of profiles confining an object
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Labels maintain a reference count to the set of pointers they reference
258c2ecf20Sopenharmony_ci * Labels are ref counted by
268c2ecf20Sopenharmony_ci *   tasks and object via the security field/security context off the field
278c2ecf20Sopenharmony_ci *   code - will take a ref count on a label if it needs the label
288c2ecf20Sopenharmony_ci *          beyond what is possible with an rcu_read_lock.
298c2ecf20Sopenharmony_ci *   profiles - each profile is a label
308c2ecf20Sopenharmony_ci *   secids - a pinned secid will keep a refcount of the label it is
318c2ecf20Sopenharmony_ci *          referencing
328c2ecf20Sopenharmony_ci *   objects - inode, files, sockets, ...
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Labels are not ref counted by the label set, so they maybe removed and
358c2ecf20Sopenharmony_ci * freed when no longer in use.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define PROXY_POISON 97
408c2ecf20Sopenharmony_ci#define LABEL_POISON 100
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void free_proxy(struct aa_proxy *proxy)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	if (proxy) {
458c2ecf20Sopenharmony_ci		/* p->label will not updated any more as p is dead */
468c2ecf20Sopenharmony_ci		aa_put_label(rcu_dereference_protected(proxy->label, true));
478c2ecf20Sopenharmony_ci		memset(proxy, 0, sizeof(*proxy));
488c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
498c2ecf20Sopenharmony_ci		kfree(proxy);
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_civoid aa_proxy_kref(struct kref *kref)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	free_proxy(proxy);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct aa_proxy *new;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(struct aa_proxy), gfp);
658c2ecf20Sopenharmony_ci	if (new) {
668c2ecf20Sopenharmony_ci		kref_init(&new->count);
678c2ecf20Sopenharmony_ci		rcu_assign_pointer(new->label, aa_get_label(label));
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	return new;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* requires profile list write lock held */
738c2ecf20Sopenharmony_civoid __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct aa_label *tmp;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	AA_BUG(!orig);
788c2ecf20Sopenharmony_ci	AA_BUG(!new);
798c2ecf20Sopenharmony_ci	lockdep_assert_held_write(&labels_set(orig)->lock);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	tmp = rcu_dereference_protected(orig->proxy->label,
828c2ecf20Sopenharmony_ci					&labels_ns(orig)->lock);
838c2ecf20Sopenharmony_ci	rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
848c2ecf20Sopenharmony_ci	orig->flags |= FLAG_STALE;
858c2ecf20Sopenharmony_ci	aa_put_label(tmp);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void __proxy_share(struct aa_label *old, struct aa_label *new)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct aa_proxy *proxy = new->proxy;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	new->proxy = aa_get_proxy(old->proxy);
938c2ecf20Sopenharmony_ci	__aa_proxy_redirect(old, new);
948c2ecf20Sopenharmony_ci	aa_put_proxy(proxy);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * ns_cmp - compare ns for label set ordering
1008c2ecf20Sopenharmony_ci * @a: ns to compare (NOT NULL)
1018c2ecf20Sopenharmony_ci * @b: ns to compare (NOT NULL)
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * Returns: <0 if a < b
1048c2ecf20Sopenharmony_ci *          ==0 if a == b
1058c2ecf20Sopenharmony_ci *          >0  if a > b
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic int ns_cmp(struct aa_ns *a, struct aa_ns *b)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	int res;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	AA_BUG(!a);
1128c2ecf20Sopenharmony_ci	AA_BUG(!b);
1138c2ecf20Sopenharmony_ci	AA_BUG(!a->base.hname);
1148c2ecf20Sopenharmony_ci	AA_BUG(!b->base.hname);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (a == b)
1178c2ecf20Sopenharmony_ci		return 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	res = a->level - b->level;
1208c2ecf20Sopenharmony_ci	if (res)
1218c2ecf20Sopenharmony_ci		return res;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return strcmp(a->base.hname, b->base.hname);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * profile_cmp - profile comparison for set ordering
1288c2ecf20Sopenharmony_ci * @a: profile to compare (NOT NULL)
1298c2ecf20Sopenharmony_ci * @b: profile to compare (NOT NULL)
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * Returns: <0  if a < b
1328c2ecf20Sopenharmony_ci *          ==0 if a == b
1338c2ecf20Sopenharmony_ci *          >0  if a > b
1348c2ecf20Sopenharmony_ci */
1358c2ecf20Sopenharmony_cistatic int profile_cmp(struct aa_profile *a, struct aa_profile *b)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	int res;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	AA_BUG(!a);
1408c2ecf20Sopenharmony_ci	AA_BUG(!b);
1418c2ecf20Sopenharmony_ci	AA_BUG(!a->ns);
1428c2ecf20Sopenharmony_ci	AA_BUG(!b->ns);
1438c2ecf20Sopenharmony_ci	AA_BUG(!a->base.hname);
1448c2ecf20Sopenharmony_ci	AA_BUG(!b->base.hname);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (a == b || a->base.hname == b->base.hname)
1478c2ecf20Sopenharmony_ci		return 0;
1488c2ecf20Sopenharmony_ci	res = ns_cmp(a->ns, b->ns);
1498c2ecf20Sopenharmony_ci	if (res)
1508c2ecf20Sopenharmony_ci		return res;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return strcmp(a->base.hname, b->base.hname);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/**
1568c2ecf20Sopenharmony_ci * vec_cmp - label comparison for set ordering
1578c2ecf20Sopenharmony_ci * @a: label to compare (NOT NULL)
1588c2ecf20Sopenharmony_ci * @vec: vector of profiles to compare (NOT NULL)
1598c2ecf20Sopenharmony_ci * @n: length of @vec
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * Returns: <0  if a < vec
1628c2ecf20Sopenharmony_ci *          ==0 if a == vec
1638c2ecf20Sopenharmony_ci *          >0  if a > vec
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic int vec_cmp(struct aa_profile **a, int an, struct aa_profile **b, int bn)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int i;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	AA_BUG(!a);
1708c2ecf20Sopenharmony_ci	AA_BUG(!*a);
1718c2ecf20Sopenharmony_ci	AA_BUG(!b);
1728c2ecf20Sopenharmony_ci	AA_BUG(!*b);
1738c2ecf20Sopenharmony_ci	AA_BUG(an <= 0);
1748c2ecf20Sopenharmony_ci	AA_BUG(bn <= 0);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < an && i < bn; i++) {
1778c2ecf20Sopenharmony_ci		int res = profile_cmp(a[i], b[i]);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		if (res != 0)
1808c2ecf20Sopenharmony_ci			return res;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return an - bn;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic bool vec_is_stale(struct aa_profile **vec, int n)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int i;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	AA_BUG(!vec);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
1938c2ecf20Sopenharmony_ci		if (profile_is_stale(vec[i]))
1948c2ecf20Sopenharmony_ci			return true;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return false;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic bool vec_unconfined(struct aa_profile **vec, int n)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int i;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	AA_BUG(!vec);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
2078c2ecf20Sopenharmony_ci		if (!profile_unconfined(vec[i]))
2088c2ecf20Sopenharmony_ci			return false;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return true;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int sort_cmp(const void *a, const void *b)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return profile_cmp(*(struct aa_profile **)a, *(struct aa_profile **)b);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * assumes vec is sorted
2218c2ecf20Sopenharmony_ci * Assumes @vec has null terminator at vec[n], and will null terminate
2228c2ecf20Sopenharmony_ci * vec[n - dups]
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic inline int unique(struct aa_profile **vec, int n)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	int i, pos, dups = 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	AA_BUG(n < 1);
2298c2ecf20Sopenharmony_ci	AA_BUG(!vec);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	pos = 0;
2328c2ecf20Sopenharmony_ci	for (i = 1; i < n; i++) {
2338c2ecf20Sopenharmony_ci		int res = profile_cmp(vec[pos], vec[i]);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		AA_BUG(res > 0, "vec not sorted");
2368c2ecf20Sopenharmony_ci		if (res == 0) {
2378c2ecf20Sopenharmony_ci			/* drop duplicate */
2388c2ecf20Sopenharmony_ci			aa_put_profile(vec[i]);
2398c2ecf20Sopenharmony_ci			dups++;
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci		pos++;
2438c2ecf20Sopenharmony_ci		if (dups)
2448c2ecf20Sopenharmony_ci			vec[pos] = vec[i];
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	AA_BUG(dups < 0);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return dups;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci * aa_vec_unique - canonical sort and unique a list of profiles
2548c2ecf20Sopenharmony_ci * @n: number of refcounted profiles in the list (@n > 0)
2558c2ecf20Sopenharmony_ci * @vec: list of profiles to sort and merge
2568c2ecf20Sopenharmony_ci *
2578c2ecf20Sopenharmony_ci * Returns: the number of duplicates eliminated == references put
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * If @flags & VEC_FLAG_TERMINATE @vec has null terminator at vec[n], and will
2608c2ecf20Sopenharmony_ci * null terminate vec[n - dups]
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_ciint aa_vec_unique(struct aa_profile **vec, int n, int flags)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	int i, dups = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	AA_BUG(n < 1);
2678c2ecf20Sopenharmony_ci	AA_BUG(!vec);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* vecs are usually small and inorder, have a fallback for larger */
2708c2ecf20Sopenharmony_ci	if (n > 8) {
2718c2ecf20Sopenharmony_ci		sort(vec, n, sizeof(struct aa_profile *), sort_cmp, NULL);
2728c2ecf20Sopenharmony_ci		dups = unique(vec, n);
2738c2ecf20Sopenharmony_ci		goto out;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* insertion sort + unique in one */
2778c2ecf20Sopenharmony_ci	for (i = 1; i < n; i++) {
2788c2ecf20Sopenharmony_ci		struct aa_profile *tmp = vec[i];
2798c2ecf20Sopenharmony_ci		int pos, j;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		for (pos = i - 1 - dups; pos >= 0; pos--) {
2828c2ecf20Sopenharmony_ci			int res = profile_cmp(vec[pos], tmp);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci			if (res == 0) {
2858c2ecf20Sopenharmony_ci				/* drop duplicate entry */
2868c2ecf20Sopenharmony_ci				aa_put_profile(tmp);
2878c2ecf20Sopenharmony_ci				dups++;
2888c2ecf20Sopenharmony_ci				goto continue_outer;
2898c2ecf20Sopenharmony_ci			} else if (res < 0)
2908c2ecf20Sopenharmony_ci				break;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci		/* pos is at entry < tmp, or index -1. Set to insert pos */
2938c2ecf20Sopenharmony_ci		pos++;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		for (j = i - dups; j > pos; j--)
2968c2ecf20Sopenharmony_ci			vec[j] = vec[j - 1];
2978c2ecf20Sopenharmony_ci		vec[pos] = tmp;
2988c2ecf20Sopenharmony_cicontinue_outer:
2998c2ecf20Sopenharmony_ci		;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	AA_BUG(dups < 0);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ciout:
3058c2ecf20Sopenharmony_ci	if (flags & VEC_FLAG_TERMINATE)
3068c2ecf20Sopenharmony_ci		vec[n - dups] = NULL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return dups;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_civoid aa_label_destroy(struct aa_label *label)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	AA_BUG(!label);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!label_isprofile(label)) {
3178c2ecf20Sopenharmony_ci		struct aa_profile *profile;
3188c2ecf20Sopenharmony_ci		struct label_it i;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		aa_put_str(label->hname);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		label_for_each(i, label, profile) {
3238c2ecf20Sopenharmony_ci			aa_put_profile(profile);
3248c2ecf20Sopenharmony_ci			label->vec[i.i] = (struct aa_profile *)
3258c2ecf20Sopenharmony_ci					   (LABEL_POISON + (long) i.i);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (label->proxy) {
3308c2ecf20Sopenharmony_ci		if (rcu_dereference_protected(label->proxy->label, true) == label)
3318c2ecf20Sopenharmony_ci			rcu_assign_pointer(label->proxy->label, NULL);
3328c2ecf20Sopenharmony_ci		aa_put_proxy(label->proxy);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	aa_free_secid(label->secid);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	label->proxy = (struct aa_proxy *) PROXY_POISON + 1;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_civoid aa_label_free(struct aa_label *label)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	if (!label)
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	aa_label_destroy(label);
3458c2ecf20Sopenharmony_ci	kfree(label);
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void label_free_switch(struct aa_label *label)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	if (label->flags & FLAG_NS_COUNT)
3518c2ecf20Sopenharmony_ci		aa_free_ns(labels_ns(label));
3528c2ecf20Sopenharmony_ci	else if (label_isprofile(label))
3538c2ecf20Sopenharmony_ci		aa_free_profile(labels_profile(label));
3548c2ecf20Sopenharmony_ci	else
3558c2ecf20Sopenharmony_ci		aa_label_free(label);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic void label_free_rcu(struct rcu_head *head)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct aa_label *label = container_of(head, struct aa_label, rcu);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (label->flags & FLAG_IN_TREE)
3638c2ecf20Sopenharmony_ci		(void) aa_label_remove(label);
3648c2ecf20Sopenharmony_ci	label_free_switch(label);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_civoid aa_label_kref(struct kref *kref)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct aa_label *label = container_of(kref, struct aa_label, count);
3708c2ecf20Sopenharmony_ci	struct aa_ns *ns = labels_ns(label);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!ns) {
3738c2ecf20Sopenharmony_ci		/* never live, no rcu callback needed, just using the fn */
3748c2ecf20Sopenharmony_ci		label_free_switch(label);
3758c2ecf20Sopenharmony_ci		return;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	/* TODO: update labels_profile macro so it works here */
3788c2ecf20Sopenharmony_ci	AA_BUG(label_isprofile(label) &&
3798c2ecf20Sopenharmony_ci	       on_list_rcu(&label->vec[0]->base.profiles));
3808c2ecf20Sopenharmony_ci	AA_BUG(label_isprofile(label) &&
3818c2ecf20Sopenharmony_ci	       on_list_rcu(&label->vec[0]->base.list));
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* TODO: if compound label and not stale add to reclaim cache */
3848c2ecf20Sopenharmony_ci	call_rcu(&label->rcu, label_free_rcu);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void label_free_or_put_new(struct aa_label *label, struct aa_label *new)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	if (label != new)
3908c2ecf20Sopenharmony_ci		/* need to free directly to break circular ref with proxy */
3918c2ecf20Sopenharmony_ci		aa_label_free(new);
3928c2ecf20Sopenharmony_ci	else
3938c2ecf20Sopenharmony_ci		aa_put_label(new);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cibool aa_label_init(struct aa_label *label, int size, gfp_t gfp)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	AA_BUG(!label);
3998c2ecf20Sopenharmony_ci	AA_BUG(size < 1);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (aa_alloc_secid(label, gfp) < 0)
4028c2ecf20Sopenharmony_ci		return false;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	label->size = size;			/* doesn't include null */
4058c2ecf20Sopenharmony_ci	label->vec[size] = NULL;		/* null terminate */
4068c2ecf20Sopenharmony_ci	kref_init(&label->count);
4078c2ecf20Sopenharmony_ci	RB_CLEAR_NODE(&label->node);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return true;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci/**
4138c2ecf20Sopenharmony_ci * aa_label_alloc - allocate a label with a profile vector of @size length
4148c2ecf20Sopenharmony_ci * @size: size of profile vector in the label
4158c2ecf20Sopenharmony_ci * @proxy: proxy to use OR null if to allocate a new one
4168c2ecf20Sopenharmony_ci * @gfp: memory allocation type
4178c2ecf20Sopenharmony_ci *
4188c2ecf20Sopenharmony_ci * Returns: new label
4198c2ecf20Sopenharmony_ci *     else NULL if failed
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistruct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct aa_label *new;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	AA_BUG(size < 1);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/*  + 1 for null terminator entry on vec */
4288c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(*new) + sizeof(struct aa_profile *) * (size + 1),
4298c2ecf20Sopenharmony_ci			gfp);
4308c2ecf20Sopenharmony_ci	AA_DEBUG("%s (%p)\n", __func__, new);
4318c2ecf20Sopenharmony_ci	if (!new)
4328c2ecf20Sopenharmony_ci		goto fail;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (!aa_label_init(new, size, gfp))
4358c2ecf20Sopenharmony_ci		goto fail;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!proxy) {
4388c2ecf20Sopenharmony_ci		proxy = aa_alloc_proxy(new, gfp);
4398c2ecf20Sopenharmony_ci		if (!proxy)
4408c2ecf20Sopenharmony_ci			goto fail;
4418c2ecf20Sopenharmony_ci	} else
4428c2ecf20Sopenharmony_ci		aa_get_proxy(proxy);
4438c2ecf20Sopenharmony_ci	/* just set new's proxy, don't redirect proxy here if it was passed in*/
4448c2ecf20Sopenharmony_ci	new->proxy = proxy;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return new;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cifail:
4498c2ecf20Sopenharmony_ci	kfree(new);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return NULL;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/**
4568c2ecf20Sopenharmony_ci * label_cmp - label comparison for set ordering
4578c2ecf20Sopenharmony_ci * @a: label to compare (NOT NULL)
4588c2ecf20Sopenharmony_ci * @b: label to compare (NOT NULL)
4598c2ecf20Sopenharmony_ci *
4608c2ecf20Sopenharmony_ci * Returns: <0  if a < b
4618c2ecf20Sopenharmony_ci *          ==0 if a == b
4628c2ecf20Sopenharmony_ci *          >0  if a > b
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_cistatic int label_cmp(struct aa_label *a, struct aa_label *b)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	AA_BUG(!b);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (a == b)
4698c2ecf20Sopenharmony_ci		return 0;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return vec_cmp(a->vec, a->size, b->vec, b->size);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/* helper fn for label_for_each_confined */
4758c2ecf20Sopenharmony_ciint aa_label_next_confined(struct aa_label *label, int i)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	AA_BUG(!label);
4788c2ecf20Sopenharmony_ci	AA_BUG(i < 0);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	for (; i < label->size; i++) {
4818c2ecf20Sopenharmony_ci		if (!profile_unconfined(label->vec[i]))
4828c2ecf20Sopenharmony_ci			return i;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return i;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/**
4898c2ecf20Sopenharmony_ci * aa_label_next_not_in_set - return the next profile of @sub not in @set
4908c2ecf20Sopenharmony_ci * @I: label iterator
4918c2ecf20Sopenharmony_ci * @set: label to test against
4928c2ecf20Sopenharmony_ci * @sub: label to if is subset of @set
4938c2ecf20Sopenharmony_ci *
4948c2ecf20Sopenharmony_ci * Returns: profile in @sub that is not in @set, with iterator set pos after
4958c2ecf20Sopenharmony_ci *     else NULL if @sub is a subset of @set
4968c2ecf20Sopenharmony_ci */
4978c2ecf20Sopenharmony_cistruct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
4988c2ecf20Sopenharmony_ci					      struct aa_label *set,
4998c2ecf20Sopenharmony_ci					      struct aa_label *sub)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	AA_BUG(!set);
5028c2ecf20Sopenharmony_ci	AA_BUG(!I);
5038c2ecf20Sopenharmony_ci	AA_BUG(I->i < 0);
5048c2ecf20Sopenharmony_ci	AA_BUG(I->i > set->size);
5058c2ecf20Sopenharmony_ci	AA_BUG(!sub);
5068c2ecf20Sopenharmony_ci	AA_BUG(I->j < 0);
5078c2ecf20Sopenharmony_ci	AA_BUG(I->j > sub->size);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	while (I->j < sub->size && I->i < set->size) {
5108c2ecf20Sopenharmony_ci		int res = profile_cmp(sub->vec[I->j], set->vec[I->i]);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		if (res == 0) {
5138c2ecf20Sopenharmony_ci			(I->j)++;
5148c2ecf20Sopenharmony_ci			(I->i)++;
5158c2ecf20Sopenharmony_ci		} else if (res > 0)
5168c2ecf20Sopenharmony_ci			(I->i)++;
5178c2ecf20Sopenharmony_ci		else
5188c2ecf20Sopenharmony_ci			return sub->vec[(I->j)++];
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (I->j < sub->size)
5228c2ecf20Sopenharmony_ci		return sub->vec[(I->j)++];
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return NULL;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci/**
5288c2ecf20Sopenharmony_ci * aa_label_is_subset - test if @sub is a subset of @set
5298c2ecf20Sopenharmony_ci * @set: label to test against
5308c2ecf20Sopenharmony_ci * @sub: label to test if is subset of @set
5318c2ecf20Sopenharmony_ci *
5328c2ecf20Sopenharmony_ci * Returns: true if @sub is subset of @set
5338c2ecf20Sopenharmony_ci *     else false
5348c2ecf20Sopenharmony_ci */
5358c2ecf20Sopenharmony_cibool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct label_it i = { };
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	AA_BUG(!set);
5408c2ecf20Sopenharmony_ci	AA_BUG(!sub);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (sub == set)
5438c2ecf20Sopenharmony_ci		return true;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return __aa_label_next_not_in_set(&i, set, sub) == NULL;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/**
5498c2ecf20Sopenharmony_ci * aa_label_is_unconfined_subset - test if @sub is a subset of @set
5508c2ecf20Sopenharmony_ci * @set: label to test against
5518c2ecf20Sopenharmony_ci * @sub: label to test if is subset of @set
5528c2ecf20Sopenharmony_ci *
5538c2ecf20Sopenharmony_ci * This checks for subset but taking into account unconfined. IF
5548c2ecf20Sopenharmony_ci * @sub contains an unconfined profile that does not have a matching
5558c2ecf20Sopenharmony_ci * unconfined in @set then this will not cause the test to fail.
5568c2ecf20Sopenharmony_ci * Conversely we don't care about an unconfined in @set that is not in
5578c2ecf20Sopenharmony_ci * @sub
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Returns: true if @sub is special_subset of @set
5608c2ecf20Sopenharmony_ci *     else false
5618c2ecf20Sopenharmony_ci */
5628c2ecf20Sopenharmony_cibool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct label_it i = { };
5658c2ecf20Sopenharmony_ci	struct aa_profile *p;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	AA_BUG(!set);
5688c2ecf20Sopenharmony_ci	AA_BUG(!sub);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (sub == set)
5718c2ecf20Sopenharmony_ci		return true;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	do {
5748c2ecf20Sopenharmony_ci		p = __aa_label_next_not_in_set(&i, set, sub);
5758c2ecf20Sopenharmony_ci		if (p && !profile_unconfined(p))
5768c2ecf20Sopenharmony_ci			break;
5778c2ecf20Sopenharmony_ci	} while (p);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return p == NULL;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci/**
5848c2ecf20Sopenharmony_ci * __label_remove - remove @label from the label set
5858c2ecf20Sopenharmony_ci * @l: label to remove
5868c2ecf20Sopenharmony_ci * @new: label to redirect to
5878c2ecf20Sopenharmony_ci *
5888c2ecf20Sopenharmony_ci * Requires: labels_set(@label)->lock write_lock
5898c2ecf20Sopenharmony_ci * Returns:  true if the label was in the tree and removed
5908c2ecf20Sopenharmony_ci */
5918c2ecf20Sopenharmony_cistatic bool __label_remove(struct aa_label *label, struct aa_label *new)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	struct aa_labelset *ls = labels_set(label);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	AA_BUG(!ls);
5968c2ecf20Sopenharmony_ci	AA_BUG(!label);
5978c2ecf20Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (new)
6008c2ecf20Sopenharmony_ci		__aa_proxy_redirect(label, new);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (!label_is_stale(label))
6038c2ecf20Sopenharmony_ci		__label_make_stale(label);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (label->flags & FLAG_IN_TREE) {
6068c2ecf20Sopenharmony_ci		rb_erase(&label->node, &ls->root);
6078c2ecf20Sopenharmony_ci		label->flags &= ~FLAG_IN_TREE;
6088c2ecf20Sopenharmony_ci		return true;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return false;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci/**
6158c2ecf20Sopenharmony_ci * __label_replace - replace @old with @new in label set
6168c2ecf20Sopenharmony_ci * @old: label to remove from label set
6178c2ecf20Sopenharmony_ci * @new: label to replace @old with
6188c2ecf20Sopenharmony_ci *
6198c2ecf20Sopenharmony_ci * Requires: labels_set(@old)->lock write_lock
6208c2ecf20Sopenharmony_ci *           valid ref count be held on @new
6218c2ecf20Sopenharmony_ci * Returns: true if @old was in set and replaced by @new
6228c2ecf20Sopenharmony_ci *
6238c2ecf20Sopenharmony_ci * Note: current implementation requires label set be order in such a way
6248c2ecf20Sopenharmony_ci *       that @new directly replaces @old position in the set (ie.
6258c2ecf20Sopenharmony_ci *       using pointer comparison of the label address would not work)
6268c2ecf20Sopenharmony_ci */
6278c2ecf20Sopenharmony_cistatic bool __label_replace(struct aa_label *old, struct aa_label *new)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct aa_labelset *ls = labels_set(old);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	AA_BUG(!ls);
6328c2ecf20Sopenharmony_ci	AA_BUG(!old);
6338c2ecf20Sopenharmony_ci	AA_BUG(!new);
6348c2ecf20Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
6358c2ecf20Sopenharmony_ci	AA_BUG(new->flags & FLAG_IN_TREE);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if (!label_is_stale(old))
6388c2ecf20Sopenharmony_ci		__label_make_stale(old);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (old->flags & FLAG_IN_TREE) {
6418c2ecf20Sopenharmony_ci		rb_replace_node(&old->node, &new->node, &ls->root);
6428c2ecf20Sopenharmony_ci		old->flags &= ~FLAG_IN_TREE;
6438c2ecf20Sopenharmony_ci		new->flags |= FLAG_IN_TREE;
6448c2ecf20Sopenharmony_ci		return true;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return false;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci/**
6518c2ecf20Sopenharmony_ci * __label_insert - attempt to insert @l into a label set
6528c2ecf20Sopenharmony_ci * @ls: set of labels to insert @l into (NOT NULL)
6538c2ecf20Sopenharmony_ci * @label: new label to insert (NOT NULL)
6548c2ecf20Sopenharmony_ci * @replace: whether insertion should replace existing entry that is not stale
6558c2ecf20Sopenharmony_ci *
6568c2ecf20Sopenharmony_ci * Requires: @ls->lock
6578c2ecf20Sopenharmony_ci *           caller to hold a valid ref on l
6588c2ecf20Sopenharmony_ci *           if @replace is true l has a preallocated proxy associated
6598c2ecf20Sopenharmony_ci * Returns: @l if successful in inserting @l - with additional refcount
6608c2ecf20Sopenharmony_ci *          else ref counted equivalent label that is already in the set,
6618c2ecf20Sopenharmony_ci *          the else condition only happens if @replace is false
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_cistatic struct aa_label *__label_insert(struct aa_labelset *ls,
6648c2ecf20Sopenharmony_ci				       struct aa_label *label, bool replace)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct rb_node **new, *parent = NULL;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	AA_BUG(!ls);
6698c2ecf20Sopenharmony_ci	AA_BUG(!label);
6708c2ecf20Sopenharmony_ci	AA_BUG(labels_set(label) != ls);
6718c2ecf20Sopenharmony_ci	lockdep_assert_held_write(&ls->lock);
6728c2ecf20Sopenharmony_ci	AA_BUG(label->flags & FLAG_IN_TREE);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* Figure out where to put new node */
6758c2ecf20Sopenharmony_ci	new = &ls->root.rb_node;
6768c2ecf20Sopenharmony_ci	while (*new) {
6778c2ecf20Sopenharmony_ci		struct aa_label *this = rb_entry(*new, struct aa_label, node);
6788c2ecf20Sopenharmony_ci		int result = label_cmp(label, this);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		parent = *new;
6818c2ecf20Sopenharmony_ci		if (result == 0) {
6828c2ecf20Sopenharmony_ci			/* !__aa_get_label means queued for destruction,
6838c2ecf20Sopenharmony_ci			 * so replace in place, however the label has
6848c2ecf20Sopenharmony_ci			 * died before the replacement so do not share
6858c2ecf20Sopenharmony_ci			 * the proxy
6868c2ecf20Sopenharmony_ci			 */
6878c2ecf20Sopenharmony_ci			if (!replace && !label_is_stale(this)) {
6888c2ecf20Sopenharmony_ci				if (__aa_get_label(this))
6898c2ecf20Sopenharmony_ci					return this;
6908c2ecf20Sopenharmony_ci			} else
6918c2ecf20Sopenharmony_ci				__proxy_share(this, label);
6928c2ecf20Sopenharmony_ci			AA_BUG(!__label_replace(this, label));
6938c2ecf20Sopenharmony_ci			return aa_get_label(label);
6948c2ecf20Sopenharmony_ci		} else if (result < 0)
6958c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
6968c2ecf20Sopenharmony_ci		else /* (result > 0) */
6978c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* Add new node and rebalance tree. */
7018c2ecf20Sopenharmony_ci	rb_link_node(&label->node, parent, new);
7028c2ecf20Sopenharmony_ci	rb_insert_color(&label->node, &ls->root);
7038c2ecf20Sopenharmony_ci	label->flags |= FLAG_IN_TREE;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	return aa_get_label(label);
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci/**
7098c2ecf20Sopenharmony_ci * __vec_find - find label that matches @vec in label set
7108c2ecf20Sopenharmony_ci * @vec: vec of profiles to find matching label for (NOT NULL)
7118c2ecf20Sopenharmony_ci * @n: length of @vec
7128c2ecf20Sopenharmony_ci *
7138c2ecf20Sopenharmony_ci * Requires: @vec_labelset(vec) lock held
7148c2ecf20Sopenharmony_ci *           caller to hold a valid ref on l
7158c2ecf20Sopenharmony_ci *
7168c2ecf20Sopenharmony_ci * Returns: ref counted @label if matching label is in tree
7178c2ecf20Sopenharmony_ci *          ref counted label that is equiv to @l in tree
7188c2ecf20Sopenharmony_ci *     else NULL if @vec equiv is not in tree
7198c2ecf20Sopenharmony_ci */
7208c2ecf20Sopenharmony_cistatic struct aa_label *__vec_find(struct aa_profile **vec, int n)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct rb_node *node;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	AA_BUG(!vec);
7258c2ecf20Sopenharmony_ci	AA_BUG(!*vec);
7268c2ecf20Sopenharmony_ci	AA_BUG(n <= 0);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	node = vec_labelset(vec, n)->root.rb_node;
7298c2ecf20Sopenharmony_ci	while (node) {
7308c2ecf20Sopenharmony_ci		struct aa_label *this = rb_entry(node, struct aa_label, node);
7318c2ecf20Sopenharmony_ci		int result = vec_cmp(this->vec, this->size, vec, n);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		if (result > 0)
7348c2ecf20Sopenharmony_ci			node = node->rb_left;
7358c2ecf20Sopenharmony_ci		else if (result < 0)
7368c2ecf20Sopenharmony_ci			node = node->rb_right;
7378c2ecf20Sopenharmony_ci		else
7388c2ecf20Sopenharmony_ci			return __aa_get_label(this);
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return NULL;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci/**
7458c2ecf20Sopenharmony_ci * __label_find - find label @label in label set
7468c2ecf20Sopenharmony_ci * @label: label to find (NOT NULL)
7478c2ecf20Sopenharmony_ci *
7488c2ecf20Sopenharmony_ci * Requires: labels_set(@label)->lock held
7498c2ecf20Sopenharmony_ci *           caller to hold a valid ref on l
7508c2ecf20Sopenharmony_ci *
7518c2ecf20Sopenharmony_ci * Returns: ref counted @label if @label is in tree OR
7528c2ecf20Sopenharmony_ci *          ref counted label that is equiv to @label in tree
7538c2ecf20Sopenharmony_ci *     else NULL if @label or equiv is not in tree
7548c2ecf20Sopenharmony_ci */
7558c2ecf20Sopenharmony_cistatic struct aa_label *__label_find(struct aa_label *label)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	AA_BUG(!label);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	return __vec_find(label->vec, label->size);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/**
7648c2ecf20Sopenharmony_ci * aa_label_remove - remove a label from the labelset
7658c2ecf20Sopenharmony_ci * @label: label to remove
7668c2ecf20Sopenharmony_ci *
7678c2ecf20Sopenharmony_ci * Returns: true if @label was removed from the tree
7688c2ecf20Sopenharmony_ci *     else @label was not in tree so it could not be removed
7698c2ecf20Sopenharmony_ci */
7708c2ecf20Sopenharmony_cibool aa_label_remove(struct aa_label *label)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct aa_labelset *ls = labels_set(label);
7738c2ecf20Sopenharmony_ci	unsigned long flags;
7748c2ecf20Sopenharmony_ci	bool res;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	AA_BUG(!ls);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
7798c2ecf20Sopenharmony_ci	res = __label_remove(label, ns_unconfined(labels_ns(label)));
7808c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	return res;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci/**
7868c2ecf20Sopenharmony_ci * aa_label_replace - replace a label @old with a new version @new
7878c2ecf20Sopenharmony_ci * @old: label to replace
7888c2ecf20Sopenharmony_ci * @new: label replacing @old
7898c2ecf20Sopenharmony_ci *
7908c2ecf20Sopenharmony_ci * Returns: true if @old was in tree and replaced
7918c2ecf20Sopenharmony_ci *     else @old was not in tree, and @new was not inserted
7928c2ecf20Sopenharmony_ci */
7938c2ecf20Sopenharmony_cibool aa_label_replace(struct aa_label *old, struct aa_label *new)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	unsigned long flags;
7968c2ecf20Sopenharmony_ci	bool res;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	if (name_is_shared(old, new) && labels_ns(old) == labels_ns(new)) {
7998c2ecf20Sopenharmony_ci		write_lock_irqsave(&labels_set(old)->lock, flags);
8008c2ecf20Sopenharmony_ci		if (old->proxy != new->proxy)
8018c2ecf20Sopenharmony_ci			__proxy_share(old, new);
8028c2ecf20Sopenharmony_ci		else
8038c2ecf20Sopenharmony_ci			__aa_proxy_redirect(old, new);
8048c2ecf20Sopenharmony_ci		res = __label_replace(old, new);
8058c2ecf20Sopenharmony_ci		write_unlock_irqrestore(&labels_set(old)->lock, flags);
8068c2ecf20Sopenharmony_ci	} else {
8078c2ecf20Sopenharmony_ci		struct aa_label *l;
8088c2ecf20Sopenharmony_ci		struct aa_labelset *ls = labels_set(old);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci		write_lock_irqsave(&ls->lock, flags);
8118c2ecf20Sopenharmony_ci		res = __label_remove(old, new);
8128c2ecf20Sopenharmony_ci		if (labels_ns(old) != labels_ns(new)) {
8138c2ecf20Sopenharmony_ci			write_unlock_irqrestore(&ls->lock, flags);
8148c2ecf20Sopenharmony_ci			ls = labels_set(new);
8158c2ecf20Sopenharmony_ci			write_lock_irqsave(&ls->lock, flags);
8168c2ecf20Sopenharmony_ci		}
8178c2ecf20Sopenharmony_ci		l = __label_insert(ls, new, true);
8188c2ecf20Sopenharmony_ci		res = (l == new);
8198c2ecf20Sopenharmony_ci		write_unlock_irqrestore(&ls->lock, flags);
8208c2ecf20Sopenharmony_ci		aa_put_label(l);
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return res;
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci/**
8278c2ecf20Sopenharmony_ci * vec_find - find label @l in label set
8288c2ecf20Sopenharmony_ci * @vec: array of profiles to find equiv label for (NOT NULL)
8298c2ecf20Sopenharmony_ci * @n: length of @vec
8308c2ecf20Sopenharmony_ci *
8318c2ecf20Sopenharmony_ci * Returns: refcounted label if @vec equiv is in tree
8328c2ecf20Sopenharmony_ci *     else NULL if @vec equiv is not in tree
8338c2ecf20Sopenharmony_ci */
8348c2ecf20Sopenharmony_cistatic struct aa_label *vec_find(struct aa_profile **vec, int n)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
8378c2ecf20Sopenharmony_ci	struct aa_label *label;
8388c2ecf20Sopenharmony_ci	unsigned long flags;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	AA_BUG(!vec);
8418c2ecf20Sopenharmony_ci	AA_BUG(!*vec);
8428c2ecf20Sopenharmony_ci	AA_BUG(n <= 0);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	ls = vec_labelset(vec, n);
8458c2ecf20Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
8468c2ecf20Sopenharmony_ci	label = __vec_find(vec, n);
8478c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	return label;
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci/* requires sort and merge done first */
8538c2ecf20Sopenharmony_cistatic struct aa_label *vec_create_and_insert_label(struct aa_profile **vec,
8548c2ecf20Sopenharmony_ci						    int len, gfp_t gfp)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct aa_label *label = NULL;
8578c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
8588c2ecf20Sopenharmony_ci	unsigned long flags;
8598c2ecf20Sopenharmony_ci	struct aa_label *new;
8608c2ecf20Sopenharmony_ci	int i;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	AA_BUG(!vec);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (len == 1)
8658c2ecf20Sopenharmony_ci		return aa_get_label(&vec[0]->label);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	ls = labels_set(&vec[len - 1]->label);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* TODO: enable when read side is lockless
8708c2ecf20Sopenharmony_ci	 * check if label exists before taking locks
8718c2ecf20Sopenharmony_ci	 */
8728c2ecf20Sopenharmony_ci	new = aa_label_alloc(len, NULL, gfp);
8738c2ecf20Sopenharmony_ci	if (!new)
8748c2ecf20Sopenharmony_ci		return NULL;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
8778c2ecf20Sopenharmony_ci		new->vec[i] = aa_get_profile(vec[i]);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
8808c2ecf20Sopenharmony_ci	label = __label_insert(ls, new, false);
8818c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
8828c2ecf20Sopenharmony_ci	label_free_or_put_new(label, new);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	return label;
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistruct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
8888c2ecf20Sopenharmony_ci					     gfp_t gfp)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct aa_label *label = vec_find(vec, len);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (label)
8938c2ecf20Sopenharmony_ci		return label;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return vec_create_and_insert_label(vec, len, gfp);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/**
8998c2ecf20Sopenharmony_ci * aa_label_find - find label @label in label set
9008c2ecf20Sopenharmony_ci * @label: label to find (NOT NULL)
9018c2ecf20Sopenharmony_ci *
9028c2ecf20Sopenharmony_ci * Requires: caller to hold a valid ref on l
9038c2ecf20Sopenharmony_ci *
9048c2ecf20Sopenharmony_ci * Returns: refcounted @label if @label is in tree
9058c2ecf20Sopenharmony_ci *          refcounted label that is equiv to @label in tree
9068c2ecf20Sopenharmony_ci *     else NULL if @label or equiv is not in tree
9078c2ecf20Sopenharmony_ci */
9088c2ecf20Sopenharmony_cistruct aa_label *aa_label_find(struct aa_label *label)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	AA_BUG(!label);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return vec_find(label->vec, label->size);
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci/**
9178c2ecf20Sopenharmony_ci * aa_label_insert - insert label @label into @ls or return existing label
9188c2ecf20Sopenharmony_ci * @ls - labelset to insert @label into
9198c2ecf20Sopenharmony_ci * @label - label to insert
9208c2ecf20Sopenharmony_ci *
9218c2ecf20Sopenharmony_ci * Requires: caller to hold a valid ref on @label
9228c2ecf20Sopenharmony_ci *
9238c2ecf20Sopenharmony_ci * Returns: ref counted @label if successful in inserting @label
9248c2ecf20Sopenharmony_ci *     else ref counted equivalent label that is already in the set
9258c2ecf20Sopenharmony_ci */
9268c2ecf20Sopenharmony_cistruct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *label)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	struct aa_label *l;
9298c2ecf20Sopenharmony_ci	unsigned long flags;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	AA_BUG(!ls);
9328c2ecf20Sopenharmony_ci	AA_BUG(!label);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* check if label exists before taking lock */
9358c2ecf20Sopenharmony_ci	if (!label_is_stale(label)) {
9368c2ecf20Sopenharmony_ci		read_lock_irqsave(&ls->lock, flags);
9378c2ecf20Sopenharmony_ci		l = __label_find(label);
9388c2ecf20Sopenharmony_ci		read_unlock_irqrestore(&ls->lock, flags);
9398c2ecf20Sopenharmony_ci		if (l)
9408c2ecf20Sopenharmony_ci			return l;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
9448c2ecf20Sopenharmony_ci	l = __label_insert(ls, label, false);
9458c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return l;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/**
9528c2ecf20Sopenharmony_ci * aa_label_next_in_merge - find the next profile when merging @a and @b
9538c2ecf20Sopenharmony_ci * @I: label iterator
9548c2ecf20Sopenharmony_ci * @a: label to merge
9558c2ecf20Sopenharmony_ci * @b: label to merge
9568c2ecf20Sopenharmony_ci *
9578c2ecf20Sopenharmony_ci * Returns: next profile
9588c2ecf20Sopenharmony_ci *     else null if no more profiles
9598c2ecf20Sopenharmony_ci */
9608c2ecf20Sopenharmony_cistruct aa_profile *aa_label_next_in_merge(struct label_it *I,
9618c2ecf20Sopenharmony_ci					  struct aa_label *a,
9628c2ecf20Sopenharmony_ci					  struct aa_label *b)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	AA_BUG(!a);
9658c2ecf20Sopenharmony_ci	AA_BUG(!b);
9668c2ecf20Sopenharmony_ci	AA_BUG(!I);
9678c2ecf20Sopenharmony_ci	AA_BUG(I->i < 0);
9688c2ecf20Sopenharmony_ci	AA_BUG(I->i > a->size);
9698c2ecf20Sopenharmony_ci	AA_BUG(I->j < 0);
9708c2ecf20Sopenharmony_ci	AA_BUG(I->j > b->size);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	if (I->i < a->size) {
9738c2ecf20Sopenharmony_ci		if (I->j < b->size) {
9748c2ecf20Sopenharmony_ci			int res = profile_cmp(a->vec[I->i], b->vec[I->j]);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci			if (res > 0)
9778c2ecf20Sopenharmony_ci				return b->vec[(I->j)++];
9788c2ecf20Sopenharmony_ci			if (res == 0)
9798c2ecf20Sopenharmony_ci				(I->j)++;
9808c2ecf20Sopenharmony_ci		}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		return a->vec[(I->i)++];
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (I->j < b->size)
9868c2ecf20Sopenharmony_ci		return b->vec[(I->j)++];
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	return NULL;
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci/**
9928c2ecf20Sopenharmony_ci * label_merge_cmp - cmp of @a merging with @b against @z for set ordering
9938c2ecf20Sopenharmony_ci * @a: label to merge then compare (NOT NULL)
9948c2ecf20Sopenharmony_ci * @b: label to merge then compare (NOT NULL)
9958c2ecf20Sopenharmony_ci * @z: label to compare merge against (NOT NULL)
9968c2ecf20Sopenharmony_ci *
9978c2ecf20Sopenharmony_ci * Assumes: using the most recent versions of @a, @b, and @z
9988c2ecf20Sopenharmony_ci *
9998c2ecf20Sopenharmony_ci * Returns: <0  if a < b
10008c2ecf20Sopenharmony_ci *          ==0 if a == b
10018c2ecf20Sopenharmony_ci *          >0  if a > b
10028c2ecf20Sopenharmony_ci */
10038c2ecf20Sopenharmony_cistatic int label_merge_cmp(struct aa_label *a, struct aa_label *b,
10048c2ecf20Sopenharmony_ci			   struct aa_label *z)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct aa_profile *p = NULL;
10078c2ecf20Sopenharmony_ci	struct label_it i = { };
10088c2ecf20Sopenharmony_ci	int k;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	AA_BUG(!a);
10118c2ecf20Sopenharmony_ci	AA_BUG(!b);
10128c2ecf20Sopenharmony_ci	AA_BUG(!z);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	for (k = 0;
10158c2ecf20Sopenharmony_ci	     k < z->size && (p = aa_label_next_in_merge(&i, a, b));
10168c2ecf20Sopenharmony_ci	     k++) {
10178c2ecf20Sopenharmony_ci		int res = profile_cmp(p, z->vec[k]);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		if (res != 0)
10208c2ecf20Sopenharmony_ci			return res;
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (p)
10248c2ecf20Sopenharmony_ci		return 1;
10258c2ecf20Sopenharmony_ci	else if (k < z->size)
10268c2ecf20Sopenharmony_ci		return -1;
10278c2ecf20Sopenharmony_ci	return 0;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/**
10318c2ecf20Sopenharmony_ci * label_merge_insert - create a new label by merging @a and @b
10328c2ecf20Sopenharmony_ci * @new: preallocated label to merge into (NOT NULL)
10338c2ecf20Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
10348c2ecf20Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
10358c2ecf20Sopenharmony_ci *
10368c2ecf20Sopenharmony_ci * Requires: preallocated proxy
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci * Returns: ref counted label either @new if merge is unique
10398c2ecf20Sopenharmony_ci *          @a if @b is a subset of @a
10408c2ecf20Sopenharmony_ci *          @b if @a is a subset of @b
10418c2ecf20Sopenharmony_ci *
10428c2ecf20Sopenharmony_ci * NOTE: will not use @new if the merge results in @new == @a or @b
10438c2ecf20Sopenharmony_ci *
10448c2ecf20Sopenharmony_ci *       Must be used within labelset write lock to avoid racing with
10458c2ecf20Sopenharmony_ci *       setting labels stale.
10468c2ecf20Sopenharmony_ci */
10478c2ecf20Sopenharmony_cistatic struct aa_label *label_merge_insert(struct aa_label *new,
10488c2ecf20Sopenharmony_ci					   struct aa_label *a,
10498c2ecf20Sopenharmony_ci					   struct aa_label *b)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	struct aa_label *label;
10528c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
10538c2ecf20Sopenharmony_ci	struct aa_profile *next;
10548c2ecf20Sopenharmony_ci	struct label_it i;
10558c2ecf20Sopenharmony_ci	unsigned long flags;
10568c2ecf20Sopenharmony_ci	int k = 0, invcount = 0;
10578c2ecf20Sopenharmony_ci	bool stale = false;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	AA_BUG(!a);
10608c2ecf20Sopenharmony_ci	AA_BUG(a->size < 0);
10618c2ecf20Sopenharmony_ci	AA_BUG(!b);
10628c2ecf20Sopenharmony_ci	AA_BUG(b->size < 0);
10638c2ecf20Sopenharmony_ci	AA_BUG(!new);
10648c2ecf20Sopenharmony_ci	AA_BUG(new->size < a->size + b->size);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	label_for_each_in_merge(i, a, b, next) {
10678c2ecf20Sopenharmony_ci		AA_BUG(!next);
10688c2ecf20Sopenharmony_ci		if (profile_is_stale(next)) {
10698c2ecf20Sopenharmony_ci			new->vec[k] = aa_get_newest_profile(next);
10708c2ecf20Sopenharmony_ci			AA_BUG(!new->vec[k]->label.proxy);
10718c2ecf20Sopenharmony_ci			AA_BUG(!new->vec[k]->label.proxy->label);
10728c2ecf20Sopenharmony_ci			if (next->label.proxy != new->vec[k]->label.proxy)
10738c2ecf20Sopenharmony_ci				invcount++;
10748c2ecf20Sopenharmony_ci			k++;
10758c2ecf20Sopenharmony_ci			stale = true;
10768c2ecf20Sopenharmony_ci		} else
10778c2ecf20Sopenharmony_ci			new->vec[k++] = aa_get_profile(next);
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci	/* set to actual size which is <= allocated len */
10808c2ecf20Sopenharmony_ci	new->size = k;
10818c2ecf20Sopenharmony_ci	new->vec[k] = NULL;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (invcount) {
10848c2ecf20Sopenharmony_ci		new->size -= aa_vec_unique(&new->vec[0], new->size,
10858c2ecf20Sopenharmony_ci					   VEC_FLAG_TERMINATE);
10868c2ecf20Sopenharmony_ci		/* TODO: deal with reference labels */
10878c2ecf20Sopenharmony_ci		if (new->size == 1) {
10888c2ecf20Sopenharmony_ci			label = aa_get_label(&new->vec[0]->label);
10898c2ecf20Sopenharmony_ci			return label;
10908c2ecf20Sopenharmony_ci		}
10918c2ecf20Sopenharmony_ci	} else if (!stale) {
10928c2ecf20Sopenharmony_ci		/*
10938c2ecf20Sopenharmony_ci		 * merge could be same as a || b, note: it is not possible
10948c2ecf20Sopenharmony_ci		 * for new->size == a->size == b->size unless a == b
10958c2ecf20Sopenharmony_ci		 */
10968c2ecf20Sopenharmony_ci		if (k == a->size)
10978c2ecf20Sopenharmony_ci			return aa_get_label(a);
10988c2ecf20Sopenharmony_ci		else if (k == b->size)
10998c2ecf20Sopenharmony_ci			return aa_get_label(b);
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci	if (vec_unconfined(new->vec, new->size))
11028c2ecf20Sopenharmony_ci		new->flags |= FLAG_UNCONFINED;
11038c2ecf20Sopenharmony_ci	ls = labels_set(new);
11048c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
11058c2ecf20Sopenharmony_ci	label = __label_insert(labels_set(new), new, false);
11068c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	return label;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci/**
11128c2ecf20Sopenharmony_ci * labelset_of_merge - find which labelset a merged label should be inserted
11138c2ecf20Sopenharmony_ci * @a: label to merge and insert
11148c2ecf20Sopenharmony_ci * @b: label to merge and insert
11158c2ecf20Sopenharmony_ci *
11168c2ecf20Sopenharmony_ci * Returns: labelset that the merged label should be inserted into
11178c2ecf20Sopenharmony_ci */
11188c2ecf20Sopenharmony_cistatic struct aa_labelset *labelset_of_merge(struct aa_label *a,
11198c2ecf20Sopenharmony_ci					     struct aa_label *b)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	struct aa_ns *nsa = labels_ns(a);
11228c2ecf20Sopenharmony_ci	struct aa_ns *nsb = labels_ns(b);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	if (ns_cmp(nsa, nsb) <= 0)
11258c2ecf20Sopenharmony_ci		return &nsa->labels;
11268c2ecf20Sopenharmony_ci	return &nsb->labels;
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci/**
11308c2ecf20Sopenharmony_ci * __label_find_merge - find label that is equiv to merge of @a and @b
11318c2ecf20Sopenharmony_ci * @ls: set of labels to search (NOT NULL)
11328c2ecf20Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
11338c2ecf20Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
11348c2ecf20Sopenharmony_ci *
11358c2ecf20Sopenharmony_ci * Requires: ls->lock read_lock held
11368c2ecf20Sopenharmony_ci *
11378c2ecf20Sopenharmony_ci * Returns: ref counted label that is equiv to merge of @a and @b
11388c2ecf20Sopenharmony_ci *     else NULL if merge of @a and @b is not in set
11398c2ecf20Sopenharmony_ci */
11408c2ecf20Sopenharmony_cistatic struct aa_label *__label_find_merge(struct aa_labelset *ls,
11418c2ecf20Sopenharmony_ci					   struct aa_label *a,
11428c2ecf20Sopenharmony_ci					   struct aa_label *b)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct rb_node *node;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	AA_BUG(!ls);
11478c2ecf20Sopenharmony_ci	AA_BUG(!a);
11488c2ecf20Sopenharmony_ci	AA_BUG(!b);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (a == b)
11518c2ecf20Sopenharmony_ci		return __label_find(a);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	node  = ls->root.rb_node;
11548c2ecf20Sopenharmony_ci	while (node) {
11558c2ecf20Sopenharmony_ci		struct aa_label *this = container_of(node, struct aa_label,
11568c2ecf20Sopenharmony_ci						     node);
11578c2ecf20Sopenharmony_ci		int result = label_merge_cmp(a, b, this);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		if (result < 0)
11608c2ecf20Sopenharmony_ci			node = node->rb_left;
11618c2ecf20Sopenharmony_ci		else if (result > 0)
11628c2ecf20Sopenharmony_ci			node = node->rb_right;
11638c2ecf20Sopenharmony_ci		else
11648c2ecf20Sopenharmony_ci			return __aa_get_label(this);
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	return NULL;
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci/**
11728c2ecf20Sopenharmony_ci * aa_label_find_merge - find label that is equiv to merge of @a and @b
11738c2ecf20Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
11748c2ecf20Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
11758c2ecf20Sopenharmony_ci *
11768c2ecf20Sopenharmony_ci * Requires: labels be fully constructed with a valid ns
11778c2ecf20Sopenharmony_ci *
11788c2ecf20Sopenharmony_ci * Returns: ref counted label that is equiv to merge of @a and @b
11798c2ecf20Sopenharmony_ci *     else NULL if merge of @a and @b is not in set
11808c2ecf20Sopenharmony_ci */
11818c2ecf20Sopenharmony_cistruct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
11848c2ecf20Sopenharmony_ci	struct aa_label *label, *ar = NULL, *br = NULL;
11858c2ecf20Sopenharmony_ci	unsigned long flags;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	AA_BUG(!a);
11888c2ecf20Sopenharmony_ci	AA_BUG(!b);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	if (label_is_stale(a))
11918c2ecf20Sopenharmony_ci		a = ar = aa_get_newest_label(a);
11928c2ecf20Sopenharmony_ci	if (label_is_stale(b))
11938c2ecf20Sopenharmony_ci		b = br = aa_get_newest_label(b);
11948c2ecf20Sopenharmony_ci	ls = labelset_of_merge(a, b);
11958c2ecf20Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
11968c2ecf20Sopenharmony_ci	label = __label_find_merge(ls, a, b);
11978c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
11988c2ecf20Sopenharmony_ci	aa_put_label(ar);
11998c2ecf20Sopenharmony_ci	aa_put_label(br);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return label;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci/**
12058c2ecf20Sopenharmony_ci * aa_label_merge - attempt to insert new merged label of @a and @b
12068c2ecf20Sopenharmony_ci * @ls: set of labels to insert label into (NOT NULL)
12078c2ecf20Sopenharmony_ci * @a: label to merge with @b  (NOT NULL)
12088c2ecf20Sopenharmony_ci * @b: label to merge with @a  (NOT NULL)
12098c2ecf20Sopenharmony_ci * @gfp: memory allocation type
12108c2ecf20Sopenharmony_ci *
12118c2ecf20Sopenharmony_ci * Requires: caller to hold valid refs on @a and @b
12128c2ecf20Sopenharmony_ci *           labels be fully constructed with a valid ns
12138c2ecf20Sopenharmony_ci *
12148c2ecf20Sopenharmony_ci * Returns: ref counted new label if successful in inserting merge of a & b
12158c2ecf20Sopenharmony_ci *     else ref counted equivalent label that is already in the set.
12168c2ecf20Sopenharmony_ci *     else NULL if could not create label (-ENOMEM)
12178c2ecf20Sopenharmony_ci */
12188c2ecf20Sopenharmony_cistruct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
12198c2ecf20Sopenharmony_ci				gfp_t gfp)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct aa_label *label = NULL;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	AA_BUG(!a);
12248c2ecf20Sopenharmony_ci	AA_BUG(!b);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (a == b)
12278c2ecf20Sopenharmony_ci		return aa_get_newest_label(a);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	/* TODO: enable when read side is lockless
12308c2ecf20Sopenharmony_ci	 * check if label exists before taking locks
12318c2ecf20Sopenharmony_ci	if (!label_is_stale(a) && !label_is_stale(b))
12328c2ecf20Sopenharmony_ci		label = aa_label_find_merge(a, b);
12338c2ecf20Sopenharmony_ci	*/
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (!label) {
12368c2ecf20Sopenharmony_ci		struct aa_label *new;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		a = aa_get_newest_label(a);
12398c2ecf20Sopenharmony_ci		b = aa_get_newest_label(b);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci		/* could use label_merge_len(a, b), but requires double
12428c2ecf20Sopenharmony_ci		 * comparison for small savings
12438c2ecf20Sopenharmony_ci		 */
12448c2ecf20Sopenharmony_ci		new = aa_label_alloc(a->size + b->size, NULL, gfp);
12458c2ecf20Sopenharmony_ci		if (!new)
12468c2ecf20Sopenharmony_ci			goto out;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		label = label_merge_insert(new, a, b);
12498c2ecf20Sopenharmony_ci		label_free_or_put_new(label, new);
12508c2ecf20Sopenharmony_ciout:
12518c2ecf20Sopenharmony_ci		aa_put_label(a);
12528c2ecf20Sopenharmony_ci		aa_put_label(b);
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	return label;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic inline bool label_is_visible(struct aa_profile *profile,
12598c2ecf20Sopenharmony_ci				    struct aa_label *label)
12608c2ecf20Sopenharmony_ci{
12618c2ecf20Sopenharmony_ci	return aa_ns_visible(profile->ns, labels_ns(label), true);
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci/* match a profile and its associated ns component if needed
12658c2ecf20Sopenharmony_ci * Assumes visibility test has already been done.
12668c2ecf20Sopenharmony_ci * If a subns profile is not to be matched should be prescreened with
12678c2ecf20Sopenharmony_ci * visibility test.
12688c2ecf20Sopenharmony_ci */
12698c2ecf20Sopenharmony_cistatic inline unsigned int match_component(struct aa_profile *profile,
12708c2ecf20Sopenharmony_ci					   struct aa_profile *tp,
12718c2ecf20Sopenharmony_ci					   unsigned int state)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	const char *ns_name;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	if (profile->ns == tp->ns)
12768c2ecf20Sopenharmony_ci		return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	/* try matching with namespace name and then profile */
12798c2ecf20Sopenharmony_ci	ns_name = aa_ns_name(profile->ns, tp->ns, true);
12808c2ecf20Sopenharmony_ci	state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
12818c2ecf20Sopenharmony_ci	state = aa_dfa_match(profile->policy.dfa, state, ns_name);
12828c2ecf20Sopenharmony_ci	state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
12838c2ecf20Sopenharmony_ci	return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/**
12878c2ecf20Sopenharmony_ci * label_compound_match - find perms for full compound label
12888c2ecf20Sopenharmony_ci * @profile: profile to find perms for
12898c2ecf20Sopenharmony_ci * @label: label to check access permissions for
12908c2ecf20Sopenharmony_ci * @start: state to start match in
12918c2ecf20Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
12928c2ecf20Sopenharmony_ci * @request: permissions to request
12938c2ecf20Sopenharmony_ci * @perms: perms struct to set
12948c2ecf20Sopenharmony_ci *
12958c2ecf20Sopenharmony_ci * Returns: 0 on success else ERROR
12968c2ecf20Sopenharmony_ci *
12978c2ecf20Sopenharmony_ci * For the label A//&B//&C this does the perm match for A//&B//&C
12988c2ecf20Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
12998c2ecf20Sopenharmony_ci *        check to be stacked.
13008c2ecf20Sopenharmony_ci */
13018c2ecf20Sopenharmony_cistatic int label_compound_match(struct aa_profile *profile,
13028c2ecf20Sopenharmony_ci				struct aa_label *label,
13038c2ecf20Sopenharmony_ci				unsigned int state, bool subns, u32 request,
13048c2ecf20Sopenharmony_ci				struct aa_perms *perms)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	struct aa_profile *tp;
13078c2ecf20Sopenharmony_ci	struct label_it i;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	/* find first subcomponent that is visible */
13108c2ecf20Sopenharmony_ci	label_for_each(i, label, tp) {
13118c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
13128c2ecf20Sopenharmony_ci			continue;
13138c2ecf20Sopenharmony_ci		state = match_component(profile, tp, state);
13148c2ecf20Sopenharmony_ci		if (!state)
13158c2ecf20Sopenharmony_ci			goto fail;
13168c2ecf20Sopenharmony_ci		goto next;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	/* no component visible */
13208c2ecf20Sopenharmony_ci	*perms = allperms;
13218c2ecf20Sopenharmony_ci	return 0;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_cinext:
13248c2ecf20Sopenharmony_ci	label_for_each_cont(i, label, tp) {
13258c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
13268c2ecf20Sopenharmony_ci			continue;
13278c2ecf20Sopenharmony_ci		state = aa_dfa_match(profile->policy.dfa, state, "//&");
13288c2ecf20Sopenharmony_ci		state = match_component(profile, tp, state);
13298c2ecf20Sopenharmony_ci		if (!state)
13308c2ecf20Sopenharmony_ci			goto fail;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci	aa_compute_perms(profile->policy.dfa, state, perms);
13338c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, perms);
13348c2ecf20Sopenharmony_ci	if ((perms->allow & request) != request)
13358c2ecf20Sopenharmony_ci		return -EACCES;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_cifail:
13408c2ecf20Sopenharmony_ci	*perms = nullperms;
13418c2ecf20Sopenharmony_ci	return state;
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci/**
13458c2ecf20Sopenharmony_ci * label_components_match - find perms for all subcomponents of a label
13468c2ecf20Sopenharmony_ci * @profile: profile to find perms for
13478c2ecf20Sopenharmony_ci * @label: label to check access permissions for
13488c2ecf20Sopenharmony_ci * @start: state to start match in
13498c2ecf20Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
13508c2ecf20Sopenharmony_ci * @request: permissions to request
13518c2ecf20Sopenharmony_ci * @perms: an initialized perms struct to add accumulation to
13528c2ecf20Sopenharmony_ci *
13538c2ecf20Sopenharmony_ci * Returns: 0 on success else ERROR
13548c2ecf20Sopenharmony_ci *
13558c2ecf20Sopenharmony_ci * For the label A//&B//&C this does the perm match for each of A and B and C
13568c2ecf20Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
13578c2ecf20Sopenharmony_ci *        check to be stacked.
13588c2ecf20Sopenharmony_ci */
13598c2ecf20Sopenharmony_cistatic int label_components_match(struct aa_profile *profile,
13608c2ecf20Sopenharmony_ci				  struct aa_label *label, unsigned int start,
13618c2ecf20Sopenharmony_ci				  bool subns, u32 request,
13628c2ecf20Sopenharmony_ci				  struct aa_perms *perms)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	struct aa_profile *tp;
13658c2ecf20Sopenharmony_ci	struct label_it i;
13668c2ecf20Sopenharmony_ci	struct aa_perms tmp;
13678c2ecf20Sopenharmony_ci	unsigned int state = 0;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	/* find first subcomponent to test */
13708c2ecf20Sopenharmony_ci	label_for_each(i, label, tp) {
13718c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
13728c2ecf20Sopenharmony_ci			continue;
13738c2ecf20Sopenharmony_ci		state = match_component(profile, tp, start);
13748c2ecf20Sopenharmony_ci		if (!state)
13758c2ecf20Sopenharmony_ci			goto fail;
13768c2ecf20Sopenharmony_ci		goto next;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/* no subcomponents visible - no change in perms */
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_cinext:
13838c2ecf20Sopenharmony_ci	aa_compute_perms(profile->policy.dfa, state, &tmp);
13848c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, &tmp);
13858c2ecf20Sopenharmony_ci	aa_perms_accum(perms, &tmp);
13868c2ecf20Sopenharmony_ci	label_for_each_cont(i, label, tp) {
13878c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
13888c2ecf20Sopenharmony_ci			continue;
13898c2ecf20Sopenharmony_ci		state = match_component(profile, tp, start);
13908c2ecf20Sopenharmony_ci		if (!state)
13918c2ecf20Sopenharmony_ci			goto fail;
13928c2ecf20Sopenharmony_ci		aa_compute_perms(profile->policy.dfa, state, &tmp);
13938c2ecf20Sopenharmony_ci		aa_apply_modes_to_perms(profile, &tmp);
13948c2ecf20Sopenharmony_ci		aa_perms_accum(perms, &tmp);
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if ((perms->allow & request) != request)
13988c2ecf20Sopenharmony_ci		return -EACCES;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	return 0;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_cifail:
14038c2ecf20Sopenharmony_ci	*perms = nullperms;
14048c2ecf20Sopenharmony_ci	return -EACCES;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci/**
14088c2ecf20Sopenharmony_ci * aa_label_match - do a multi-component label match
14098c2ecf20Sopenharmony_ci * @profile: profile to match against (NOT NULL)
14108c2ecf20Sopenharmony_ci * @label: label to match (NOT NULL)
14118c2ecf20Sopenharmony_ci * @state: state to start in
14128c2ecf20Sopenharmony_ci * @subns: whether to match subns components
14138c2ecf20Sopenharmony_ci * @request: permission request
14148c2ecf20Sopenharmony_ci * @perms: Returns computed perms (NOT NULL)
14158c2ecf20Sopenharmony_ci *
14168c2ecf20Sopenharmony_ci * Returns: the state the match finished in, may be the none matching state
14178c2ecf20Sopenharmony_ci */
14188c2ecf20Sopenharmony_ciint aa_label_match(struct aa_profile *profile, struct aa_label *label,
14198c2ecf20Sopenharmony_ci		   unsigned int state, bool subns, u32 request,
14208c2ecf20Sopenharmony_ci		   struct aa_perms *perms)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	int error = label_compound_match(profile, label, state, subns, request,
14238c2ecf20Sopenharmony_ci					 perms);
14248c2ecf20Sopenharmony_ci	if (!error)
14258c2ecf20Sopenharmony_ci		return error;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	*perms = allperms;
14288c2ecf20Sopenharmony_ci	return label_components_match(profile, label, state, subns, request,
14298c2ecf20Sopenharmony_ci				      perms);
14308c2ecf20Sopenharmony_ci}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci/**
14348c2ecf20Sopenharmony_ci * aa_update_label_name - update a label to have a stored name
14358c2ecf20Sopenharmony_ci * @ns: ns being viewed from (NOT NULL)
14368c2ecf20Sopenharmony_ci * @label: label to update (NOT NULL)
14378c2ecf20Sopenharmony_ci * @gfp: type of memory allocation
14388c2ecf20Sopenharmony_ci *
14398c2ecf20Sopenharmony_ci * Requires: labels_set(label) not locked in caller
14408c2ecf20Sopenharmony_ci *
14418c2ecf20Sopenharmony_ci * note: only updates the label name if it does not have a name already
14428c2ecf20Sopenharmony_ci *       and if it is in the labelset
14438c2ecf20Sopenharmony_ci */
14448c2ecf20Sopenharmony_cibool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
14478c2ecf20Sopenharmony_ci	unsigned long flags;
14488c2ecf20Sopenharmony_ci	char __counted *name;
14498c2ecf20Sopenharmony_ci	bool res = false;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	AA_BUG(!ns);
14528c2ecf20Sopenharmony_ci	AA_BUG(!label);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	if (label->hname || labels_ns(label) != ns)
14558c2ecf20Sopenharmony_ci		return res;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	if (aa_label_acntsxprint(&name, ns, label, FLAGS_NONE, gfp) < 0)
14588c2ecf20Sopenharmony_ci		return res;
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	ls = labels_set(label);
14618c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
14628c2ecf20Sopenharmony_ci	if (!label->hname && label->flags & FLAG_IN_TREE) {
14638c2ecf20Sopenharmony_ci		label->hname = name;
14648c2ecf20Sopenharmony_ci		res = true;
14658c2ecf20Sopenharmony_ci	} else
14668c2ecf20Sopenharmony_ci		aa_put_str(name);
14678c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	return res;
14708c2ecf20Sopenharmony_ci}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci/*
14738c2ecf20Sopenharmony_ci * cached label name is present and visible
14748c2ecf20Sopenharmony_ci * @label->hname only exists if label is namespace hierachical
14758c2ecf20Sopenharmony_ci */
14768c2ecf20Sopenharmony_cistatic inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
14778c2ecf20Sopenharmony_ci				   int flags)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	if (label->hname && (!ns || labels_ns(label) == ns) &&
14808c2ecf20Sopenharmony_ci	    !(flags & ~FLAG_SHOW_MODE))
14818c2ecf20Sopenharmony_ci		return true;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	return false;
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci/* helper macro for snprint routines */
14878c2ecf20Sopenharmony_ci#define update_for_len(total, len, size, str)	\
14888c2ecf20Sopenharmony_cido {					\
14898c2ecf20Sopenharmony_ci	size_t ulen = len;		\
14908c2ecf20Sopenharmony_ci					\
14918c2ecf20Sopenharmony_ci	AA_BUG(len < 0);		\
14928c2ecf20Sopenharmony_ci	total += ulen;			\
14938c2ecf20Sopenharmony_ci	ulen = min(ulen, size);		\
14948c2ecf20Sopenharmony_ci	size -= ulen;			\
14958c2ecf20Sopenharmony_ci	str += ulen;			\
14968c2ecf20Sopenharmony_ci} while (0)
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci/**
14998c2ecf20Sopenharmony_ci * aa_profile_snxprint - print a profile name to a buffer
15008c2ecf20Sopenharmony_ci * @str: buffer to write to. (MAY BE NULL if @size == 0)
15018c2ecf20Sopenharmony_ci * @size: size of buffer
15028c2ecf20Sopenharmony_ci * @view: namespace profile is being viewed from
15038c2ecf20Sopenharmony_ci * @profile: profile to view (NOT NULL)
15048c2ecf20Sopenharmony_ci * @flags: whether to include the mode string
15058c2ecf20Sopenharmony_ci * @prev_ns: last ns printed when used in compound print
15068c2ecf20Sopenharmony_ci *
15078c2ecf20Sopenharmony_ci * Returns: size of name written or would be written if larger than
15088c2ecf20Sopenharmony_ci *          available buffer
15098c2ecf20Sopenharmony_ci *
15108c2ecf20Sopenharmony_ci * Note: will not print anything if the profile is not visible
15118c2ecf20Sopenharmony_ci */
15128c2ecf20Sopenharmony_cistatic int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
15138c2ecf20Sopenharmony_ci			       struct aa_profile *profile, int flags,
15148c2ecf20Sopenharmony_ci			       struct aa_ns **prev_ns)
15158c2ecf20Sopenharmony_ci{
15168c2ecf20Sopenharmony_ci	const char *ns_name = NULL;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	AA_BUG(!str && size != 0);
15198c2ecf20Sopenharmony_ci	AA_BUG(!profile);
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	if (!view)
15228c2ecf20Sopenharmony_ci		view = profiles_ns(profile);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (view != profile->ns &&
15258c2ecf20Sopenharmony_ci	    (!prev_ns || (*prev_ns != profile->ns))) {
15268c2ecf20Sopenharmony_ci		if (prev_ns)
15278c2ecf20Sopenharmony_ci			*prev_ns = profile->ns;
15288c2ecf20Sopenharmony_ci		ns_name = aa_ns_name(view, profile->ns,
15298c2ecf20Sopenharmony_ci				     flags & FLAG_VIEW_SUBNS);
15308c2ecf20Sopenharmony_ci		if (ns_name == aa_hidden_ns_name) {
15318c2ecf20Sopenharmony_ci			if (flags & FLAG_HIDDEN_UNCONFINED)
15328c2ecf20Sopenharmony_ci				return snprintf(str, size, "%s", "unconfined");
15338c2ecf20Sopenharmony_ci			return snprintf(str, size, "%s", ns_name);
15348c2ecf20Sopenharmony_ci		}
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	if ((flags & FLAG_SHOW_MODE) && profile != profile->ns->unconfined) {
15388c2ecf20Sopenharmony_ci		const char *modestr = aa_profile_mode_names[profile->mode];
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci		if (ns_name)
15418c2ecf20Sopenharmony_ci			return snprintf(str, size, ":%s:%s (%s)", ns_name,
15428c2ecf20Sopenharmony_ci					profile->base.hname, modestr);
15438c2ecf20Sopenharmony_ci		return snprintf(str, size, "%s (%s)", profile->base.hname,
15448c2ecf20Sopenharmony_ci				modestr);
15458c2ecf20Sopenharmony_ci	}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	if (ns_name)
15488c2ecf20Sopenharmony_ci		return snprintf(str, size, ":%s:%s", ns_name,
15498c2ecf20Sopenharmony_ci				profile->base.hname);
15508c2ecf20Sopenharmony_ci	return snprintf(str, size, "%s", profile->base.hname);
15518c2ecf20Sopenharmony_ci}
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_cistatic const char *label_modename(struct aa_ns *ns, struct aa_label *label,
15548c2ecf20Sopenharmony_ci				  int flags)
15558c2ecf20Sopenharmony_ci{
15568c2ecf20Sopenharmony_ci	struct aa_profile *profile;
15578c2ecf20Sopenharmony_ci	struct label_it i;
15588c2ecf20Sopenharmony_ci	int mode = -1, count = 0;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	label_for_each(i, label, profile) {
15618c2ecf20Sopenharmony_ci		if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
15628c2ecf20Sopenharmony_ci			count++;
15638c2ecf20Sopenharmony_ci			if (profile == profile->ns->unconfined)
15648c2ecf20Sopenharmony_ci				/* special case unconfined so stacks with
15658c2ecf20Sopenharmony_ci				 * unconfined don't report as mixed. ie.
15668c2ecf20Sopenharmony_ci				 * profile_foo//&:ns1:unconfined (mixed)
15678c2ecf20Sopenharmony_ci				 */
15688c2ecf20Sopenharmony_ci				continue;
15698c2ecf20Sopenharmony_ci			if (mode == -1)
15708c2ecf20Sopenharmony_ci				mode = profile->mode;
15718c2ecf20Sopenharmony_ci			else if (mode != profile->mode)
15728c2ecf20Sopenharmony_ci				return "mixed";
15738c2ecf20Sopenharmony_ci		}
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	if (count == 0)
15778c2ecf20Sopenharmony_ci		return "-";
15788c2ecf20Sopenharmony_ci	if (mode == -1)
15798c2ecf20Sopenharmony_ci		/* everything was unconfined */
15808c2ecf20Sopenharmony_ci		mode = APPARMOR_UNCONFINED;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	return aa_profile_mode_names[mode];
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci/* if any visible label is not unconfined the display_mode returns true */
15868c2ecf20Sopenharmony_cistatic inline bool display_mode(struct aa_ns *ns, struct aa_label *label,
15878c2ecf20Sopenharmony_ci				int flags)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	if ((flags & FLAG_SHOW_MODE)) {
15908c2ecf20Sopenharmony_ci		struct aa_profile *profile;
15918c2ecf20Sopenharmony_ci		struct label_it i;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci		label_for_each(i, label, profile) {
15948c2ecf20Sopenharmony_ci			if (aa_ns_visible(ns, profile->ns,
15958c2ecf20Sopenharmony_ci					  flags & FLAG_VIEW_SUBNS) &&
15968c2ecf20Sopenharmony_ci			    profile != profile->ns->unconfined)
15978c2ecf20Sopenharmony_ci				return true;
15988c2ecf20Sopenharmony_ci		}
15998c2ecf20Sopenharmony_ci		/* only ns->unconfined in set of profiles in ns */
16008c2ecf20Sopenharmony_ci		return false;
16018c2ecf20Sopenharmony_ci	}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	return false;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci/**
16078c2ecf20Sopenharmony_ci * aa_label_snxprint - print a label name to a string buffer
16088c2ecf20Sopenharmony_ci * @str: buffer to write to. (MAY BE NULL if @size == 0)
16098c2ecf20Sopenharmony_ci * @size: size of buffer
16108c2ecf20Sopenharmony_ci * @ns: namespace profile is being viewed from
16118c2ecf20Sopenharmony_ci * @label: label to view (NOT NULL)
16128c2ecf20Sopenharmony_ci * @flags: whether to include the mode string
16138c2ecf20Sopenharmony_ci *
16148c2ecf20Sopenharmony_ci * Returns: size of name written or would be written if larger than
16158c2ecf20Sopenharmony_ci *          available buffer
16168c2ecf20Sopenharmony_ci *
16178c2ecf20Sopenharmony_ci * Note: labels do not have to be strictly hierarchical to the ns as
16188c2ecf20Sopenharmony_ci *       objects may be shared across different namespaces and thus
16198c2ecf20Sopenharmony_ci *       pickup labeling from each ns.  If a particular part of the
16208c2ecf20Sopenharmony_ci *       label is not visible it will just be excluded.  And if none
16218c2ecf20Sopenharmony_ci *       of the label is visible "---" will be used.
16228c2ecf20Sopenharmony_ci */
16238c2ecf20Sopenharmony_ciint aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
16248c2ecf20Sopenharmony_ci		      struct aa_label *label, int flags)
16258c2ecf20Sopenharmony_ci{
16268c2ecf20Sopenharmony_ci	struct aa_profile *profile;
16278c2ecf20Sopenharmony_ci	struct aa_ns *prev_ns = NULL;
16288c2ecf20Sopenharmony_ci	struct label_it i;
16298c2ecf20Sopenharmony_ci	int count = 0, total = 0;
16308c2ecf20Sopenharmony_ci	ssize_t len;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	AA_BUG(!str && size != 0);
16338c2ecf20Sopenharmony_ci	AA_BUG(!label);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) {
16368c2ecf20Sopenharmony_ci		ns = root_ns;
16378c2ecf20Sopenharmony_ci		len = snprintf(str, size, "_");
16388c2ecf20Sopenharmony_ci		update_for_len(total, len, size, str);
16398c2ecf20Sopenharmony_ci	} else if (!ns) {
16408c2ecf20Sopenharmony_ci		ns = labels_ns(label);
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	label_for_each(i, label, profile) {
16448c2ecf20Sopenharmony_ci		if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
16458c2ecf20Sopenharmony_ci			if (count > 0) {
16468c2ecf20Sopenharmony_ci				len = snprintf(str, size, "//&");
16478c2ecf20Sopenharmony_ci				update_for_len(total, len, size, str);
16488c2ecf20Sopenharmony_ci			}
16498c2ecf20Sopenharmony_ci			len = aa_profile_snxprint(str, size, ns, profile,
16508c2ecf20Sopenharmony_ci						  flags & FLAG_VIEW_SUBNS,
16518c2ecf20Sopenharmony_ci						  &prev_ns);
16528c2ecf20Sopenharmony_ci			update_for_len(total, len, size, str);
16538c2ecf20Sopenharmony_ci			count++;
16548c2ecf20Sopenharmony_ci		}
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (count == 0) {
16588c2ecf20Sopenharmony_ci		if (flags & FLAG_HIDDEN_UNCONFINED)
16598c2ecf20Sopenharmony_ci			return snprintf(str, size, "%s", "unconfined");
16608c2ecf20Sopenharmony_ci		return snprintf(str, size, "%s", aa_hidden_ns_name);
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	/* count == 1 && ... is for backwards compat where the mode
16648c2ecf20Sopenharmony_ci	 * is not displayed for 'unconfined' in the current ns
16658c2ecf20Sopenharmony_ci	 */
16668c2ecf20Sopenharmony_ci	if (display_mode(ns, label, flags)) {
16678c2ecf20Sopenharmony_ci		len = snprintf(str, size, " (%s)",
16688c2ecf20Sopenharmony_ci			       label_modename(ns, label, flags));
16698c2ecf20Sopenharmony_ci		update_for_len(total, len, size, str);
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	return total;
16738c2ecf20Sopenharmony_ci}
16748c2ecf20Sopenharmony_ci#undef update_for_len
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci/**
16778c2ecf20Sopenharmony_ci * aa_label_asxprint - allocate a string buffer and print label into it
16788c2ecf20Sopenharmony_ci * @strp: Returns - the allocated buffer with the label name. (NOT NULL)
16798c2ecf20Sopenharmony_ci * @ns: namespace profile is being viewed from
16808c2ecf20Sopenharmony_ci * @label: label to view (NOT NULL)
16818c2ecf20Sopenharmony_ci * @flags: flags controlling what label info is printed
16828c2ecf20Sopenharmony_ci * @gfp: kernel memory allocation type
16838c2ecf20Sopenharmony_ci *
16848c2ecf20Sopenharmony_ci * Returns: size of name written or would be written if larger than
16858c2ecf20Sopenharmony_ci *          available buffer
16868c2ecf20Sopenharmony_ci */
16878c2ecf20Sopenharmony_ciint aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
16888c2ecf20Sopenharmony_ci		      int flags, gfp_t gfp)
16898c2ecf20Sopenharmony_ci{
16908c2ecf20Sopenharmony_ci	int size;
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	AA_BUG(!strp);
16938c2ecf20Sopenharmony_ci	AA_BUG(!label);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	size = aa_label_snxprint(NULL, 0, ns, label, flags);
16968c2ecf20Sopenharmony_ci	if (size < 0)
16978c2ecf20Sopenharmony_ci		return size;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	*strp = kmalloc(size + 1, gfp);
17008c2ecf20Sopenharmony_ci	if (!*strp)
17018c2ecf20Sopenharmony_ci		return -ENOMEM;
17028c2ecf20Sopenharmony_ci	return aa_label_snxprint(*strp, size + 1, ns, label, flags);
17038c2ecf20Sopenharmony_ci}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci/**
17068c2ecf20Sopenharmony_ci * aa_label_acntsxprint - allocate a __counted string buffer and print label
17078c2ecf20Sopenharmony_ci * @strp: buffer to write to.
17088c2ecf20Sopenharmony_ci * @ns: namespace profile is being viewed from
17098c2ecf20Sopenharmony_ci * @label: label to view (NOT NULL)
17108c2ecf20Sopenharmony_ci * @flags: flags controlling what label info is printed
17118c2ecf20Sopenharmony_ci * @gfp: kernel memory allocation type
17128c2ecf20Sopenharmony_ci *
17138c2ecf20Sopenharmony_ci * Returns: size of name written or would be written if larger than
17148c2ecf20Sopenharmony_ci *          available buffer
17158c2ecf20Sopenharmony_ci */
17168c2ecf20Sopenharmony_ciint aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns,
17178c2ecf20Sopenharmony_ci			 struct aa_label *label, int flags, gfp_t gfp)
17188c2ecf20Sopenharmony_ci{
17198c2ecf20Sopenharmony_ci	int size;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	AA_BUG(!strp);
17228c2ecf20Sopenharmony_ci	AA_BUG(!label);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	size = aa_label_snxprint(NULL, 0, ns, label, flags);
17258c2ecf20Sopenharmony_ci	if (size < 0)
17268c2ecf20Sopenharmony_ci		return size;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	*strp = aa_str_alloc(size + 1, gfp);
17298c2ecf20Sopenharmony_ci	if (!*strp)
17308c2ecf20Sopenharmony_ci		return -ENOMEM;
17318c2ecf20Sopenharmony_ci	return aa_label_snxprint(*strp, size + 1, ns, label, flags);
17328c2ecf20Sopenharmony_ci}
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_civoid aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
17368c2ecf20Sopenharmony_ci		     struct aa_label *label, int flags, gfp_t gfp)
17378c2ecf20Sopenharmony_ci{
17388c2ecf20Sopenharmony_ci	const char *str;
17398c2ecf20Sopenharmony_ci	char *name = NULL;
17408c2ecf20Sopenharmony_ci	int len;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	AA_BUG(!ab);
17438c2ecf20Sopenharmony_ci	AA_BUG(!label);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (!use_label_hname(ns, label, flags) ||
17468c2ecf20Sopenharmony_ci	    display_mode(ns, label, flags)) {
17478c2ecf20Sopenharmony_ci		len  = aa_label_asxprint(&name, ns, label, flags, gfp);
17488c2ecf20Sopenharmony_ci		if (len < 0) {
17498c2ecf20Sopenharmony_ci			AA_DEBUG("label print error");
17508c2ecf20Sopenharmony_ci			return;
17518c2ecf20Sopenharmony_ci		}
17528c2ecf20Sopenharmony_ci		str = name;
17538c2ecf20Sopenharmony_ci	} else {
17548c2ecf20Sopenharmony_ci		str = (char *) label->hname;
17558c2ecf20Sopenharmony_ci		len = strlen(str);
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci	if (audit_string_contains_control(str, len))
17588c2ecf20Sopenharmony_ci		audit_log_n_hex(ab, str, len);
17598c2ecf20Sopenharmony_ci	else
17608c2ecf20Sopenharmony_ci		audit_log_n_string(ab, str, len);
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	kfree(name);
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_civoid aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
17668c2ecf20Sopenharmony_ci			 struct aa_label *label, int flags, gfp_t gfp)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	AA_BUG(!f);
17698c2ecf20Sopenharmony_ci	AA_BUG(!label);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	if (!use_label_hname(ns, label, flags)) {
17728c2ecf20Sopenharmony_ci		char *str;
17738c2ecf20Sopenharmony_ci		int len;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci		len = aa_label_asxprint(&str, ns, label, flags, gfp);
17768c2ecf20Sopenharmony_ci		if (len < 0) {
17778c2ecf20Sopenharmony_ci			AA_DEBUG("label print error");
17788c2ecf20Sopenharmony_ci			return;
17798c2ecf20Sopenharmony_ci		}
17808c2ecf20Sopenharmony_ci		seq_puts(f, str);
17818c2ecf20Sopenharmony_ci		kfree(str);
17828c2ecf20Sopenharmony_ci	} else if (display_mode(ns, label, flags))
17838c2ecf20Sopenharmony_ci		seq_printf(f, "%s (%s)", label->hname,
17848c2ecf20Sopenharmony_ci			   label_modename(ns, label, flags));
17858c2ecf20Sopenharmony_ci	else
17868c2ecf20Sopenharmony_ci		seq_puts(f, label->hname);
17878c2ecf20Sopenharmony_ci}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_civoid aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
17908c2ecf20Sopenharmony_ci		      gfp_t gfp)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	AA_BUG(!label);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	if (!use_label_hname(ns, label, flags)) {
17958c2ecf20Sopenharmony_ci		char *str;
17968c2ecf20Sopenharmony_ci		int len;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci		len = aa_label_asxprint(&str, ns, label, flags, gfp);
17998c2ecf20Sopenharmony_ci		if (len < 0) {
18008c2ecf20Sopenharmony_ci			AA_DEBUG("label print error");
18018c2ecf20Sopenharmony_ci			return;
18028c2ecf20Sopenharmony_ci		}
18038c2ecf20Sopenharmony_ci		pr_info("%s", str);
18048c2ecf20Sopenharmony_ci		kfree(str);
18058c2ecf20Sopenharmony_ci	} else if (display_mode(ns, label, flags))
18068c2ecf20Sopenharmony_ci		pr_info("%s (%s)", label->hname,
18078c2ecf20Sopenharmony_ci		       label_modename(ns, label, flags));
18088c2ecf20Sopenharmony_ci	else
18098c2ecf20Sopenharmony_ci		pr_info("%s", label->hname);
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_civoid aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
18138c2ecf20Sopenharmony_ci{
18148c2ecf20Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
18178c2ecf20Sopenharmony_ci	aa_put_ns(ns);
18188c2ecf20Sopenharmony_ci}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_civoid aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
18218c2ecf20Sopenharmony_ci{
18228c2ecf20Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
18258c2ecf20Sopenharmony_ci	aa_put_ns(ns);
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_civoid aa_label_printk(struct aa_label *label, gfp_t gfp)
18298c2ecf20Sopenharmony_ci{
18308c2ecf20Sopenharmony_ci	struct aa_ns *ns = aa_get_current_ns();
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	aa_label_xprintk(ns, label, FLAG_VIEW_SUBNS, gfp);
18338c2ecf20Sopenharmony_ci	aa_put_ns(ns);
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic int label_count_strn_entries(const char *str, size_t n)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	const char *end = str + n;
18398c2ecf20Sopenharmony_ci	const char *split;
18408c2ecf20Sopenharmony_ci	int count = 1;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	AA_BUG(!str);
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	for (split = aa_label_strn_split(str, end - str);
18458c2ecf20Sopenharmony_ci	     split;
18468c2ecf20Sopenharmony_ci	     split = aa_label_strn_split(str, end - str)) {
18478c2ecf20Sopenharmony_ci		count++;
18488c2ecf20Sopenharmony_ci		str = split + 3;
18498c2ecf20Sopenharmony_ci	}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	return count;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci/*
18558c2ecf20Sopenharmony_ci * ensure stacks with components like
18568c2ecf20Sopenharmony_ci *   :ns:A//&B
18578c2ecf20Sopenharmony_ci * have :ns: applied to both 'A' and 'B' by making the lookup relative
18588c2ecf20Sopenharmony_ci * to the base if the lookup specifies an ns, else making the stacked lookup
18598c2ecf20Sopenharmony_ci * relative to the last embedded ns in the string.
18608c2ecf20Sopenharmony_ci */
18618c2ecf20Sopenharmony_cistatic struct aa_profile *fqlookupn_profile(struct aa_label *base,
18628c2ecf20Sopenharmony_ci					    struct aa_label *currentbase,
18638c2ecf20Sopenharmony_ci					    const char *str, size_t n)
18648c2ecf20Sopenharmony_ci{
18658c2ecf20Sopenharmony_ci	const char *first = skipn_spaces(str, n);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (first && *first == ':')
18688c2ecf20Sopenharmony_ci		return aa_fqlookupn_profile(base, str, n);
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	return aa_fqlookupn_profile(currentbase, str, n);
18718c2ecf20Sopenharmony_ci}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci/**
18748c2ecf20Sopenharmony_ci * aa_label_strn_parse - parse, validate and convert a text string to a label
18758c2ecf20Sopenharmony_ci * @base: base label to use for lookups (NOT NULL)
18768c2ecf20Sopenharmony_ci * @str: null terminated text string (NOT NULL)
18778c2ecf20Sopenharmony_ci * @n: length of str to parse, will stop at \0 if encountered before n
18788c2ecf20Sopenharmony_ci * @gfp: allocation type
18798c2ecf20Sopenharmony_ci * @create: true if should create compound labels if they don't exist
18808c2ecf20Sopenharmony_ci * @force_stack: true if should stack even if no leading &
18818c2ecf20Sopenharmony_ci *
18828c2ecf20Sopenharmony_ci * Returns: the matching refcounted label if present
18838c2ecf20Sopenharmony_ci *     else ERRPTR
18848c2ecf20Sopenharmony_ci */
18858c2ecf20Sopenharmony_cistruct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
18868c2ecf20Sopenharmony_ci				     size_t n, gfp_t gfp, bool create,
18878c2ecf20Sopenharmony_ci				     bool force_stack)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	DEFINE_VEC(profile, vec);
18908c2ecf20Sopenharmony_ci	struct aa_label *label, *currbase = base;
18918c2ecf20Sopenharmony_ci	int i, len, stack = 0, error;
18928c2ecf20Sopenharmony_ci	const char *end = str + n;
18938c2ecf20Sopenharmony_ci	const char *split;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	AA_BUG(!base);
18968c2ecf20Sopenharmony_ci	AA_BUG(!str);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	str = skipn_spaces(str, n);
18998c2ecf20Sopenharmony_ci	if (str == NULL || (AA_DEBUG_LABEL && *str == '_' &&
19008c2ecf20Sopenharmony_ci			    base != &root_ns->unconfined->label))
19018c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	len = label_count_strn_entries(str, end - str);
19048c2ecf20Sopenharmony_ci	if (*str == '&' || force_stack) {
19058c2ecf20Sopenharmony_ci		/* stack on top of base */
19068c2ecf20Sopenharmony_ci		stack = base->size;
19078c2ecf20Sopenharmony_ci		len += stack;
19088c2ecf20Sopenharmony_ci		if (*str == '&')
19098c2ecf20Sopenharmony_ci			str++;
19108c2ecf20Sopenharmony_ci	}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	error = vec_setup(profile, vec, len, gfp);
19138c2ecf20Sopenharmony_ci	if (error)
19148c2ecf20Sopenharmony_ci		return ERR_PTR(error);
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	for (i = 0; i < stack; i++)
19178c2ecf20Sopenharmony_ci		vec[i] = aa_get_profile(base->vec[i]);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	for (split = aa_label_strn_split(str, end - str), i = stack;
19208c2ecf20Sopenharmony_ci	     split && i < len; i++) {
19218c2ecf20Sopenharmony_ci		vec[i] = fqlookupn_profile(base, currbase, str, split - str);
19228c2ecf20Sopenharmony_ci		if (!vec[i])
19238c2ecf20Sopenharmony_ci			goto fail;
19248c2ecf20Sopenharmony_ci		/*
19258c2ecf20Sopenharmony_ci		 * if component specified a new ns it becomes the new base
19268c2ecf20Sopenharmony_ci		 * so that subsequent lookups are relative to it
19278c2ecf20Sopenharmony_ci		 */
19288c2ecf20Sopenharmony_ci		if (vec[i]->ns != labels_ns(currbase))
19298c2ecf20Sopenharmony_ci			currbase = &vec[i]->label;
19308c2ecf20Sopenharmony_ci		str = split + 3;
19318c2ecf20Sopenharmony_ci		split = aa_label_strn_split(str, end - str);
19328c2ecf20Sopenharmony_ci	}
19338c2ecf20Sopenharmony_ci	/* last element doesn't have a split */
19348c2ecf20Sopenharmony_ci	if (i < len) {
19358c2ecf20Sopenharmony_ci		vec[i] = fqlookupn_profile(base, currbase, str, end - str);
19368c2ecf20Sopenharmony_ci		if (!vec[i])
19378c2ecf20Sopenharmony_ci			goto fail;
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci	if (len == 1)
19408c2ecf20Sopenharmony_ci		/* no need to free vec as len < LOCAL_VEC_ENTRIES */
19418c2ecf20Sopenharmony_ci		return &vec[0]->label;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	len -= aa_vec_unique(vec, len, VEC_FLAG_TERMINATE);
19448c2ecf20Sopenharmony_ci	/* TODO: deal with reference labels */
19458c2ecf20Sopenharmony_ci	if (len == 1) {
19468c2ecf20Sopenharmony_ci		label = aa_get_label(&vec[0]->label);
19478c2ecf20Sopenharmony_ci		goto out;
19488c2ecf20Sopenharmony_ci	}
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	if (create)
19518c2ecf20Sopenharmony_ci		label = aa_vec_find_or_create_label(vec, len, gfp);
19528c2ecf20Sopenharmony_ci	else
19538c2ecf20Sopenharmony_ci		label = vec_find(vec, len);
19548c2ecf20Sopenharmony_ci	if (!label)
19558c2ecf20Sopenharmony_ci		goto fail;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ciout:
19588c2ecf20Sopenharmony_ci	/* use adjusted len from after vec_unique, not original */
19598c2ecf20Sopenharmony_ci	vec_cleanup(profile, vec, len);
19608c2ecf20Sopenharmony_ci	return label;
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cifail:
19638c2ecf20Sopenharmony_ci	label = ERR_PTR(-ENOENT);
19648c2ecf20Sopenharmony_ci	goto out;
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_cistruct aa_label *aa_label_parse(struct aa_label *base, const char *str,
19688c2ecf20Sopenharmony_ci				gfp_t gfp, bool create, bool force_stack)
19698c2ecf20Sopenharmony_ci{
19708c2ecf20Sopenharmony_ci	return aa_label_strn_parse(base, str, strlen(str), gfp, create,
19718c2ecf20Sopenharmony_ci				   force_stack);
19728c2ecf20Sopenharmony_ci}
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci/**
19758c2ecf20Sopenharmony_ci * aa_labelset_destroy - remove all labels from the label set
19768c2ecf20Sopenharmony_ci * @ls: label set to cleanup (NOT NULL)
19778c2ecf20Sopenharmony_ci *
19788c2ecf20Sopenharmony_ci * Labels that are removed from the set may still exist beyond the set
19798c2ecf20Sopenharmony_ci * being destroyed depending on their reference counting
19808c2ecf20Sopenharmony_ci */
19818c2ecf20Sopenharmony_civoid aa_labelset_destroy(struct aa_labelset *ls)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct rb_node *node;
19848c2ecf20Sopenharmony_ci	unsigned long flags;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	AA_BUG(!ls);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
19898c2ecf20Sopenharmony_ci	for (node = rb_first(&ls->root); node; node = rb_first(&ls->root)) {
19908c2ecf20Sopenharmony_ci		struct aa_label *this = rb_entry(node, struct aa_label, node);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci		if (labels_ns(this) != root_ns)
19938c2ecf20Sopenharmony_ci			__label_remove(this,
19948c2ecf20Sopenharmony_ci				       ns_unconfined(labels_ns(this)->parent));
19958c2ecf20Sopenharmony_ci		else
19968c2ecf20Sopenharmony_ci			__label_remove(this, NULL);
19978c2ecf20Sopenharmony_ci	}
19988c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
19998c2ecf20Sopenharmony_ci}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci/*
20028c2ecf20Sopenharmony_ci * @ls: labelset to init (NOT NULL)
20038c2ecf20Sopenharmony_ci */
20048c2ecf20Sopenharmony_civoid aa_labelset_init(struct aa_labelset *ls)
20058c2ecf20Sopenharmony_ci{
20068c2ecf20Sopenharmony_ci	AA_BUG(!ls);
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	rwlock_init(&ls->lock);
20098c2ecf20Sopenharmony_ci	ls->root = RB_ROOT;
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_cistatic struct aa_label *labelset_next_stale(struct aa_labelset *ls)
20138c2ecf20Sopenharmony_ci{
20148c2ecf20Sopenharmony_ci	struct aa_label *label;
20158c2ecf20Sopenharmony_ci	struct rb_node *node;
20168c2ecf20Sopenharmony_ci	unsigned long flags;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	AA_BUG(!ls);
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	read_lock_irqsave(&ls->lock, flags);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	__labelset_for_each(ls, node) {
20238c2ecf20Sopenharmony_ci		label = rb_entry(node, struct aa_label, node);
20248c2ecf20Sopenharmony_ci		if ((label_is_stale(label) ||
20258c2ecf20Sopenharmony_ci		     vec_is_stale(label->vec, label->size)) &&
20268c2ecf20Sopenharmony_ci		    __aa_get_label(label))
20278c2ecf20Sopenharmony_ci			goto out;
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	}
20308c2ecf20Sopenharmony_ci	label = NULL;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ciout:
20338c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&ls->lock, flags);
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	return label;
20368c2ecf20Sopenharmony_ci}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci/**
20398c2ecf20Sopenharmony_ci * __label_update - insert updated version of @label into labelset
20408c2ecf20Sopenharmony_ci * @label - the label to update/replace
20418c2ecf20Sopenharmony_ci *
20428c2ecf20Sopenharmony_ci * Returns: new label that is up to date
20438c2ecf20Sopenharmony_ci *     else NULL on failure
20448c2ecf20Sopenharmony_ci *
20458c2ecf20Sopenharmony_ci * Requires: @ns lock be held
20468c2ecf20Sopenharmony_ci *
20478c2ecf20Sopenharmony_ci * Note: worst case is the stale @label does not get updated and has
20488c2ecf20Sopenharmony_ci *       to be updated at a later time.
20498c2ecf20Sopenharmony_ci */
20508c2ecf20Sopenharmony_cistatic struct aa_label *__label_update(struct aa_label *label)
20518c2ecf20Sopenharmony_ci{
20528c2ecf20Sopenharmony_ci	struct aa_label *new, *tmp;
20538c2ecf20Sopenharmony_ci	struct aa_labelset *ls;
20548c2ecf20Sopenharmony_ci	unsigned long flags;
20558c2ecf20Sopenharmony_ci	int i, invcount = 0;
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	AA_BUG(!label);
20588c2ecf20Sopenharmony_ci	AA_BUG(!mutex_is_locked(&labels_ns(label)->lock));
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci	new = aa_label_alloc(label->size, label->proxy, GFP_KERNEL);
20618c2ecf20Sopenharmony_ci	if (!new)
20628c2ecf20Sopenharmony_ci		return NULL;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	/*
20658c2ecf20Sopenharmony_ci	 * while holding the ns_lock will stop profile replacement, removal,
20668c2ecf20Sopenharmony_ci	 * and label updates, label merging and removal can be occurring
20678c2ecf20Sopenharmony_ci	 */
20688c2ecf20Sopenharmony_ci	ls = labels_set(label);
20698c2ecf20Sopenharmony_ci	write_lock_irqsave(&ls->lock, flags);
20708c2ecf20Sopenharmony_ci	for (i = 0; i < label->size; i++) {
20718c2ecf20Sopenharmony_ci		AA_BUG(!label->vec[i]);
20728c2ecf20Sopenharmony_ci		new->vec[i] = aa_get_newest_profile(label->vec[i]);
20738c2ecf20Sopenharmony_ci		AA_BUG(!new->vec[i]);
20748c2ecf20Sopenharmony_ci		AA_BUG(!new->vec[i]->label.proxy);
20758c2ecf20Sopenharmony_ci		AA_BUG(!new->vec[i]->label.proxy->label);
20768c2ecf20Sopenharmony_ci		if (new->vec[i]->label.proxy != label->vec[i]->label.proxy)
20778c2ecf20Sopenharmony_ci			invcount++;
20788c2ecf20Sopenharmony_ci	}
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	/* updated stale label by being removed/renamed from labelset */
20818c2ecf20Sopenharmony_ci	if (invcount) {
20828c2ecf20Sopenharmony_ci		new->size -= aa_vec_unique(&new->vec[0], new->size,
20838c2ecf20Sopenharmony_ci					   VEC_FLAG_TERMINATE);
20848c2ecf20Sopenharmony_ci		/* TODO: deal with reference labels */
20858c2ecf20Sopenharmony_ci		if (new->size == 1) {
20868c2ecf20Sopenharmony_ci			tmp = aa_get_label(&new->vec[0]->label);
20878c2ecf20Sopenharmony_ci			AA_BUG(tmp == label);
20888c2ecf20Sopenharmony_ci			goto remove;
20898c2ecf20Sopenharmony_ci		}
20908c2ecf20Sopenharmony_ci		if (labels_set(label) != labels_set(new)) {
20918c2ecf20Sopenharmony_ci			write_unlock_irqrestore(&ls->lock, flags);
20928c2ecf20Sopenharmony_ci			tmp = aa_label_insert(labels_set(new), new);
20938c2ecf20Sopenharmony_ci			write_lock_irqsave(&ls->lock, flags);
20948c2ecf20Sopenharmony_ci			goto remove;
20958c2ecf20Sopenharmony_ci		}
20968c2ecf20Sopenharmony_ci	} else
20978c2ecf20Sopenharmony_ci		AA_BUG(labels_ns(label) != labels_ns(new));
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	tmp = __label_insert(labels_set(label), new, true);
21008c2ecf20Sopenharmony_ciremove:
21018c2ecf20Sopenharmony_ci	/* ensure label is removed, and redirected correctly */
21028c2ecf20Sopenharmony_ci	__label_remove(label, tmp);
21038c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&ls->lock, flags);
21048c2ecf20Sopenharmony_ci	label_free_or_put_new(tmp, new);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	return tmp;
21078c2ecf20Sopenharmony_ci}
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci/**
21108c2ecf20Sopenharmony_ci * __labelset_update - update labels in @ns
21118c2ecf20Sopenharmony_ci * @ns: namespace to update labels in  (NOT NULL)
21128c2ecf20Sopenharmony_ci *
21138c2ecf20Sopenharmony_ci * Requires: @ns lock be held
21148c2ecf20Sopenharmony_ci *
21158c2ecf20Sopenharmony_ci * Walk the labelset ensuring that all labels are up to date and valid
21168c2ecf20Sopenharmony_ci * Any label that has a stale component is marked stale and replaced and
21178c2ecf20Sopenharmony_ci * by an updated version.
21188c2ecf20Sopenharmony_ci *
21198c2ecf20Sopenharmony_ci * If failures happen due to memory pressures then stale labels will
21208c2ecf20Sopenharmony_ci * be left in place until the next pass.
21218c2ecf20Sopenharmony_ci */
21228c2ecf20Sopenharmony_cistatic void __labelset_update(struct aa_ns *ns)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	struct aa_label *label;
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	AA_BUG(!ns);
21278c2ecf20Sopenharmony_ci	AA_BUG(!mutex_is_locked(&ns->lock));
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	do {
21308c2ecf20Sopenharmony_ci		label = labelset_next_stale(&ns->labels);
21318c2ecf20Sopenharmony_ci		if (label) {
21328c2ecf20Sopenharmony_ci			struct aa_label *l = __label_update(label);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci			aa_put_label(l);
21358c2ecf20Sopenharmony_ci			aa_put_label(label);
21368c2ecf20Sopenharmony_ci		}
21378c2ecf20Sopenharmony_ci	} while (label);
21388c2ecf20Sopenharmony_ci}
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci/**
21418c2ecf20Sopenharmony_ci * __aa_labelset_udate_subtree - update all labels with a stale component
21428c2ecf20Sopenharmony_ci * @ns: ns to start update at (NOT NULL)
21438c2ecf20Sopenharmony_ci *
21448c2ecf20Sopenharmony_ci * Requires: @ns lock be held
21458c2ecf20Sopenharmony_ci *
21468c2ecf20Sopenharmony_ci * Invalidates labels based on @p in @ns and any children namespaces.
21478c2ecf20Sopenharmony_ci */
21488c2ecf20Sopenharmony_civoid __aa_labelset_update_subtree(struct aa_ns *ns)
21498c2ecf20Sopenharmony_ci{
21508c2ecf20Sopenharmony_ci	struct aa_ns *child;
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	AA_BUG(!ns);
21538c2ecf20Sopenharmony_ci	AA_BUG(!mutex_is_locked(&ns->lock));
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	__labelset_update(ns);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	list_for_each_entry(child, &ns->sub_ns, base.list) {
21588c2ecf20Sopenharmony_ci		mutex_lock_nested(&child->lock, child->level);
21598c2ecf20Sopenharmony_ci		__aa_labelset_update_subtree(child);
21608c2ecf20Sopenharmony_ci		mutex_unlock(&child->lock);
21618c2ecf20Sopenharmony_ci	}
21628c2ecf20Sopenharmony_ci}
2163