[PATCH] libsepol: Cap max depth and processed nodes for cil_tree_walk()

Stephen Smalley <[email protected]> Mon, 3 Aug 2026 10:23:59 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <[email protected]>
Cap the max tree depth and number of processed nodes for
cil_tree_walk() to avoid stack overflows and OOM conditions.

Reported-by: oss-fuzz (issue 471608794)
Signed-off-by: Stephen Smalley <[email protected]>
---
 libsepol/cil/src/cil_tree.c | 65 ++++++++++++++++++++++++++++++-------
 1 file changed, 53 insertions(+), 12 deletions(-)

diff --git a/libsepol/cil/src/cil_tree.c b/libsepol/cil/src/cil_tree.c
index 63dd33dd..17f19256 100644
--- a/libsepol/cil/src/cil_tree.c
+++ b/libsepol/cil/src/cil_tree.c
@@ -41,6 +41,9 @@
 #include "cil_parser.h"
 #include "cil_strpool.h"
 
+#define MAX_DEPTH (256)
+#define MAX_NODES (1U << 20)
+
 struct cil_tree_node *cil_tree_get_next_path(struct cil_tree_node *node,
 					     char **info_kind,
 					     uint32_t *hll_line, char **path)
@@ -311,25 +314,41 @@ void cil_tree_node_remove(struct cil_tree_node *node)
    extra_args:               any additional data to be passed to the helper functions
 */
 
+static int cil_tree_walk_helper(
+	struct cil_tree_node *node,
+	int (*process_node)(struct cil_tree_node *node, uint32_t *finished,
+			    void *extra_args),
+	int (*first_child)(struct cil_tree_node *node, void *extra_args),
+	int (*last_child)(struct cil_tree_node *node, void *extra_args),
+	void *extra_args, unsigned depth, unsigned *processed);
+
 static int cil_tree_walk_core(
 	struct cil_tree_node *node,
 	int (*process_node)(struct cil_tree_node *node, uint32_t *finished,
 			    void *extra_args),
 	int (*first_child)(struct cil_tree_node *node, void *extra_args),
 	int (*last_child)(struct cil_tree_node *node, void *extra_args),
-	void *extra_args)
+	void *extra_args, unsigned depth, unsigned *processed)
 {
 	int rc = SEPOL_ERR;
 
 	while (node) {
 		uint32_t finished = CIL_TREE_SKIP_NOTHING;
 
+		if (*processed >= MAX_NODES) {
+			cil_tree_log(node, CIL_ERR,
+				     "Exceeded max tree processed nodes (%u)",
+				     MAX_NODES);
+			return SEPOL_ERR;
+		}
+
 		if (process_node != NULL) {
 			rc = (*process_node)(node, &finished, extra_args);
 			if (rc != SEPOL_OK) {
 				cil_tree_log(node, CIL_INFO, "Problem");
 				return rc;
 			}
+			(*processed)++;
 		}
 
 		if (finished & CIL_TREE_SKIP_NEXT) {
@@ -337,8 +356,10 @@ static int cil_tree_walk_core(
 		}
 
 		if (node->cl_head != NULL && !(finished & CIL_TREE_SKIP_HEAD)) {
-			rc = cil_tree_walk(node, process_node, first_child,
-					   last_child, extra_args);
+			rc = cil_tree_walk_helper(node, process_node,
+						  first_child, last_child,
+						  extra_args, depth + 1,
+						  processed);
 			if (rc != SEPOL_OK) {
 				return rc;
 			}
@@ -350,17 +371,22 @@ static int cil_tree_walk_core(
 	return SEPOL_OK;
 }
 
-int cil_tree_walk(struct cil_tree_node *node,
-		  int (*process_node)(struct cil_tree_node *node,
-				      uint32_t *finished, void *extra_args),
-		  int (*first_child)(struct cil_tree_node *node,
-				     void *extra_args),
-		  int (*last_child)(struct cil_tree_node *node,
-				    void *extra_args),
-		  void *extra_args)
+static int cil_tree_walk_helper(
+	struct cil_tree_node *node,
+	int (*process_node)(struct cil_tree_node *node, uint32_t *finished,
+			    void *extra_args),
+	int (*first_child)(struct cil_tree_node *node, void *extra_args),
+	int (*last_child)(struct cil_tree_node *node, void *extra_args),
+	void *extra_args, unsigned depth, unsigned *processed)
 {
 	int rc = SEPOL_ERR;
 
+	if (depth >= MAX_DEPTH) {
+		cil_tree_log(node, CIL_ERR, "Exceeded max tree depth (%u)",
+			     MAX_DEPTH);
+		return SEPOL_ERR;
+	}
+
 	if (!node || !node->cl_head) {
 		return SEPOL_OK;
 	}
@@ -374,7 +400,7 @@ int cil_tree_walk(struct cil_tree_node *node,
 	}
 
 	rc = cil_tree_walk_core(node->cl_head, process_node, first_child,
-				last_child, extra_args);
+				last_child, extra_args, depth, processed);
 	if (rc != SEPOL_OK) {
 		return rc;
 	}
@@ -389,3 +415,18 @@ int cil_tree_walk(struct cil_tree_node *node,
 
 	return SEPOL_OK;
 }
+
+int cil_tree_walk(struct cil_tree_node *node,
+		  int (*process_node)(struct cil_tree_node *node,
+				      uint32_t *finished, void *extra_args),
+		  int (*first_child)(struct cil_tree_node *node,
+				     void *extra_args),
+		  int (*last_child)(struct cil_tree_node *node,
+				    void *extra_args),
+		  void *extra_args)
+{
+	unsigned processed = 0;
+
+	return cil_tree_walk_helper(node, process_node, first_child, last_child,
+				    extra_args, 0, &processed);
+}
-- 
2.55.0