16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * Author: Ondrej Mosnacek <omosnacek@gmail.com>
36cd6a6acSopenharmony_ci *
46cd6a6acSopenharmony_ci * Copyright (C) 2019 Red Hat Inc.
56cd6a6acSopenharmony_ci *
66cd6a6acSopenharmony_ci *  This library is free software; you can redistribute it and/or
76cd6a6acSopenharmony_ci *  modify it under the terms of the GNU Lesser General Public
86cd6a6acSopenharmony_ci *  License as published by the Free Software Foundation; either
96cd6a6acSopenharmony_ci *  version 2.1 of the License, or (at your option) any later version.
106cd6a6acSopenharmony_ci *
116cd6a6acSopenharmony_ci *  This library is distributed in the hope that it will be useful,
126cd6a6acSopenharmony_ci *  but WITHOUT ANY WARRANTY; without even the implied warranty of
136cd6a6acSopenharmony_ci *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
146cd6a6acSopenharmony_ci *  Lesser General Public License for more details.
156cd6a6acSopenharmony_ci *
166cd6a6acSopenharmony_ci *  You should have received a copy of the GNU Lesser General Public
176cd6a6acSopenharmony_ci *  License along with this library; if not, write to the Free Software
186cd6a6acSopenharmony_ci *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
196cd6a6acSopenharmony_ci */
206cd6a6acSopenharmony_ci
216cd6a6acSopenharmony_ci/*
226cd6a6acSopenharmony_ci * Binary policy optimization.
236cd6a6acSopenharmony_ci *
246cd6a6acSopenharmony_ci * Defines the policydb_optimize() function, which finds and removes
256cd6a6acSopenharmony_ci * redundant rules from the binary policy to reduce its size and potentially
266cd6a6acSopenharmony_ci * improve rule matching times. Only rules that are already covered by a
276cd6a6acSopenharmony_ci * more general rule are removed. The resulting policy is functionally
286cd6a6acSopenharmony_ci * equivalent to the original one.
296cd6a6acSopenharmony_ci */
306cd6a6acSopenharmony_ci
316cd6a6acSopenharmony_ci#include <sepol/policydb/policydb.h>
326cd6a6acSopenharmony_ci#include <sepol/policydb/conditional.h>
336cd6a6acSopenharmony_ci
346cd6a6acSopenharmony_ci#include "debug.h"
356cd6a6acSopenharmony_ci#include "private.h"
366cd6a6acSopenharmony_ci
376cd6a6acSopenharmony_ci#define TYPE_VEC_INIT_SIZE 16
386cd6a6acSopenharmony_ci
396cd6a6acSopenharmony_cistruct type_vec {
406cd6a6acSopenharmony_ci	uint32_t *types;
416cd6a6acSopenharmony_ci	unsigned int count, capacity;
426cd6a6acSopenharmony_ci};
436cd6a6acSopenharmony_ci
446cd6a6acSopenharmony_cistatic int type_vec_init(struct type_vec *v)
456cd6a6acSopenharmony_ci{
466cd6a6acSopenharmony_ci	v->capacity = TYPE_VEC_INIT_SIZE;
476cd6a6acSopenharmony_ci	v->count = 0;
486cd6a6acSopenharmony_ci	v->types = calloc(v->capacity, sizeof(*v->types));
496cd6a6acSopenharmony_ci	if (!v->types)
506cd6a6acSopenharmony_ci		return -1;
516cd6a6acSopenharmony_ci	return 0;
526cd6a6acSopenharmony_ci}
536cd6a6acSopenharmony_ci
546cd6a6acSopenharmony_cistatic void type_vec_destroy(struct type_vec *v)
556cd6a6acSopenharmony_ci{
566cd6a6acSopenharmony_ci	free(v->types);
576cd6a6acSopenharmony_ci}
586cd6a6acSopenharmony_ci
596cd6a6acSopenharmony_cistatic int type_vec_append(struct type_vec *v, uint32_t type)
606cd6a6acSopenharmony_ci{
616cd6a6acSopenharmony_ci	if (v->capacity == v->count) {
626cd6a6acSopenharmony_ci		unsigned int new_capacity = v->capacity * 2;
636cd6a6acSopenharmony_ci		uint32_t *new_types = reallocarray(v->types,
646cd6a6acSopenharmony_ci						   new_capacity,
656cd6a6acSopenharmony_ci						   sizeof(*v->types));
666cd6a6acSopenharmony_ci		if (!new_types)
676cd6a6acSopenharmony_ci			return -1;
686cd6a6acSopenharmony_ci
696cd6a6acSopenharmony_ci		v->types = new_types;
706cd6a6acSopenharmony_ci		v->capacity = new_capacity;
716cd6a6acSopenharmony_ci	}
726cd6a6acSopenharmony_ci
736cd6a6acSopenharmony_ci	v->types[v->count++] = type;
746cd6a6acSopenharmony_ci	return 0;
756cd6a6acSopenharmony_ci}
766cd6a6acSopenharmony_ci
776cd6a6acSopenharmony_cistatic int type_vec_contains(const struct type_vec *v, uint32_t type)
786cd6a6acSopenharmony_ci{
796cd6a6acSopenharmony_ci	unsigned int s = 0, e = v->count;
806cd6a6acSopenharmony_ci
816cd6a6acSopenharmony_ci	while (s != e) {
826cd6a6acSopenharmony_ci		unsigned int mid = (s + e) / 2;
836cd6a6acSopenharmony_ci
846cd6a6acSopenharmony_ci		if (v->types[mid] == type)
856cd6a6acSopenharmony_ci			return 1;
866cd6a6acSopenharmony_ci
876cd6a6acSopenharmony_ci		if (v->types[mid] < type)
886cd6a6acSopenharmony_ci			s = mid + 1;
896cd6a6acSopenharmony_ci		else
906cd6a6acSopenharmony_ci			e = mid;
916cd6a6acSopenharmony_ci	}
926cd6a6acSopenharmony_ci	return 0;
936cd6a6acSopenharmony_ci}
946cd6a6acSopenharmony_ci
956cd6a6acSopenharmony_ci/* builds map: type/attribute -> {all attributes that are a superset of it} */
966cd6a6acSopenharmony_cistatic struct type_vec *build_type_map(const policydb_t *p)
976cd6a6acSopenharmony_ci{
986cd6a6acSopenharmony_ci	unsigned int i, k;
996cd6a6acSopenharmony_ci	ebitmap_node_t *n;
1006cd6a6acSopenharmony_ci	struct type_vec *map = calloc(p->p_types.nprim, sizeof(*map));
1016cd6a6acSopenharmony_ci	if (!map)
1026cd6a6acSopenharmony_ci		return NULL;
1036cd6a6acSopenharmony_ci
1046cd6a6acSopenharmony_ci	for (i = 0; i < p->p_types.nprim; i++) {
1056cd6a6acSopenharmony_ci		if (type_vec_init(&map[i]))
1066cd6a6acSopenharmony_ci			goto err;
1076cd6a6acSopenharmony_ci
1086cd6a6acSopenharmony_ci		if (!p->type_val_to_struct[i])
1096cd6a6acSopenharmony_ci			continue;
1106cd6a6acSopenharmony_ci
1116cd6a6acSopenharmony_ci		if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
1126cd6a6acSopenharmony_ci			ebitmap_for_each_positive_bit(&p->type_attr_map[i],
1136cd6a6acSopenharmony_ci						      n, k) {
1146cd6a6acSopenharmony_ci				if (type_vec_append(&map[i], k))
1156cd6a6acSopenharmony_ci					goto err;
1166cd6a6acSopenharmony_ci			}
1176cd6a6acSopenharmony_ci		} else {
1186cd6a6acSopenharmony_ci			ebitmap_t *types_i = &p->attr_type_map[i];
1196cd6a6acSopenharmony_ci
1206cd6a6acSopenharmony_ci			for (k = 0; k < p->p_types.nprim; k++) {
1216cd6a6acSopenharmony_ci				const ebitmap_t *types_k;
1226cd6a6acSopenharmony_ci
1236cd6a6acSopenharmony_ci				if (!p->type_val_to_struct[k] || p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
1246cd6a6acSopenharmony_ci					continue;
1256cd6a6acSopenharmony_ci
1266cd6a6acSopenharmony_ci				types_k = &p->attr_type_map[k];
1276cd6a6acSopenharmony_ci
1286cd6a6acSopenharmony_ci				if (ebitmap_contains(types_k, types_i)) {
1296cd6a6acSopenharmony_ci					if (type_vec_append(&map[i], k))
1306cd6a6acSopenharmony_ci						goto err;
1316cd6a6acSopenharmony_ci				}
1326cd6a6acSopenharmony_ci			}
1336cd6a6acSopenharmony_ci		}
1346cd6a6acSopenharmony_ci	}
1356cd6a6acSopenharmony_ci	return map;
1366cd6a6acSopenharmony_cierr:
1376cd6a6acSopenharmony_ci	for (k = 0; k <= i; k++)
1386cd6a6acSopenharmony_ci		type_vec_destroy(&map[k]);
1396cd6a6acSopenharmony_ci	free(map);
1406cd6a6acSopenharmony_ci	return NULL;
1416cd6a6acSopenharmony_ci}
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_cistatic void destroy_type_map(const policydb_t *p, struct type_vec *type_map)
1446cd6a6acSopenharmony_ci{
1456cd6a6acSopenharmony_ci	unsigned int i;
1466cd6a6acSopenharmony_ci	for (i = 0; i < p->p_types.nprim; i++)
1476cd6a6acSopenharmony_ci		type_vec_destroy(&type_map[i]);
1486cd6a6acSopenharmony_ci	free(type_map);
1496cd6a6acSopenharmony_ci}
1506cd6a6acSopenharmony_ci
1516cd6a6acSopenharmony_cistatic int process_xperms(uint32_t *p1, const uint32_t *p2)
1526cd6a6acSopenharmony_ci{
1536cd6a6acSopenharmony_ci	size_t i;
1546cd6a6acSopenharmony_ci	int ret = 1;
1556cd6a6acSopenharmony_ci
1566cd6a6acSopenharmony_ci	for (i = 0; i < EXTENDED_PERMS_LEN; i++) {
1576cd6a6acSopenharmony_ci		p1[i] &= ~p2[i];
1586cd6a6acSopenharmony_ci		if (p1[i] != 0)
1596cd6a6acSopenharmony_ci			ret = 0;
1606cd6a6acSopenharmony_ci	}
1616cd6a6acSopenharmony_ci	return ret;
1626cd6a6acSopenharmony_ci}
1636cd6a6acSopenharmony_ci
1646cd6a6acSopenharmony_cistatic int process_avtab_datum(uint16_t specified,
1656cd6a6acSopenharmony_ci			       avtab_datum_t *d1, const avtab_datum_t *d2)
1666cd6a6acSopenharmony_ci{
1676cd6a6acSopenharmony_ci	/* inverse logic needed for AUDITDENY rules */
1686cd6a6acSopenharmony_ci	if (specified & AVTAB_AUDITDENY)
1696cd6a6acSopenharmony_ci		return (d1->data |= ~d2->data) == UINT32_C(0xFFFFFFFF);
1706cd6a6acSopenharmony_ci
1716cd6a6acSopenharmony_ci	if (specified & AVTAB_AV)
1726cd6a6acSopenharmony_ci		return (d1->data &= ~d2->data) == 0;
1736cd6a6acSopenharmony_ci
1746cd6a6acSopenharmony_ci	if (specified & AVTAB_XPERMS) {
1756cd6a6acSopenharmony_ci		avtab_extended_perms_t *x1 = d1->xperms;
1766cd6a6acSopenharmony_ci		const avtab_extended_perms_t *x2 = d2->xperms;
1776cd6a6acSopenharmony_ci
1786cd6a6acSopenharmony_ci		if (x1->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
1796cd6a6acSopenharmony_ci			if (x2->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
1806cd6a6acSopenharmony_ci				if (x1->driver != x2->driver)
1816cd6a6acSopenharmony_ci					return 0;
1826cd6a6acSopenharmony_ci				return process_xperms(x1->perms, x2->perms);
1836cd6a6acSopenharmony_ci			}
1846cd6a6acSopenharmony_ci			if (x2->specified == AVTAB_XPERMS_IOCTLDRIVER)
1856cd6a6acSopenharmony_ci				return xperm_test(x1->driver, x2->perms);
1866cd6a6acSopenharmony_ci		} else if (x1->specified == AVTAB_XPERMS_IOCTLDRIVER) {
1876cd6a6acSopenharmony_ci			if (x2->specified == AVTAB_XPERMS_IOCTLFUNCTION)
1886cd6a6acSopenharmony_ci				return 0;
1896cd6a6acSopenharmony_ci
1906cd6a6acSopenharmony_ci			if (x2->specified == AVTAB_XPERMS_IOCTLDRIVER)
1916cd6a6acSopenharmony_ci				return process_xperms(x1->perms, x2->perms);
1926cd6a6acSopenharmony_ci		}
1936cd6a6acSopenharmony_ci		return 0;
1946cd6a6acSopenharmony_ci	}
1956cd6a6acSopenharmony_ci	return 0;
1966cd6a6acSopenharmony_ci}
1976cd6a6acSopenharmony_ci
1986cd6a6acSopenharmony_ci/* checks if avtab contains a rule that covers the given rule */
1996cd6a6acSopenharmony_cistatic int is_avrule_redundant(avtab_ptr_t entry, avtab_t *tab,
2006cd6a6acSopenharmony_ci			       const struct type_vec *type_map,
2016cd6a6acSopenharmony_ci			       unsigned char not_cond)
2026cd6a6acSopenharmony_ci{
2036cd6a6acSopenharmony_ci	unsigned int i, k, s_idx, t_idx;
2046cd6a6acSopenharmony_ci	uint32_t st, tt;
2056cd6a6acSopenharmony_ci	avtab_datum_t *d1, *d2;
2066cd6a6acSopenharmony_ci	avtab_key_t key;
2076cd6a6acSopenharmony_ci
2086cd6a6acSopenharmony_ci	/* we only care about AV rules */
2096cd6a6acSopenharmony_ci	if (!(entry->key.specified & (AVTAB_AV|AVTAB_XPERMS)))
2106cd6a6acSopenharmony_ci		return 0;
2116cd6a6acSopenharmony_ci
2126cd6a6acSopenharmony_ci	s_idx = entry->key.source_type - 1;
2136cd6a6acSopenharmony_ci	t_idx = entry->key.target_type - 1;
2146cd6a6acSopenharmony_ci
2156cd6a6acSopenharmony_ci	key.target_class = entry->key.target_class;
2166cd6a6acSopenharmony_ci	key.specified    = entry->key.specified;
2176cd6a6acSopenharmony_ci
2186cd6a6acSopenharmony_ci	d1 = &entry->datum;
2196cd6a6acSopenharmony_ci
2206cd6a6acSopenharmony_ci	for (i = 0; i < type_map[s_idx].count; i++) {
2216cd6a6acSopenharmony_ci		st = type_map[s_idx].types[i];
2226cd6a6acSopenharmony_ci		key.source_type = st + 1;
2236cd6a6acSopenharmony_ci
2246cd6a6acSopenharmony_ci		for (k = 0; k < type_map[t_idx].count; k++) {
2256cd6a6acSopenharmony_ci			tt = type_map[t_idx].types[k];
2266cd6a6acSopenharmony_ci
2276cd6a6acSopenharmony_ci			if (not_cond && s_idx == st && t_idx == tt)
2286cd6a6acSopenharmony_ci				continue;
2296cd6a6acSopenharmony_ci
2306cd6a6acSopenharmony_ci			key.target_type = tt + 1;
2316cd6a6acSopenharmony_ci
2326cd6a6acSopenharmony_ci			d2 = avtab_search(tab, &key);
2336cd6a6acSopenharmony_ci			if (!d2)
2346cd6a6acSopenharmony_ci				continue;
2356cd6a6acSopenharmony_ci
2366cd6a6acSopenharmony_ci			if (process_avtab_datum(key.specified, d1, d2))
2376cd6a6acSopenharmony_ci				return 1;
2386cd6a6acSopenharmony_ci		}
2396cd6a6acSopenharmony_ci	}
2406cd6a6acSopenharmony_ci	return 0;
2416cd6a6acSopenharmony_ci}
2426cd6a6acSopenharmony_ci
2436cd6a6acSopenharmony_cistatic int is_type_attr(policydb_t *p, unsigned int id)
2446cd6a6acSopenharmony_ci{
2456cd6a6acSopenharmony_ci	return p->type_val_to_struct[id]->flavor == TYPE_ATTRIB;
2466cd6a6acSopenharmony_ci}
2476cd6a6acSopenharmony_ci
2486cd6a6acSopenharmony_cistatic int is_avrule_with_attr(avtab_ptr_t entry, policydb_t *p)
2496cd6a6acSopenharmony_ci{
2506cd6a6acSopenharmony_ci	unsigned int s_idx = entry->key.source_type - 1;
2516cd6a6acSopenharmony_ci	unsigned int t_idx = entry->key.target_type - 1;
2526cd6a6acSopenharmony_ci
2536cd6a6acSopenharmony_ci	return is_type_attr(p, s_idx) || is_type_attr(p, t_idx);
2546cd6a6acSopenharmony_ci}
2556cd6a6acSopenharmony_ci
2566cd6a6acSopenharmony_ci/* checks if conditional list contains a rule that covers the given rule */
2576cd6a6acSopenharmony_cistatic int is_cond_rule_redundant(avtab_ptr_t e1, cond_av_list_t *list,
2586cd6a6acSopenharmony_ci				  const struct type_vec *type_map)
2596cd6a6acSopenharmony_ci{
2606cd6a6acSopenharmony_ci	unsigned int s1, t1, c1, k1, s2, t2, c2, k2;
2616cd6a6acSopenharmony_ci
2626cd6a6acSopenharmony_ci	/* we only care about AV rules */
2636cd6a6acSopenharmony_ci	if (!(e1->key.specified & (AVTAB_AV|AVTAB_XPERMS)))
2646cd6a6acSopenharmony_ci		return 0;
2656cd6a6acSopenharmony_ci
2666cd6a6acSopenharmony_ci	s1 = e1->key.source_type - 1;
2676cd6a6acSopenharmony_ci	t1 = e1->key.target_type - 1;
2686cd6a6acSopenharmony_ci	c1 = e1->key.target_class;
2696cd6a6acSopenharmony_ci	k1 = e1->key.specified;
2706cd6a6acSopenharmony_ci
2716cd6a6acSopenharmony_ci	for (; list; list = list->next) {
2726cd6a6acSopenharmony_ci		avtab_ptr_t e2 = list->node;
2736cd6a6acSopenharmony_ci
2746cd6a6acSopenharmony_ci		s2 = e2->key.source_type - 1;
2756cd6a6acSopenharmony_ci		t2 = e2->key.target_type - 1;
2766cd6a6acSopenharmony_ci		c2 = e2->key.target_class;
2776cd6a6acSopenharmony_ci		k2 = e2->key.specified;
2786cd6a6acSopenharmony_ci
2796cd6a6acSopenharmony_ci		if (k1 != k2 || c1 != c2)
2806cd6a6acSopenharmony_ci			continue;
2816cd6a6acSopenharmony_ci
2826cd6a6acSopenharmony_ci		if (s1 == s2 && t1 == t2)
2836cd6a6acSopenharmony_ci			continue;
2846cd6a6acSopenharmony_ci		if (!type_vec_contains(&type_map[s1], s2))
2856cd6a6acSopenharmony_ci			continue;
2866cd6a6acSopenharmony_ci		if (!type_vec_contains(&type_map[t1], t2))
2876cd6a6acSopenharmony_ci			continue;
2886cd6a6acSopenharmony_ci
2896cd6a6acSopenharmony_ci		if (process_avtab_datum(k1, &e1->datum, &e2->datum))
2906cd6a6acSopenharmony_ci			return 1;
2916cd6a6acSopenharmony_ci	}
2926cd6a6acSopenharmony_ci	return 0;
2936cd6a6acSopenharmony_ci}
2946cd6a6acSopenharmony_ci
2956cd6a6acSopenharmony_cistatic void optimize_avtab(policydb_t *p, const struct type_vec *type_map)
2966cd6a6acSopenharmony_ci{
2976cd6a6acSopenharmony_ci	avtab_t *tab = &p->te_avtab;
2986cd6a6acSopenharmony_ci	unsigned int i;
2996cd6a6acSopenharmony_ci	avtab_ptr_t *cur;
3006cd6a6acSopenharmony_ci
3016cd6a6acSopenharmony_ci	for (i = 0; i < tab->nslot; i++) {
3026cd6a6acSopenharmony_ci		cur = &tab->htable[i];
3036cd6a6acSopenharmony_ci		while (*cur) {
3046cd6a6acSopenharmony_ci			if (is_avrule_redundant(*cur, tab, type_map, 1)) {
3056cd6a6acSopenharmony_ci				/* redundant rule -> remove it */
3066cd6a6acSopenharmony_ci				avtab_ptr_t tmp = *cur;
3076cd6a6acSopenharmony_ci
3086cd6a6acSopenharmony_ci				*cur = tmp->next;
3096cd6a6acSopenharmony_ci				if (tmp->key.specified & AVTAB_XPERMS)
3106cd6a6acSopenharmony_ci					free(tmp->datum.xperms);
3116cd6a6acSopenharmony_ci				free(tmp);
3126cd6a6acSopenharmony_ci
3136cd6a6acSopenharmony_ci				tab->nel--;
3146cd6a6acSopenharmony_ci			} else {
3156cd6a6acSopenharmony_ci				/* rule not redundant -> move to next rule */
3166cd6a6acSopenharmony_ci				cur = &(*cur)->next;
3176cd6a6acSopenharmony_ci			}
3186cd6a6acSopenharmony_ci		}
3196cd6a6acSopenharmony_ci	}
3206cd6a6acSopenharmony_ci}
3216cd6a6acSopenharmony_ci
3226cd6a6acSopenharmony_ci/* find redundant rules in (*cond) and put them into (*del) */
3236cd6a6acSopenharmony_cistatic void optimize_cond_av_list(cond_av_list_t **cond, cond_av_list_t **del,
3246cd6a6acSopenharmony_ci				  policydb_t *p, const struct type_vec *type_map)
3256cd6a6acSopenharmony_ci{
3266cd6a6acSopenharmony_ci	cond_av_list_t **listp = cond;
3276cd6a6acSopenharmony_ci	cond_av_list_t *pcov = NULL;
3286cd6a6acSopenharmony_ci	cond_av_list_t **pcov_cur;
3296cd6a6acSopenharmony_ci
3306cd6a6acSopenharmony_ci	/*
3316cd6a6acSopenharmony_ci	 * Separate out all "potentially covering" rules (src or tgt is an attr)
3326cd6a6acSopenharmony_ci	 * and move them to the end of the list. This is needed to avoid
3336cd6a6acSopenharmony_ci	 * polynomial complexity when almost all rules are expanded.
3346cd6a6acSopenharmony_ci	 */
3356cd6a6acSopenharmony_ci	while (*cond) {
3366cd6a6acSopenharmony_ci		if (is_avrule_with_attr((*cond)->node, p)) {
3376cd6a6acSopenharmony_ci			cond_av_list_t *tmp = *cond;
3386cd6a6acSopenharmony_ci
3396cd6a6acSopenharmony_ci			*cond = tmp->next;
3406cd6a6acSopenharmony_ci			tmp->next = pcov;
3416cd6a6acSopenharmony_ci			pcov = tmp;
3426cd6a6acSopenharmony_ci		} else {
3436cd6a6acSopenharmony_ci			cond = &(*cond)->next;
3446cd6a6acSopenharmony_ci		}
3456cd6a6acSopenharmony_ci	}
3466cd6a6acSopenharmony_ci	/* link the "potentially covering" rules to the end of the list */
3476cd6a6acSopenharmony_ci	*cond = pcov;
3486cd6a6acSopenharmony_ci
3496cd6a6acSopenharmony_ci	/* now go through the list and find the redundant rules */
3506cd6a6acSopenharmony_ci	cond = listp;
3516cd6a6acSopenharmony_ci	pcov_cur = &pcov;
3526cd6a6acSopenharmony_ci	while (*cond) {
3536cd6a6acSopenharmony_ci		/* needed because pcov itself may get deleted */
3546cd6a6acSopenharmony_ci		if (*cond == pcov)
3556cd6a6acSopenharmony_ci			pcov_cur = cond;
3566cd6a6acSopenharmony_ci		/*
3576cd6a6acSopenharmony_ci		 * First check if covered by an unconditional rule, then also
3586cd6a6acSopenharmony_ci		 * check if covered by another rule in the same list.
3596cd6a6acSopenharmony_ci		 */
3606cd6a6acSopenharmony_ci		if (is_avrule_redundant((*cond)->node, &p->te_avtab, type_map, 0) ||
3616cd6a6acSopenharmony_ci		    is_cond_rule_redundant((*cond)->node, *pcov_cur, type_map)) {
3626cd6a6acSopenharmony_ci			cond_av_list_t *tmp = *cond;
3636cd6a6acSopenharmony_ci
3646cd6a6acSopenharmony_ci			*cond = tmp->next;
3656cd6a6acSopenharmony_ci			tmp->next = *del;
3666cd6a6acSopenharmony_ci			*del = tmp;
3676cd6a6acSopenharmony_ci		} else {
3686cd6a6acSopenharmony_ci			cond = &(*cond)->next;
3696cd6a6acSopenharmony_ci		}
3706cd6a6acSopenharmony_ci	}
3716cd6a6acSopenharmony_ci}
3726cd6a6acSopenharmony_ci
3736cd6a6acSopenharmony_cistatic void optimize_cond_avtab(policydb_t *p, const struct type_vec *type_map)
3746cd6a6acSopenharmony_ci{
3756cd6a6acSopenharmony_ci	avtab_t *tab = &p->te_cond_avtab;
3766cd6a6acSopenharmony_ci	unsigned int i;
3776cd6a6acSopenharmony_ci	avtab_ptr_t *cur;
3786cd6a6acSopenharmony_ci	cond_node_t **cond;
3796cd6a6acSopenharmony_ci	cond_av_list_t **avcond, *del = NULL;
3806cd6a6acSopenharmony_ci
3816cd6a6acSopenharmony_ci	/* First go through all conditionals and collect redundant rules. */
3826cd6a6acSopenharmony_ci	cond = &p->cond_list;
3836cd6a6acSopenharmony_ci	while (*cond) {
3846cd6a6acSopenharmony_ci		optimize_cond_av_list(&(*cond)->true_list,  &del, p, type_map);
3856cd6a6acSopenharmony_ci		optimize_cond_av_list(&(*cond)->false_list, &del, p, type_map);
3866cd6a6acSopenharmony_ci		/* TODO: maybe also check for rules present in both lists */
3876cd6a6acSopenharmony_ci
3886cd6a6acSopenharmony_ci		/* nothing left in both lists -> remove the whole conditional */
3896cd6a6acSopenharmony_ci		if (!(*cond)->true_list && !(*cond)->false_list) {
3906cd6a6acSopenharmony_ci			cond_node_t *cond_tmp = *cond;
3916cd6a6acSopenharmony_ci
3926cd6a6acSopenharmony_ci			*cond = cond_tmp->next;
3936cd6a6acSopenharmony_ci			cond_node_destroy(cond_tmp);
3946cd6a6acSopenharmony_ci			free(cond_tmp);
3956cd6a6acSopenharmony_ci		} else {
3966cd6a6acSopenharmony_ci			cond = &(*cond)->next;
3976cd6a6acSopenharmony_ci		}
3986cd6a6acSopenharmony_ci	}
3996cd6a6acSopenharmony_ci
4006cd6a6acSopenharmony_ci	if (!del)
4016cd6a6acSopenharmony_ci		return;
4026cd6a6acSopenharmony_ci
4036cd6a6acSopenharmony_ci	/*
4046cd6a6acSopenharmony_ci	 * Now go through the whole cond_avtab and remove all rules that are
4056cd6a6acSopenharmony_ci	 * found in the 'del' list.
4066cd6a6acSopenharmony_ci	 */
4076cd6a6acSopenharmony_ci	for (i = 0; i < tab->nslot; i++) {
4086cd6a6acSopenharmony_ci		cur = &tab->htable[i];
4096cd6a6acSopenharmony_ci		while (*cur) {
4106cd6a6acSopenharmony_ci			int redundant = 0;
4116cd6a6acSopenharmony_ci			avcond = &del;
4126cd6a6acSopenharmony_ci			while (*avcond) {
4136cd6a6acSopenharmony_ci				if ((*avcond)->node == *cur) {
4146cd6a6acSopenharmony_ci					cond_av_list_t *cond_tmp = *avcond;
4156cd6a6acSopenharmony_ci
4166cd6a6acSopenharmony_ci					*avcond = cond_tmp->next;
4176cd6a6acSopenharmony_ci					free(cond_tmp);
4186cd6a6acSopenharmony_ci					redundant = 1;
4196cd6a6acSopenharmony_ci					break;
4206cd6a6acSopenharmony_ci				} else {
4216cd6a6acSopenharmony_ci					avcond = &(*avcond)->next;
4226cd6a6acSopenharmony_ci				}
4236cd6a6acSopenharmony_ci			}
4246cd6a6acSopenharmony_ci			if (redundant) {
4256cd6a6acSopenharmony_ci				avtab_ptr_t tmp = *cur;
4266cd6a6acSopenharmony_ci
4276cd6a6acSopenharmony_ci				*cur = tmp->next;
4286cd6a6acSopenharmony_ci				if (tmp->key.specified & AVTAB_XPERMS)
4296cd6a6acSopenharmony_ci					free(tmp->datum.xperms);
4306cd6a6acSopenharmony_ci				free(tmp);
4316cd6a6acSopenharmony_ci
4326cd6a6acSopenharmony_ci				tab->nel--;
4336cd6a6acSopenharmony_ci			} else {
4346cd6a6acSopenharmony_ci				cur = &(*cur)->next;
4356cd6a6acSopenharmony_ci			}
4366cd6a6acSopenharmony_ci		}
4376cd6a6acSopenharmony_ci	}
4386cd6a6acSopenharmony_ci}
4396cd6a6acSopenharmony_ci
4406cd6a6acSopenharmony_ciint policydb_optimize(policydb_t *p)
4416cd6a6acSopenharmony_ci{
4426cd6a6acSopenharmony_ci	struct type_vec *type_map;
4436cd6a6acSopenharmony_ci
4446cd6a6acSopenharmony_ci	if (p->policy_type != POLICY_KERN)
4456cd6a6acSopenharmony_ci		return -1;
4466cd6a6acSopenharmony_ci
4476cd6a6acSopenharmony_ci	if (p->policyvers >= POLICYDB_VERSION_AVTAB && p->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
4486cd6a6acSopenharmony_ci		/*
4496cd6a6acSopenharmony_ci		 * For policy versions between 20 and 23, attributes exist in the policy,
4506cd6a6acSopenharmony_ci		 * but only in the type_attr_map. This means that there are gaps in both
4516cd6a6acSopenharmony_ci		 * the type_val_to_struct and p_type_val_to_name arrays and policy rules
4526cd6a6acSopenharmony_ci		 * can refer to those gaps.
4536cd6a6acSopenharmony_ci		 */
4546cd6a6acSopenharmony_ci		ERR(NULL, "Optimizing policy versions between 20 and 23 is not supported");
4556cd6a6acSopenharmony_ci		return -1;
4566cd6a6acSopenharmony_ci	}
4576cd6a6acSopenharmony_ci
4586cd6a6acSopenharmony_ci	type_map = build_type_map(p);
4596cd6a6acSopenharmony_ci	if (!type_map)
4606cd6a6acSopenharmony_ci		return -1;
4616cd6a6acSopenharmony_ci
4626cd6a6acSopenharmony_ci	optimize_avtab(p, type_map);
4636cd6a6acSopenharmony_ci	optimize_cond_avtab(p, type_map);
4646cd6a6acSopenharmony_ci
4656cd6a6acSopenharmony_ci	destroy_type_map(p, type_map);
4666cd6a6acSopenharmony_ci	return 0;
4676cd6a6acSopenharmony_ci}
468