[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