Blame SOURCES/0095-libsepol-cil-Improve-checking-for-bad-inheritance-pa.patch

71cd55
From 9af91692416d01814f4b2ac22e39d3b57993af4f Mon Sep 17 00:00:00 2001
71cd55
From: James Carter <jwcart2@gmail.com>
71cd55
Date: Wed, 30 Jun 2021 15:12:16 -0400
71cd55
Subject: [PATCH] libsepol/cil: Improve checking for bad inheritance patterns
71cd55
71cd55
commits 37863b0b1444c85a1ddc6c333c8bfea0c678c592 (libsepol/cil:
71cd55
Improve degenerate inheritance check) and
71cd55
74d00a8decebf940d95064ff60042dcb2cbcc2c0 (libsepol/cil: Detect
71cd55
degenerate inheritance and exit with an error) attempted to detect
71cd55
and exit with an error when compiling policies that have degenerate
71cd55
inheritances. These policies result in the exponential growth of memory
71cd55
usage while copying the blocks that are inherited.
71cd55
71cd55
There were two problems with the previous attempts to detect this
71cd55
bad inheritance problem. The first is that the quick check using
71cd55
cil_possible_degenerate_inheritance() did not detect all patterns
71cd55
of degenerate inheritance. The second problem is that the detection
71cd55
of inheritance loops during the CIL_PASS_BLKIN_LINK pass did not
71cd55
detect all inheritance loops which made it possible for the full
71cd55
degenerate inheritance checking done with
71cd55
cil_check_for_degenerate_inheritance() to have a stack overflow
71cd55
when encountering the inheritance loops. Both the degenerate and
71cd55
loop inheritance checks need to be done at the same time and done
71cd55
after the CIL_PASS_BLKIN_LINK pass. Otherwise, if loops are being
71cd55
detected first, then a degenerate policy can cause the consumption
71cd55
of all system memory and if degenerate policy is being detected
71cd55
first, then an inheritance loop can cause a stack overflow.
71cd55
71cd55
With the new approach, the quick check is eliminated and the full
71cd55
check is always done after the CIL_PASS_BLKIN_LINK pass. Because
71cd55
of this the "inheritance_check" field in struct cil_resolve_args
71cd55
is not needed and removed and the functions
71cd55
cil_print_recursive_blockinherit(), cil_check_recursive_blockinherit(),
71cd55
and cil_possible_degenerate_inheritance() have been deleted. The
71cd55
function cil_count_potential() is renamed cil_check_inheritances()
71cd55
and has checks for both degenerate inheritance and inheritance loops.
71cd55
The inheritance checking is improved and uses an approach similar
71cd55
to commit c28525a26fa145cb5fd911fd2a3b9125a275677f (libsepol/cil:
71cd55
Properly check for loops in sets).
71cd55
71cd55
As has been the case with these degenerate inheritance patches,
71cd55
these issues were discovered by the secilc-fuzzer.
71cd55
71cd55
Signed-off-by: James Carter <jwcart2@gmail.com>
71cd55
---
71cd55
 libsepol/cil/src/cil_resolve_ast.c | 172 +++++++----------------------
71cd55
 1 file changed, 42 insertions(+), 130 deletions(-)
71cd55
71cd55
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
71cd55
index 9a02e3867659..145d4e7452dd 100644
71cd55
--- a/libsepol/cil/src/cil_resolve_ast.c
71cd55
+++ b/libsepol/cil/src/cil_resolve_ast.c
71cd55
@@ -64,7 +64,6 @@ struct cil_args_resolve {
71cd55
 	struct cil_list *sensitivityorder_lists;
71cd55
 	struct cil_list *in_list;
71cd55
 	struct cil_stack *disabled_optionals;
71cd55
-	int *inheritance_check;
71cd55
 };
71cd55
 
71cd55
 static struct cil_name * __cil_insert_name(struct cil_db *db, hashtab_key_t key, struct cil_tree_node *ast_node)
71cd55
@@ -2309,100 +2308,8 @@ exit:
71cd55
 	return rc;
71cd55
 }
71cd55
 
71cd55
-static void cil_print_recursive_blockinherit(struct cil_tree_node *bi_node, struct cil_tree_node *terminating_node)
71cd55
-{
71cd55
-	struct cil_list *trace = NULL;
71cd55
-	struct cil_list_item *item = NULL;
71cd55
-	struct cil_tree_node *curr = NULL;
71cd55
-
71cd55
-	cil_list_init(&trace, CIL_NODE);
71cd55
-
71cd55
-	for (curr = bi_node; curr != terminating_node; curr = curr->parent) {
71cd55
-		if (curr->flavor == CIL_BLOCK) {
71cd55
-			cil_list_prepend(trace, CIL_NODE, curr);
71cd55
-		} else if (curr->flavor == CIL_BLOCKINHERIT) {
71cd55
-			if (curr != bi_node) {
71cd55
-				cil_list_prepend(trace, CIL_NODE, NODE(((struct cil_blockinherit *)curr->data)->block));
71cd55
-			}
71cd55
-			cil_list_prepend(trace, CIL_NODE, curr);
71cd55
-		} else {
71cd55
-			cil_list_prepend(trace, CIL_NODE, curr);
71cd55
-		}
71cd55
-	}
71cd55
-	cil_list_prepend(trace, CIL_NODE, terminating_node);
71cd55
-
71cd55
-	cil_list_for_each(item, trace) {
71cd55
-		curr = item->data;
71cd55
-		if (curr->flavor == CIL_BLOCK) {
71cd55
-			cil_tree_log(curr, CIL_ERR, "block %s", DATUM(curr->data)->name);
71cd55
-		} else if (curr->flavor == CIL_BLOCKINHERIT) {
71cd55
-			cil_tree_log(curr, CIL_ERR, "blockinherit %s", ((struct cil_blockinherit *)curr->data)->block_str);
71cd55
-		} else if (curr->flavor == CIL_OPTIONAL) {
71cd55
-			cil_tree_log(curr, CIL_ERR, "optional %s", DATUM(curr->data)->name);
71cd55
-		} else {
71cd55
-			cil_tree_log(curr, CIL_ERR, "%s", cil_node_to_string(curr));
71cd55
-		}
71cd55
-	}
71cd55
-
71cd55
-	cil_list_destroy(&trace, CIL_FALSE);
71cd55
-}
71cd55
-
71cd55
-static int cil_check_recursive_blockinherit(struct cil_tree_node *bi_node)
71cd55
-{
71cd55
-	struct cil_tree_node *curr = NULL;
71cd55
-	struct cil_blockinherit *bi = NULL;
71cd55
-	struct cil_block *block = NULL;
71cd55
-	int rc = SEPOL_ERR;
71cd55
-
71cd55
-	bi = bi_node->data;
71cd55
-
71cd55
-	for (curr = bi_node->parent; curr != NULL; curr = curr->parent) {
71cd55
-		if (curr->flavor != CIL_BLOCK) {
71cd55
-			continue;
71cd55
-		}
71cd55
-
71cd55
-		block = curr->data;
71cd55
-
71cd55
-		if (block != bi->block) {
71cd55
-			continue;
71cd55
-		}
71cd55
-
71cd55
-		cil_log(CIL_ERR, "Recursive blockinherit found:\n");
71cd55
-		cil_print_recursive_blockinherit(bi_node, curr);
71cd55
-
71cd55
-		rc = SEPOL_ERR;
71cd55
-		goto exit;
71cd55
-	}
71cd55
-
71cd55
-	rc = SEPOL_OK;
71cd55
-
71cd55
-exit:
71cd55
-	return rc;
71cd55
-}
71cd55
-
71cd55
-static int cil_possible_degenerate_inheritance(struct cil_tree_node *node)
71cd55
-{
71cd55
-	unsigned depth = 1;
71cd55
-
71cd55
-	node = node->parent;
71cd55
-	while (node && node->flavor != CIL_ROOT) {
71cd55
-		if (node->flavor == CIL_BLOCK) {
71cd55
-			if (((struct cil_block *)(node->data))->bi_nodes != NULL) {
71cd55
-				depth++;
71cd55
-				if (depth >= CIL_DEGENERATE_INHERITANCE_DEPTH) {
71cd55
-					return CIL_TRUE;
71cd55
-				}
71cd55
-			}
71cd55
-		}
71cd55
-		node = node->parent;
71cd55
-	}
71cd55
-
71cd55
-	return CIL_FALSE;
71cd55
-}
71cd55
-
71cd55
 int cil_resolve_blockinherit_link(struct cil_tree_node *current, void *extra_args)
71cd55
 {
71cd55
-	struct cil_args_resolve *args = extra_args;
71cd55
 	struct cil_blockinherit *inherit = current->data;
71cd55
 	struct cil_symtab_datum *block_datum = NULL;
71cd55
 	struct cil_tree_node *node = NULL;
71cd55
@@ -2423,20 +2330,11 @@ int cil_resolve_blockinherit_link(struct cil_tree_node *current, void *extra_arg
71cd55
 
71cd55
 	inherit->block = (struct cil_block *)block_datum;
71cd55
 
71cd55
-	rc = cil_check_recursive_blockinherit(current);
71cd55
-	if (rc != SEPOL_OK) {
71cd55
-			goto exit;
71cd55
-	}
71cd55
-
71cd55
 	if (inherit->block->bi_nodes == NULL) {
71cd55
 		cil_list_init(&inherit->block->bi_nodes, CIL_NODE);
71cd55
 	}
71cd55
 	cil_list_append(inherit->block->bi_nodes, CIL_NODE, current);
71cd55
 
71cd55
-	if (*(args->inheritance_check) == CIL_FALSE) {
71cd55
-		*(args->inheritance_check) = cil_possible_degenerate_inheritance(node);
71cd55
-	}
71cd55
-
71cd55
 	return SEPOL_OK;
71cd55
 
71cd55
 exit:
71cd55
@@ -2466,11 +2364,6 @@ int cil_resolve_blockinherit_copy(struct cil_tree_node *current, void *extra_arg
71cd55
 	}
71cd55
 
71cd55
 	cil_list_for_each(item, block->bi_nodes) {
71cd55
-		rc = cil_check_recursive_blockinherit(item->data);
71cd55
-		if (rc != SEPOL_OK) {
71cd55
-			goto exit;
71cd55
-		}
71cd55
-
71cd55
 		rc = cil_copy_ast(db, current, item->data);
71cd55
 		if (rc != SEPOL_OK) {
71cd55
 			cil_log(CIL_ERR, "Failed to copy block contents into blockinherit\n");
71cd55
@@ -3611,34 +3504,58 @@ static unsigned cil_count_actual(struct cil_tree_node *node)
71cd55
 	return count;
71cd55
 }
71cd55
 
71cd55
-static unsigned cil_count_potential(struct cil_tree_node *node, unsigned max)
71cd55
+static int cil_check_inheritances(struct cil_tree_node *node, unsigned max, unsigned *count, struct cil_stack *stack, unsigned *loop)
71cd55
 {
71cd55
-	unsigned count = 0;
71cd55
+	int rc;
71cd55
 
71cd55
 	if (node->flavor == CIL_BLOCKINHERIT) {
71cd55
 		struct cil_blockinherit *bi = node->data;
71cd55
-		count += 1;
71cd55
+		*count += 1;
71cd55
+		if (*count > max) {
71cd55
+			cil_tree_log(node, CIL_ERR, "Degenerate inheritance detected");
71cd55
+			return SEPOL_ERR;
71cd55
+		}
71cd55
 		if (bi->block) {
71cd55
-			count += cil_count_potential(NODE(bi->block), max);
71cd55
-			if (count > max) {
71cd55
-				return count;
71cd55
+			struct cil_tree_node *block_node = NODE(bi->block);
71cd55
+			struct cil_stack_item *item;
71cd55
+			int i = 0;
71cd55
+			cil_stack_for_each(stack, i, item) {
71cd55
+				if (block_node == (struct cil_tree_node *)item->data) {
71cd55
+					*loop = CIL_TRUE;
71cd55
+					cil_tree_log(block_node, CIL_ERR, "Block inheritance loop found");
71cd55
+					cil_tree_log(node, CIL_ERR, "  blockinherit");
71cd55
+					return SEPOL_ERR;
71cd55
+				}
71cd55
+			}
71cd55
+			cil_stack_push(stack, CIL_BLOCK, block_node);
71cd55
+			rc = cil_check_inheritances(block_node, max, count, stack, loop);
71cd55
+			cil_stack_pop(stack);
71cd55
+			if (rc != SEPOL_OK) {
71cd55
+				if (*loop == CIL_TRUE) {
71cd55
+					cil_tree_log(node, CIL_ERR, "  blockinherit");
71cd55
+				}
71cd55
+				return SEPOL_ERR;
71cd55
 			}
71cd55
 		}
71cd55
 	}
71cd55
 
71cd55
 	for (node = node->cl_head; node; node = node->next) {
71cd55
-		count += cil_count_potential(node, max);
71cd55
-		if (count > max) {
71cd55
-			return count;
71cd55
+		rc = cil_check_inheritances(node, max, count, stack, loop);
71cd55
+		if (rc != SEPOL_OK) {
71cd55
+			return SEPOL_ERR;
71cd55
 		}
71cd55
 	}
71cd55
 
71cd55
-	return count;
71cd55
+	return SEPOL_OK;
71cd55
 }
71cd55
 
71cd55
-static int cil_check_for_degenerate_inheritance(struct cil_tree_node *node)
71cd55
+static int cil_check_for_bad_inheritance(struct cil_tree_node *node)
71cd55
 {
71cd55
-	uint64_t num_actual, num_potential, max;
71cd55
+	unsigned num_actual, max;
71cd55
+	unsigned num_potential = 0;
71cd55
+	unsigned loop = CIL_FALSE;
71cd55
+	struct cil_stack *stack;
71cd55
+	int rc;
71cd55
 
71cd55
 	num_actual = cil_count_actual(node);
71cd55
 
71cd55
@@ -3647,13 +3564,11 @@ static int cil_check_for_degenerate_inheritance(struct cil_tree_node *node)
71cd55
 		max = CIL_DEGENERATE_INHERITANCE_MINIMUM;
71cd55
 	}
71cd55
 
71cd55
-	num_potential = cil_count_potential(node, max);
71cd55
+	cil_stack_init(&stack);
71cd55
+	rc = cil_check_inheritances(node, max, &num_potential, stack, &loop);
71cd55
+	cil_stack_destroy(&stack);
71cd55
 
71cd55
-	if (num_potential > max) {
71cd55
-		return SEPOL_ERR;
71cd55
-	}
71cd55
-
71cd55
-	return SEPOL_OK;
71cd55
+	return rc;
71cd55
 }
71cd55
 
71cd55
 int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
71cd55
@@ -4127,7 +4042,6 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
71cd55
 	struct cil_args_resolve extra_args;
71cd55
 	enum cil_pass pass = CIL_PASS_TIF;
71cd55
 	uint32_t changed = 0;
71cd55
-	int inheritance_check = 0;
71cd55
 
71cd55
 	if (db == NULL || current == NULL) {
71cd55
 		return rc;
71cd55
@@ -4147,7 +4061,6 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
71cd55
 	extra_args.sensitivityorder_lists = NULL;
71cd55
 	extra_args.in_list = NULL;
71cd55
 	extra_args.disabled_optionals = NULL;
71cd55
-	extra_args.inheritance_check = &inheritance_check;
71cd55
 
71cd55
 	cil_list_init(&extra_args.to_destroy, CIL_NODE);
71cd55
 	cil_list_init(&extra_args.sidorder_lists, CIL_LIST_ITEM);
71cd55
@@ -4174,10 +4087,9 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
71cd55
 			cil_list_destroy(&extra_args.in_list, CIL_FALSE);
71cd55
 		}
71cd55
 
71cd55
-		if (pass == CIL_PASS_BLKIN_LINK && inheritance_check == CIL_TRUE) {
71cd55
-			rc = cil_check_for_degenerate_inheritance(current);
71cd55
+		if (pass == CIL_PASS_BLKIN_LINK) {
71cd55
+			rc = cil_check_for_bad_inheritance(current);
71cd55
 			if (rc != SEPOL_OK) {
71cd55
-				cil_log(CIL_ERR, "Degenerate inheritance detected\n");
71cd55
 				rc = SEPOL_ERR;
71cd55
 				goto exit;
71cd55
 			}
71cd55
-- 
71cd55
2.32.0
71cd55