Blame SOURCES/0013-libsepol-cil-Destroy-disabled-optional-blocks-after-.patch

212ad1
From d668f8e3a0a0361c03881ae3f00509196eaee064 Mon Sep 17 00:00:00 2001
212ad1
From: James Carter <jwcart2@gmail.com>
212ad1
Date: Mon, 8 Feb 2021 11:23:42 -0500
212ad1
Subject: [PATCH] libsepol/cil: Destroy disabled optional blocks after pass is
212ad1
 complete
212ad1
212ad1
Nicolas Iooss reports:
212ad1
  I am continuing to investigate OSS-Fuzz crashes and the following one
212ad1
  is quite complex. Here is a CIL policy which triggers a
212ad1
  heap-use-after-free error in the CIL compiler:
212ad1
212ad1
  (class CLASS (PERM2))
212ad1
  (classorder (CLASS))
212ad1
  (classpermission CLSPRM)
212ad1
  (optional o
212ad1
      (mlsvalidatetrans x (domby l1 h1))
212ad1
      (common CLSCOMMON (PERM1))
212ad1
      (classcommon CLASS CLSCOMMON)
212ad1
  )
212ad1
  (classpermissionset CLSPRM (CLASS (PERM1)))
212ad1
212ad1
  The issue is that the mlsvalidatetrans fails to resolve in pass
212ad1
  CIL_PASS_MISC3, which comes after the resolution of classcommon (in
212ad1
  pass CIL_PASS_MISC2). So:
212ad1
212ad1
  * In pass CIL_PASS_MISC2, the optional block still exists, the
212ad1
  classcommon is resolved and class CLASS is linked with common
212ad1
  CLSCOMMON.
212ad1
  * In pass CIL_PASS_MISC3, the optional block is destroyed, including
212ad1
  the common CLSCOMMON.
212ad1
  * When classpermissionset is resolved, function cil_resolve_classperms
212ad1
  uses "common_symtab = &class->common->perms;", which has been freed.
212ad1
  The use-after-free issue occurs in __cil_resolve_perms (in
212ad1
  libsepol/cil/src/cil_resolve_ast.c):
212ad1
212ad1
    // common_symtab was freed
212ad1
    rc = cil_symtab_get_datum(common_symtab, curr->data, &perm_datum);
212ad1
212ad1
The fundamental problem here is that when the optional block is
212ad1
disabled it is immediately destroyed in the middle of the pass, so
212ad1
the class has not been reset and still refers to the now destroyed
212ad1
common when the classpermissionset is resolved later in the same pass.
212ad1
212ad1
Added a list, disabled_optionals, to struct cil_args_resolve which is
212ad1
passed when resolving the tree. When optionals are disabled, they are
212ad1
now added to this list and then are destroyed after the tree has been
212ad1
reset between passes.
212ad1
212ad1
Reported-by: Nicolas Iooss <nicolas.iooss@m4x.org>
212ad1
Signed-off-by: James Carter <jwcart2@gmail.com>
212ad1
Acked-by: Nicolas Iooss <nicolas.iooss@m4x.org>
212ad1
---
212ad1
 libsepol/cil/src/cil_resolve_ast.c | 11 ++++++++++-
212ad1
 1 file changed, 10 insertions(+), 1 deletion(-)
212ad1
212ad1
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
212ad1
index ed876260..979fa17d 100644
212ad1
--- a/libsepol/cil/src/cil_resolve_ast.c
212ad1
+++ b/libsepol/cil/src/cil_resolve_ast.c
212ad1
@@ -51,6 +51,7 @@ struct cil_args_resolve {
212ad1
 	struct cil_db *db;
212ad1
 	enum cil_pass pass;
212ad1
 	uint32_t *changed;
212ad1
+	struct cil_list *disabled_optionals;
212ad1
 	struct cil_tree_node *optstack;
212ad1
 	struct cil_tree_node *boolif;
212ad1
 	struct cil_tree_node *macro;
212ad1
@@ -3854,7 +3855,7 @@ int __cil_resolve_ast_last_child_helper(struct cil_tree_node *current, void *ext
212ad1
 
212ad1
 		if (((struct cil_optional *)parent->data)->enabled == CIL_FALSE) {
212ad1
 			*(args->changed) = CIL_TRUE;
212ad1
-			cil_tree_children_destroy(parent);
212ad1
+			cil_list_append(args->disabled_optionals, CIL_NODE, parent);
212ad1
 		}
212ad1
 
212ad1
 		/* pop off the stack */
212ad1
@@ -3917,6 +3918,7 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
212ad1
 	extra_args.in_list = NULL;
212ad1
 	extra_args.blockstack = NULL;
212ad1
 
212ad1
+	cil_list_init(&extra_args.disabled_optionals, CIL_NODE);
212ad1
 	cil_list_init(&extra_args.sidorder_lists, CIL_LIST_ITEM);
212ad1
 	cil_list_init(&extra_args.classorder_lists, CIL_LIST_ITEM);
212ad1
 	cil_list_init(&extra_args.unordered_classorder_lists, CIL_LIST_ITEM);
212ad1
@@ -3984,6 +3986,7 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
212ad1
 		}
212ad1
 
212ad1
 		if (changed && (pass > CIL_PASS_CALL1)) {
212ad1
+			struct cil_list_item *item;
212ad1
 			/* Need to re-resolve because an optional was disabled that contained
212ad1
 			 * one or more declarations. We only need to reset to the call1 pass 
212ad1
 			 * because things done in the preceeding passes aren't allowed in 
212ad1
@@ -4012,6 +4015,11 @@ int cil_resolve_ast(struct cil_db *db, struct cil_tree_node *current)
212ad1
 				cil_log(CIL_ERR, "Failed to reset declarations\n");
212ad1
 				goto exit;
212ad1
 			}
212ad1
+			cil_list_for_each(item, extra_args.disabled_optionals) {
212ad1
+				cil_tree_children_destroy(item->data);
212ad1
+			}
212ad1
+			cil_list_destroy(&extra_args.disabled_optionals, CIL_FALSE);
212ad1
+			cil_list_init(&extra_args.disabled_optionals, CIL_NODE);
212ad1
 		}
212ad1
 
212ad1
 		/* reset the arguments */
212ad1
@@ -4040,6 +4048,7 @@ exit:
212ad1
 	__cil_ordered_lists_destroy(&extra_args.catorder_lists);
212ad1
 	__cil_ordered_lists_destroy(&extra_args.sensitivityorder_lists);
212ad1
 	__cil_ordered_lists_destroy(&extra_args.unordered_classorder_lists);
212ad1
+	cil_list_destroy(&extra_args.disabled_optionals, CIL_FALSE);
212ad1
 	cil_list_destroy(&extra_args.in_list, CIL_FALSE);
212ad1
 
212ad1
 	return rc;
212ad1
-- 
212ad1
2.30.2
212ad1