diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
index e6e553b..afdc240 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -228,6 +228,7 @@ static void cil_init_keys(void)
 	CIL_KEY_ALLOWX = cil_strpool_add("allowx");
 	CIL_KEY_AUDITALLOWX = cil_strpool_add("auditallowx");
 	CIL_KEY_DONTAUDITX = cil_strpool_add("dontauditx");
+	CIL_KEY_NEVERALLOWX = cil_strpool_add("neverallowx");
 	CIL_KEY_PERMISSIONX = cil_strpool_add("permissionx");
 	CIL_KEY_IOCTL = cil_strpool_add("ioctl");
 	CIL_KEY_UNORDERED = cil_strpool_add("unordered");
@@ -668,11 +669,9 @@ void cil_destroy_data(void **data, enum cil_flavor flavor)
 		cil_destroy_roleallow(*data);
 		break;
 	case CIL_AVRULE:
+	case CIL_AVRULEX:
 		cil_destroy_avrule(*data);
 		break;
-	case CIL_AVRULEX:
-		cil_destroy_avrulex(*data);
-		break;
 	case CIL_PERMISSIONX:
 		cil_destroy_permissionx(*data);
 		break;
@@ -1026,13 +1025,15 @@ const char * cil_node_to_string(struct cil_tree_node *node)
 		}
 		break;
 	case CIL_AVRULEX:
-		switch (((struct cil_avrulex *)node->data)->rule_kind) {
+		switch (((struct cil_avrule *)node->data)->rule_kind) {
 		case CIL_AVRULE_ALLOWED:
 			return CIL_KEY_ALLOWX;
 		case CIL_AVRULE_AUDITALLOW:
 			return CIL_KEY_AUDITALLOWX;
 		case CIL_AVRULE_DONTAUDIT:
 			return CIL_KEY_DONTAUDITX;
+		case CIL_AVRULE_NEVERALLOW:
+			return CIL_KEY_NEVERALLOWX;
 		default:
 			break;
 		}
@@ -2116,12 +2117,13 @@ void cil_avrule_init(struct cil_avrule **avrule)
 {
 	*avrule = cil_malloc(sizeof(**avrule));
 
+	(*avrule)->is_extended = 0;
 	(*avrule)->rule_kind = CIL_NONE;
 	(*avrule)->src_str = NULL;
 	(*avrule)->src = NULL;
 	(*avrule)->tgt_str = NULL;
 	(*avrule)->tgt = NULL;
-	(*avrule)->classperms = NULL;
+	memset(&((*avrule)->perms), 0, sizeof((*avrule)->perms));
 }
 
 void cil_permissionx_init(struct cil_permissionx **permx)
@@ -2136,19 +2138,6 @@ void cil_permissionx_init(struct cil_permissionx **permx)
 	(*permx)->perms = NULL;
 }
 
-void cil_avrulex_init(struct cil_avrulex **avrule)
-{
-	*avrule = cil_malloc(sizeof(**avrule));
-
-	(*avrule)->rule_kind = CIL_NONE;
-	(*avrule)->src_str = NULL;
-	(*avrule)->src = NULL;
-	(*avrule)->tgt_str = NULL;
-	(*avrule)->tgt = NULL;
-	(*avrule)->permx_str = NULL;
-	(*avrule)->permx = NULL;
-}
-
 void cil_type_rule_init(struct cil_type_rule **type_rule)
 {
 	*type_rule = cil_malloc(sizeof(**type_rule));
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index db09ec5..42afab3 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -238,55 +238,6 @@ static ocontext_t *cil_add_ocontext(ocontext_t **head, ocontext_t **tail)
 	return new;
 }
 
-static void __add_classes_from_classperms_list(struct cil_list *classperms, struct cil_list *class_list)
-{
-	struct cil_list_item *curr;
-
-	cil_list_for_each(curr, classperms) {
-		if (curr->flavor == CIL_CLASSPERMS) {
-			struct cil_classperms *cp = curr->data;
-			if (FLAVOR(cp->class) == CIL_CLASS) {
-				cil_list_append(class_list, CIL_CLASS, cp->class);
-			} else { /* MAP */
-				struct cil_list_item *i = NULL;
-				cil_list_for_each(i, cp->perms) {
-					struct cil_perm *cmp = i->data;
-					__add_classes_from_classperms_list(cmp->classperms, class_list);
-				}
-			}	
-		} else { /* SET */
-			struct cil_classperms_set *cp_set = curr->data;
-			struct cil_classpermission *cp = cp_set->set;
-			__add_classes_from_classperms_list(cp->classperms, class_list);
-		}
-	}
-}
-
-static int __add_classes_from_map_perms(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
-{
-	struct cil_list *class_list = args;
-	struct cil_perm *cmp = (struct cil_perm *)d;
-
-	__add_classes_from_classperms_list(cmp->classperms, class_list);
-
-	return SEPOL_OK;
-}
-
-static struct cil_list *cil_expand_class(struct cil_class *class)
-{
-	struct cil_list *class_list;
-
-	cil_list_init(&class_list, CIL_CLASS);
-
-	if (FLAVOR(class) == CIL_CLASS) {
-		cil_list_append(class_list, CIL_CLASS, class);
-	} else { /* MAP */
-		cil_symtab_map(&class->perms, __add_classes_from_map_perms, class_list);
-	}
-
-	return class_list;
-}
-
 int cil_common_to_policydb(policydb_t *pdb, struct cil_class *cil_common, common_datum_t **common_out)
 {
 	int rc = SEPOL_ERR;
@@ -1267,6 +1218,30 @@ int cil_typetransition_to_policydb(policydb_t *pdb, const struct cil_db *db, str
 	return  __cil_typetransition_to_avtab(pdb, db, typetrans, NULL, CIL_FALSE, filename_trans_table);
 }
 
+int __perm_str_to_datum(char *perm_str, class_datum_t *sepol_class, uint32_t *datum)
+{
+	int rc;
+	perm_datum_t *sepol_perm;
+	common_datum_t *sepol_common;
+
+	sepol_perm = hashtab_search(sepol_class->permissions.table, perm_str);
+	if (sepol_perm == NULL) {
+		sepol_common = sepol_class->comdatum;
+		sepol_perm = hashtab_search(sepol_common->permissions.table, perm_str);
+		if (sepol_perm == NULL) {
+			cil_log(CIL_ERR, "Failed to find datum for perm %s\n", perm_str);
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+	}
+	*datum |= 1 << (sepol_perm->s.value - 1);
+
+	return SEPOL_OK;
+
+exit:
+	return rc;
+}
+
 int __cil_perms_to_datum(struct cil_list *perms, class_datum_t *sepol_class, uint32_t *datum)
 {
 	int rc = SEPOL_ERR;
@@ -1276,20 +1251,13 @@ int __cil_perms_to_datum(struct cil_list *perms, class_datum_t *sepol_class, uin
 	uint32_t data = 0;
 
 	cil_list_for_each(curr_perm, perms) {
-		perm_datum_t *sepol_perm;
 		cil_perm = curr_perm->data;
 		key = cil_perm->datum.fqn;
-		sepol_perm = hashtab_search(sepol_class->permissions.table, key);
-		if (sepol_perm == NULL) {
-			common_datum_t *sepol_common = sepol_class->comdatum;
-			sepol_perm = hashtab_search(sepol_common->permissions.table, key);
-			if (sepol_perm == NULL) {
-				cil_log(CIL_ERR, "Failed to find datum for perm %s\n", key);
-				rc = SEPOL_ERR;
-				goto exit;
-			}
+
+		rc = __perm_str_to_datum(key, sepol_class, &data);
+		if (rc != SEPOL_OK) {
+			goto exit;
 		}
-		data |= 1 << (sepol_perm->s.value - 1);
 	}
 
 	*datum = data;
@@ -1433,7 +1401,7 @@ int __cil_avrule_to_avtab(policydb_t *pdb, const struct cil_db *db, struct cil_a
 	uint16_t kind = cil_avrule->rule_kind;
 	struct cil_symtab_datum *src = NULL;
 	struct cil_symtab_datum *tgt = NULL;
-	struct cil_list *classperms = cil_avrule->classperms;
+	struct cil_list *classperms = cil_avrule->perms.classperms;
 
 	if (cil_avrule->rule_kind == CIL_AVRULE_DONTAUDIT && db->disable_dontaudit == CIL_TRUE) {
 		// Do not add dontaudit rules to binary
@@ -1514,13 +1482,8 @@ void __avrule_xperm_setrangebits(uint16_t low, uint16_t high, struct avtab_exten
 #define IOC_DRIV(x) (x >> 8)
 #define IOC_FUNC(x) (x & 0xff)
 
-int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args)
+int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list)
 {
-	int rc = SEPOL_OK;
-	struct policydb *pdb;
-	avtab_key_t *avtab_key;
-	avtab_datum_t avtab_datum;
-	ebitmap_t *xperms;
 	ebitmap_node_t *node;
 	unsigned int i;
 	uint16_t low = 0, high = 0;
@@ -1528,11 +1491,7 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void
 	struct avtab_extended_perms *complete = NULL;
 	int start_new_range;
 
-	avtab_key = (avtab_key_t *)k;
-	xperms = datum;
-	pdb = args;
-
-	avtab_datum.data = 0;
+	cil_list_init(xperms_list, CIL_NONE);
 
 	start_new_range = 1;
 
@@ -1565,12 +1524,7 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void
 			__avrule_xperm_setrangebits(IOC_DRIV(low), IOC_DRIV(low), complete);
 		} else {
 			if (partial && partial->driver != IOC_DRIV(low)) {
-				avtab_datum.xperms = partial;
-				rc = avtab_insert(&pdb->te_avtab, avtab_key, &avtab_datum);
-				if (rc != SEPOL_OK) {
-					goto exit;
-				}
-				free(partial);
+				cil_list_append(*xperms_list, CIL_NONE, partial);
 				partial = NULL;
 			}
 
@@ -1585,15 +1539,48 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void
 	}
 
 	if (partial) {
-		avtab_datum.xperms = partial;
-		rc = avtab_insert(&pdb->te_avtab, avtab_key, &avtab_datum);
-		if (rc != SEPOL_OK) {
-			goto exit;
-		}
+		cil_list_append(*xperms_list, CIL_NONE, partial);
 	}
 
 	if (complete) {
-		avtab_datum.xperms = complete;
+		cil_list_append(*xperms_list, CIL_NONE, complete);
+	}
+
+	return SEPOL_OK;
+}
+
+int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args)
+{
+	int rc = SEPOL_OK;
+	struct policydb *pdb;
+	avtab_key_t *avtab_key;
+	avtab_datum_t avtab_datum;
+	struct cil_list *xperms_list = NULL;
+	struct cil_list_item *item;
+	class_datum_t *sepol_obj;
+	uint32_t data = 0;
+
+	avtab_key = (avtab_key_t *)k;
+	pdb = args;
+
+	sepol_obj = pdb->class_val_to_struct[avtab_key->target_class - 1];
+
+	// setting the data for an extended avtab isn't really neccessary because
+	// it is ignored by the kernel. However, neverallow checking requires that
+	// the data value be set, so set it for that to work.
+	rc = __perm_str_to_datum(CIL_KEY_IOCTL, sepol_obj, &data);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+	avtab_datum.data = data;
+
+	rc = __cil_permx_bitmap_to_sepol_xperms_list(datum, &xperms_list);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	cil_list_for_each(item, xperms_list) {
+		avtab_datum.xperms = item->data;
 		rc = avtab_insert(&pdb->te_avtab, avtab_key, &avtab_datum);
 		if (rc != SEPOL_OK) {
 			goto exit;
@@ -1603,15 +1590,19 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void
 	rc = SEPOL_OK;
 
 exit:
-	free(partial);
-	free(complete);
+	if (xperms_list != NULL) {
+		cil_list_for_each(item, xperms_list) {
+			free(item->data);
+		}
+		cil_list_destroy(&xperms_list, CIL_FALSE);
+	}
 
 	// hashtab_t does not have a way to free keys or datum since it doesn't
 	// know what they are. We won't need the keys/datum after this function, so
 	// clean them up here.
 	free(avtab_key);
-	ebitmap_destroy(xperms);
-	free(xperms);
+	ebitmap_destroy(datum);
+	free(datum);
 
 	return rc;
 }
@@ -1711,7 +1702,7 @@ exit:
 	return rc;
 }
 
-int cil_avrulex_to_hashtable(policydb_t *pdb, const struct cil_db *db, struct cil_avrulex *cil_avrulex, struct cil_args_binary *args)
+int cil_avrulex_to_hashtable(policydb_t *pdb, const struct cil_db *db, struct cil_avrule *cil_avrulex, struct cil_args_binary *args)
 {
 	int rc = SEPOL_ERR;
 	uint16_t kind;
@@ -1741,13 +1732,13 @@ int cil_avrulex_to_hashtable(policydb_t *pdb, const struct cil_db *db, struct ci
 			if (!ebitmap_get_bit(&type_bitmap, i)) continue;
 
 			src = DATUM(db->val_to_type[i]);
-			rc = __cil_avrulex_to_hashtable_helper(pdb, kind, src, src, cil_avrulex->permx, args);
+			rc = __cil_avrulex_to_hashtable_helper(pdb, kind, src, src, cil_avrulex->perms.x.permx, args);
 			if (rc != SEPOL_OK) {
 				goto exit;
 			}
 		}
 	} else {
-		rc = __cil_avrulex_to_hashtable_helper(pdb, kind, src, tgt, cil_avrulex->permx, args);
+		rc = __cil_avrulex_to_hashtable_helper(pdb, kind, src, tgt, cil_avrulex->perms.x.permx, args);
 		if (rc != SEPOL_OK) goto exit;
 	}
 
@@ -1820,6 +1811,89 @@ exit:
 	return SEPOL_ERR;
 }
 
+static void __cil_expr_to_string(struct cil_list *expr, enum cil_flavor flavor, char **out);
+
+static void __cil_expr_to_string_helper(struct cil_list_item *curr, enum cil_flavor flavor, char **out)
+{
+	char *c;
+
+	if (curr->flavor == CIL_DATUM) {
+		*out = cil_strdup(DATUM(curr->data)->fqn);
+	} else if (curr->flavor == CIL_LIST) {
+		__cil_expr_to_string(curr->data, flavor, &c);
+		cil_asprintf(out, "(%s)", c);
+		free(c);
+	} else if (flavor == CIL_PERMISSIONX) {
+		// permissionx expressions aren't resolved into anything, so curr->flavor
+		// is just a CIL_STRING, not a CIL_DATUM, so just check on flavor for those
+		*out = cil_strdup(curr->data);
+	}
+}
+
+static void __cil_expr_to_string(struct cil_list *expr, enum cil_flavor flavor, char **out)
+{
+	struct cil_list_item *curr;
+	char *s1 = NULL;
+	char *s2 = NULL;
+	enum cil_flavor op;
+
+	if (expr == NULL || expr->head == NULL) {
+		*out = cil_strdup("");
+		return;
+	}
+
+	curr = expr->head;
+
+	if (curr->flavor == CIL_OP) {
+		op = (enum cil_flavor)curr->data;
+
+		if (op == CIL_ALL) {
+			*out = cil_strdup(CIL_KEY_ALL);
+		} else if (op == CIL_RANGE) {
+			__cil_expr_to_string_helper(curr->next, flavor, &s1);
+			__cil_expr_to_string_helper(curr->next->next, flavor, &s2);
+			cil_asprintf(out, "%s %s %s", CIL_KEY_RANGE, s1, s2);
+			free(s1);
+			free(s2);
+		} else {
+			__cil_expr_to_string_helper(curr->next, flavor, &s1);
+
+			if (op == CIL_NOT) {
+				cil_asprintf(out, "%s %s", CIL_KEY_NOT, s1);
+				free(s1);
+			} else {
+				char *opstr = "";
+
+				__cil_expr_to_string_helper(curr->next->next, flavor, &s2);
+
+				if (op == CIL_OR) {
+					opstr = CIL_KEY_OR;
+				} else if (op == CIL_AND) {
+					opstr = CIL_KEY_AND;
+				} else if (op == CIL_XOR) {
+					opstr = CIL_KEY_XOR;
+				}
+
+				cil_asprintf(out, "%s %s %s", opstr, s1, s2);
+				free(s1);
+				free(s2);
+			}
+		}
+	} else {
+		char *c1 = NULL;
+		char *c2 = NULL;
+		__cil_expr_to_string_helper(curr, flavor, &c1);
+		for (curr = curr->next; curr; curr = curr->next) {
+			__cil_expr_to_string_helper(curr, flavor, &s1);
+			cil_asprintf(&c2, "%s %s", c1, s1);
+			free(c1);
+			free(s1);
+			c1 = c2;
+		}
+		*out = c1;
+	}
+}
+
 static int __cil_cond_expr_to_sepol_expr_helper(policydb_t *pdb, struct cil_list *cil_expr, cond_expr_t **head, cond_expr_t **tail);
 
 static int __cil_cond_item_to_sepol_expr(policydb_t *pdb, struct cil_list_item *item, cond_expr_t **head, cond_expr_t **tail)
@@ -3427,7 +3501,8 @@ int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
 		case CIL_TYPE_RULE:
 			rc = cil_type_rule_to_policydb(pdb, db, node->data);
 			break;
-		case CIL_AVRULE: {
+		case CIL_AVRULE:
+		case CIL_AVRULEX: {
 			struct cil_avrule *rule = node->data;
 			if (db->disable_neverallow != CIL_TRUE && rule->rule_kind == CIL_AVRULE_NEVERALLOW) {
 				struct cil_list *neverallows = args->neverallows;
@@ -3489,8 +3564,12 @@ int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args)
 				}
 			}
 			break;
-		case CIL_AVRULEX:
-			rc = cil_avrulex_to_hashtable(pdb, db, node->data, args);
+		case CIL_AVRULEX: {
+				struct cil_avrule *rule = node->data;
+				if (rule->rule_kind != CIL_AVRULE_NEVERALLOW) {
+					rc = cil_avrulex_to_hashtable(pdb, db, node->data, args);
+				}
+			}
 			break;
 		case CIL_ROLEALLOW:
 			rc = cil_roleallow_to_policydb(pdb, db, node->data);
@@ -4064,6 +4143,51 @@ exit:
 	return rc;
 }
 
+static int __cil_permx_to_sepol_class_perms(policydb_t *pdb, struct cil_permissionx *permx, class_perm_node_t **sepol_class_perms)
+{
+	int rc;
+	struct cil_list *class_list = NULL;
+	struct cil_list_item *c;
+	class_datum_t *sepol_obj = NULL;
+	class_perm_node_t *cpn;
+	uint32_t data = 0;
+	char *perm_str = NULL;
+
+	class_list = cil_expand_class(permx->obj);
+
+	cil_list_for_each(c, class_list) {
+		rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+
+		switch (permx->kind) {
+			case CIL_PERMX_KIND_IOCTL:
+				perm_str = CIL_KEY_IOCTL;
+				break;
+			default:
+				rc = SEPOL_ERR;
+				goto exit;
+		}
+
+		rc = __perm_str_to_datum(perm_str, sepol_obj, &data);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+
+		cpn = cil_malloc(sizeof(*cpn));
+		cpn->tclass = sepol_obj->s.value;
+		cpn->data = data;
+		cpn->next = *sepol_class_perms;
+		*sepol_class_perms = cpn;
+	}
+
+exit:
+	cil_list_destroy(&class_list, CIL_FALSE);
+
+	return rc;
+}
+
 static void __cil_init_sepol_type_set(type_set_t *t)
 {
 	ebitmap_init(&t->types);
@@ -4133,62 +4257,6 @@ static void __cil_destroy_sepol_avrules(avrule_t *curr)
 	}
 }
 
-static int __cil_rule_to_expanded_sepol_avrule(const struct cil_db *db, policydb_t *pdb, struct cil_tree_node *node, avrule_t **avrule)
-{
-	int rc = SEPOL_ERR;
-	struct cil_avrule *cil_rule = node->data;
-	struct cil_symtab_datum *tgt = cil_rule->tgt;
-	uint32_t kind;
-	avrule_t *rule;
-
-	*avrule = NULL;
-
-	switch (cil_rule->rule_kind) {
-	case CIL_AVRULE_AUDITALLOW:
-		kind = AVRULE_AUDITALLOW;
-		break;
-	case CIL_AVRULE_DONTAUDIT:
-		kind = AVRULE_AUDITDENY;
-		break;
-	case CIL_AVRULE_NEVERALLOW:
-		kind = AVRULE_NEVERALLOW;
-		break;
-	default:
-		kind = AVRULE_ALLOWED;
-		break;
-	}
-
-	rule = __cil_init_sepol_avrule(kind, node);
-
-	rc = __cil_rule_to_sepol_class_perms(pdb, cil_rule->classperms, &rule->perms);
-	if (rc != SEPOL_OK) {
-		goto exit;
-	}
-
-	rc = __cil_add_sepol_type(pdb, db, cil_rule->src, &rule->stypes.types);
-	if (rc != SEPOL_OK) {
-		goto exit;
-	}
-
-	if (tgt->fqn == CIL_KEY_SELF) {
-		rule->flags = RULE_SELF;
-	} else {
-		rc = __cil_add_sepol_type(pdb, db, cil_rule->tgt, &rule->ttypes.types);
-		if (rc != SEPOL_OK) {
-			goto exit;
-		}
-	}
-
-	rule->next = NULL;
-	*avrule = rule;
-
-	return SEPOL_OK;
-
-exit:
-	__cil_destroy_sepol_avrules(rule);
-	return rc;
-}
-
 static void __cil_print_parents(const char *pad, struct cil_tree_node *n)
 {
 	if (!n) return;
@@ -4202,14 +4270,10 @@ static void __cil_print_parents(const char *pad, struct cil_tree_node *n)
 	}
 }
 
-static void __cil_print_rule(const char *pad, const char *kind, struct cil_avrule *avrule)
+static void __cil_print_classperm(struct cil_list *cp_list)
 {
-	struct cil_list *cp_list = avrule->classperms;
 	struct cil_list_item *i1, *i2;
 
-	cil_log(CIL_ERR,"%s(%s ", pad, kind);
-	cil_log(CIL_ERR,"%s %s ", DATUM(avrule->src)->fqn, DATUM(avrule->tgt)->fqn);
-
 	i1 = cp_list->head;
 	if (i1->flavor == CIL_CLASSPERMS) {
 		struct cil_classperms *cp = i1->data;
@@ -4226,63 +4290,210 @@ static void __cil_print_rule(const char *pad, const char *kind, struct cil_avrul
 		struct cil_classperms_set *cp_set = i1->data;
 		cil_log(CIL_ERR,"%s", DATUM(cp_set->set)->fqn);
 	}
+}
+
+static void __cil_print_permissionx(struct cil_permissionx *px)
+{
+	char *kind_str = "";
+	char *expr_str;
+
+	switch (px->kind) {
+		case CIL_PERMX_KIND_IOCTL:
+			kind_str = CIL_KEY_IOCTL;
+			break;
+		default:
+			kind_str = "unknown";
+			break;
+	}
+
+	__cil_expr_to_string(px->expr_str, CIL_PERMISSIONX, &expr_str);
+
+	cil_log(CIL_ERR, "%s %s (%s)", kind_str, DATUM(px->obj)->fqn, expr_str);
+
+	free(expr_str);
+}
+
+static void __cil_print_rule(const char *pad, const char *kind, struct cil_avrule *avrule)
+{
+	cil_log(CIL_ERR,"%s(%s ", pad, kind);
+	cil_log(CIL_ERR,"%s %s ", DATUM(avrule->src)->fqn, DATUM(avrule->tgt)->fqn);
+
+	if (!avrule->is_extended) {
+		__cil_print_classperm(avrule->perms.classperms);
+	} else {
+		cil_log(CIL_ERR, "(");
+		__cil_print_permissionx(avrule->perms.x.permx);
+		cil_log(CIL_ERR, ")");
+	}
 
 	cil_log(CIL_ERR,")\n");
 }
 
-static int cil_check_neverallows(const struct cil_db *db, policydb_t *pdb, struct cil_list *neverallows)
+static int __cil_print_neverallow_failure(const struct cil_db *db, struct cil_tree_node *node)
 {
-	int rc = SEPOL_OK;
-	struct cil_list_item *i1;
+	int rc;
+	struct cil_list_item *i2;
+	struct cil_list *matching;
+	struct cil_avrule *cil_rule = node->data;
+	struct cil_avrule target;
+	struct cil_tree_node *n2;
+	struct cil_avrule *r2;
+	char *neverallow_str;
+	char *allow_str;
+	enum cil_flavor avrule_flavor;
 
-	cil_list_for_each(i1, neverallows) {
-		struct cil_tree_node *node = i1->data;
-		avrule_t *avrule;
-		rc = __cil_rule_to_expanded_sepol_avrule(db, pdb, node, &avrule);
+	target.rule_kind = CIL_AVRULE_ALLOWED;
+	target.is_extended = cil_rule->is_extended;
+	target.src = cil_rule->src;
+	target.tgt = cil_rule->tgt;
+	target.perms = cil_rule->perms;
+
+	if (!cil_rule->is_extended) {
+		neverallow_str = CIL_KEY_NEVERALLOW;
+		allow_str = CIL_KEY_ALLOW;
+		avrule_flavor = CIL_AVRULE;
+	} else {
+		neverallow_str = CIL_KEY_NEVERALLOWX;
+		allow_str = CIL_KEY_ALLOWX;
+		avrule_flavor = CIL_AVRULEX;
+	}
+	cil_log(CIL_ERR, "%s check failed at line %d of %s\n", neverallow_str, node->line, node->path);
+	__cil_print_rule("  ", neverallow_str, cil_rule);
+	cil_list_init(&matching, CIL_NODE);
+	rc = cil_find_matching_avrule_in_ast(db->ast->root, avrule_flavor, &target, matching, CIL_FALSE);
+	if (rc) {
+		cil_log(CIL_ERR, "Error occurred while checking %s rules\n", neverallow_str);
+		cil_list_destroy(&matching, CIL_FALSE);
+		goto exit;
+	}
+
+	cil_list_for_each(i2, matching) {
+		n2 = i2->data;
+		r2 = n2->data;
+		__cil_print_parents("    ", n2);
+		__cil_print_rule("      ", allow_str, r2);
+	}
+	cil_log(CIL_ERR,"\n");
+	cil_list_destroy(&matching, CIL_FALSE);
+
+exit:
+	return rc;
+}
+
+static int cil_check_neverallow(const struct cil_db *db, policydb_t *pdb, struct cil_tree_node *node)
+{
+	int rc = SEPOL_ERR;
+	int ret = CIL_FALSE;
+	struct cil_avrule *cil_rule = node->data;
+	struct cil_symtab_datum *tgt = cil_rule->tgt;
+	uint32_t kind;
+	avrule_t *rule;
+	struct cil_list *xperms = NULL;
+	struct cil_list_item *item;
+
+	if (!cil_rule->is_extended) {
+		kind = AVRULE_NEVERALLOW;
+	} else {
+		kind = AVRULE_XPERMS_NEVERALLOW;
+	}
+
+	rule = __cil_init_sepol_avrule(kind, node);
+	rule->next = NULL;
+
+	rc = __cil_add_sepol_type(pdb, db, cil_rule->src, &rule->stypes.types);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	if (tgt->fqn == CIL_KEY_SELF) {
+		rule->flags = RULE_SELF;
+	} else {
+		rc = __cil_add_sepol_type(pdb, db, cil_rule->tgt, &rule->ttypes.types);
 		if (rc != SEPOL_OK) {
-			cil_log(CIL_ERR, "Failed to create expanded sepol avrules to check neverallow rules\n");
 			goto exit;
 		}
+	}
 
-		rc = check_assertion(pdb, avrule);
+	if (!cil_rule->is_extended) {
+		rc = __cil_rule_to_sepol_class_perms(pdb, cil_rule->perms.classperms, &rule->perms);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
 
+		rc = check_assertion(pdb, rule);
 		if (rc == CIL_TRUE) {
-			struct cil_list_item *i2;
-			struct cil_list *matching;
-			struct cil_avrule *cil_rule = node->data;
-			struct cil_avrule target;
-			struct cil_tree_node *n2;
-			struct cil_avrule *r2;
-			target.rule_kind = CIL_AVRULE_ALLOWED;
-			target.src = cil_rule->src;
-			target.tgt = cil_rule->tgt;
-			target.classperms = cil_rule->classperms;
-			cil_log(CIL_ERR, "Neverallow check failed at line %d of %s\n", node->line, node->path);
-			__cil_print_rule("  ", "neverallow", cil_rule);
-			cil_list_init(&matching, CIL_NODE);
-			rc = cil_find_matching_avrule_in_ast(db->ast->root, CIL_AVRULE, &target, matching, CIL_FALSE);
-			if (rc) {
-				cil_log(CIL_ERR, "Error occurred while checking neverallow rules\n");
-				cil_list_destroy(&matching, CIL_FALSE);
-				__cil_destroy_sepol_avrules(avrule);
+			rc = __cil_print_neverallow_failure(db, node);
+			if (rc != SEPOL_OK) {
 				goto exit;
 			}
+			ret = CIL_TRUE;
+		}
+
+	} else {
+		rc = __cil_permx_to_sepol_class_perms(pdb, cil_rule->perms.x.permx, &rule->perms);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+
+		rc = __cil_permx_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->perms, &xperms);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
 
-			cil_list_for_each(i2, matching) {
-				n2 = i2->data;
-				r2 = n2->data;
-				__cil_print_parents("    ", n2);
-				__cil_print_rule("      ", "allow", r2);
+		cil_list_for_each(item, xperms) {
+			rule->xperms = item->data;
+			rc = check_assertion(pdb, rule);
+			if (rc == CIL_TRUE) {
+				rc = __cil_print_neverallow_failure(db, node);
+				if (rc != SEPOL_OK) {
+					goto exit;
+				}
+				ret = CIL_TRUE;
+				goto exit;
 			}
-			cil_log(CIL_ERR,"\n");
-			cil_list_destroy(&matching, CIL_FALSE);
 		}
-		__cil_destroy_sepol_avrules(avrule);
-		avrule = NULL;
 	}
 
 exit:
-	return rc;
+	if (xperms != NULL) {
+		cil_list_for_each(item, xperms) {
+			free(item->data);
+			item->data = NULL;
+		}
+		cil_list_destroy(&xperms, CIL_FALSE);
+	}
+
+	rule->xperms = NULL;
+	__cil_destroy_sepol_avrules(rule);
+
+	if (rc) {
+		return rc;
+	} else {
+		return ret;
+	}
+}
+
+static int cil_check_neverallows(const struct cil_db *db, policydb_t *pdb, struct cil_list *neverallows)
+{
+	int rc = SEPOL_OK;
+	int ret = CIL_FALSE;
+	struct cil_list_item *item;
+
+	cil_list_for_each(item, neverallows) {
+		rc = cil_check_neverallow(db, pdb, item->data);
+		if (rc < 0) {
+			goto exit;
+		} else if (rc > 0) {
+			ret = CIL_TRUE;
+		}
+	}
+
+exit:
+	if (rc || ret) {
+		return SEPOL_ERR;
+	} else {
+		return SEPOL_OK;
+	}
 }
 
 static struct cil_list *cil_classperms_from_sepol(policydb_t *pdb, uint16_t class, uint32_t data, struct cil_class *class_value_to_cil[], struct cil_perm **perm_value_to_cil[])
@@ -4327,8 +4538,8 @@ static int cil_avrule_from_sepol(policydb_t *pdb, avtab_ptr_t sepol_rule, struct
 	cil_rule->tgt = type_value_to_cil[k->target_type];
 	if (!cil_rule->tgt) goto exit;
 
-	cil_rule->classperms = cil_classperms_from_sepol(pdb, k->target_class, d->data, class_value_to_cil, perm_value_to_cil);
-	if (!cil_rule->classperms) goto exit;
+	cil_rule->perms.classperms = cil_classperms_from_sepol(pdb, k->target_class, d->data, class_value_to_cil, perm_value_to_cil);
+	if (!cil_rule->perms.classperms) goto exit;
 
 	return SEPOL_OK;
 
@@ -4386,7 +4597,7 @@ static int cil_check_type_bounds(const struct cil_db *db, policydb_t *pdb, void
 				if (rc) {
 					cil_log(CIL_ERR, "Error occurred while checking type bounds\n");
 					cil_list_destroy(&matching, CIL_FALSE);
-					cil_list_destroy(&target.classperms, CIL_TRUE);
+					cil_list_destroy(&target.perms.classperms, CIL_TRUE);
 					bounds_destroy_bad(bad);
 					goto exit;
 				}
@@ -4399,7 +4610,7 @@ static int cil_check_type_bounds(const struct cil_db *db, policydb_t *pdb, void
 				__cil_print_rule("      ", "allow", n->data);
 				cil_log(CIL_ERR,"\n");
 				cil_list_destroy(&matching, CIL_FALSE);
-				cil_list_destroy(&target.classperms, CIL_TRUE);
+				cil_list_destroy(&target.perms.classperms, CIL_TRUE);
 			}
 			bounds_destroy_bad(bad);
 		}
diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
index 0407d20..f29d0c6 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -2025,12 +2025,13 @@ int cil_gen_avrule(struct cil_tree_node *parse_current, struct cil_tree_node *as
 
 	cil_avrule_init(&rule);
 
+	rule->is_extended = 0;
 	rule->rule_kind = rule_kind;
 
 	rule->src_str = parse_current->next->data;
 	rule->tgt_str = parse_current->next->next->data;
 
-	rc = cil_fill_classperms_list(parse_current->next->next->next, &rule->classperms);
+	rc = cil_fill_classperms_list(parse_current->next->next->next, &rule->perms.classperms);
 	if (rc != SEPOL_OK) {
 		goto exit;
 	}
@@ -2053,7 +2054,13 @@ void cil_destroy_avrule(struct cil_avrule *rule)
 		return;
 	}
 
-	cil_destroy_classperms_list(&rule->classperms);
+	if (!rule->is_extended) {
+		cil_destroy_classperms_list(&rule->perms.classperms);
+	} else {
+		if (rule->perms.x.permx_str == NULL && rule->perms.x.permx != NULL) {
+			cil_destroy_permissionx(rule->perms.x.permx);
+		}
+	}
 
 	free(rule);
 }
@@ -2167,7 +2174,7 @@ int cil_gen_avrulex(struct cil_tree_node *parse_current, struct cil_tree_node *a
 		CIL_SYN_END
 	};
 	int syntax_len = sizeof(syntax)/sizeof(*syntax);
-	struct cil_avrulex *rule = NULL;
+	struct cil_avrule *rule = NULL;
 	int rc = SEPOL_ERR;
 
 	if (parse_current == NULL || ast_node == NULL) {
@@ -2179,18 +2186,19 @@ int cil_gen_avrulex(struct cil_tree_node *parse_current, struct cil_tree_node *a
 		goto exit;
 	}
 
-	cil_avrulex_init(&rule);
+	cil_avrule_init(&rule);
 
+	rule->is_extended = 1;
 	rule->rule_kind = rule_kind;
 	rule->src_str = parse_current->next->data;
 	rule->tgt_str = parse_current->next->next->data;
 
 	if (parse_current->next->next->next->cl_head == NULL) {
-		rule->permx_str = parse_current->next->next->next->data;
+		rule->perms.x.permx_str = parse_current->next->next->next->data;
 	} else {
-		cil_permissionx_init(&rule->permx);
+		cil_permissionx_init(&rule->perms.x.permx);
 
-		rc = cil_fill_permissionx(parse_current->next->next->next->cl_head, rule->permx);
+		rc = cil_fill_permissionx(parse_current->next->next->next->cl_head, rule->perms.x.permx);
 		if (rc != SEPOL_OK) {
 			goto exit;
 		}
@@ -2204,23 +2212,10 @@ int cil_gen_avrulex(struct cil_tree_node *parse_current, struct cil_tree_node *a
 exit:
 	cil_log(CIL_ERR, "Bad allowx rule at line %d of %s\n",
 		parse_current->line, parse_current->path);
-	cil_destroy_avrulex(rule);
+	cil_destroy_avrule(rule);
 	return rc;
 }
 
-void cil_destroy_avrulex(struct cil_avrulex *rule)
-{
-	if (rule == NULL) {
-		return;
-	}
-
-	if (rule->permx_str == NULL && rule->permx != NULL) {
-		cil_destroy_permissionx(rule->permx);
-	}
-
-	free(rule);
-}
-
 int cil_gen_type_rule(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node, uint32_t rule_kind)
 {
 	enum cil_syntax syntax[] = {
@@ -6127,6 +6122,9 @@ int __cil_build_ast_node_helper(struct cil_tree_node *parse_current, uint32_t *f
 	} else if (parse_current->data == CIL_KEY_DONTAUDITX) {
 		rc = cil_gen_avrulex(parse_current, ast_node, CIL_AVRULE_DONTAUDIT);
 		*finished = CIL_TREE_SKIP_NEXT;
+	} else if (parse_current->data == CIL_KEY_NEVERALLOWX) {
+		rc = cil_gen_avrulex(parse_current, ast_node, CIL_AVRULE_NEVERALLOW);
+		*finished = CIL_TREE_SKIP_NEXT;
 	} else if (parse_current->data == CIL_KEY_PERMISSIONX) {
 		rc = cil_gen_permissionx(db, parse_current, ast_node);
 		*finished = CIL_TREE_SKIP_NEXT;
diff --git a/libsepol/cil/src/cil_build_ast.h b/libsepol/cil/src/cil_build_ast.h
index 11f51f5..f428394 100644
--- a/libsepol/cil/src/cil_build_ast.h
+++ b/libsepol/cil/src/cil_build_ast.h
@@ -112,7 +112,6 @@ int cil_gen_rolebounds(struct cil_db *db, struct cil_tree_node *parse_current, s
 int cil_gen_avrule(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node, uint32_t rule_kind);
 void cil_destroy_avrule(struct cil_avrule *rule);
 int cil_gen_avrulex(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node, uint32_t rule_kind);
-void cil_destroy_avrulex(struct cil_avrulex *rule);
 int cil_gen_permissionx(struct cil_db *db, struct cil_tree_node *parse_current, struct cil_tree_node *ast_node);
 void cil_destroy_permissionx(struct cil_permissionx *permx);
 int cil_gen_type_rule(struct cil_tree_node *parse_current, struct cil_tree_node *ast_node, uint32_t rule_kind);
diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
index 8c50ff0..f49364c 100644
--- a/libsepol/cil/src/cil_copy_ast.c
+++ b/libsepol/cil/src/cil_copy_ast.c
@@ -782,6 +782,13 @@ int cil_copy_tunable(__attribute__((unused)) struct cil_db *db, void *data, void
 	return SEPOL_OK;
 }
 
+void cil_copy_fill_permissionx(struct cil_db *db, struct cil_permissionx *orig, struct cil_permissionx *new)
+{
+	new->kind = orig->kind;
+	new->obj_str = orig->obj_str;
+	cil_copy_expr(db, orig->expr_str, &new->expr_str);
+}
+
 int cil_copy_avrule(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
 {
 	struct cil_avrule *orig = data;
@@ -789,23 +796,27 @@ int cil_copy_avrule(__attribute__((unused)) struct cil_db *db, void *data, void
 
 	cil_avrule_init(&new);
 
+	new->is_extended = orig->is_extended;
 	new->rule_kind = orig->rule_kind;
 	new->src_str = orig->src_str;
 	new->tgt_str = orig->tgt_str;
-	cil_copy_classperms_list(orig->classperms, &new->classperms);
+
+	if (!new->is_extended) {
+		cil_copy_classperms_list(orig->perms.classperms, &new->perms.classperms);
+	} else {
+		if (new->perms.x.permx_str != NULL) {
+			new->perms.x.permx_str = orig->perms.x.permx_str;
+		} else {
+			cil_permissionx_init(&new->perms.x.permx);
+			cil_copy_fill_permissionx(db, orig->perms.x.permx, new->perms.x.permx);
+		}
+	}
 
 	*copy = new;
 
 	return SEPOL_OK;
 }
 
-void cil_copy_fill_permissionx(struct cil_db *db, struct cil_permissionx *orig, struct cil_permissionx *new)
-{
-	new->kind = orig->kind;
-	new->obj_str = orig->obj_str;
-	cil_copy_expr(db, orig->expr_str, &new->expr_str);
-}
-
 int cil_copy_permissionx(struct cil_db *db, void *data, void **copy, symtab_t *symtab)
 {
 	struct cil_permissionx *orig = data;
@@ -828,30 +839,6 @@ int cil_copy_permissionx(struct cil_db *db, void *data, void **copy, symtab_t *s
 	return SEPOL_OK;
 }
 
-
-int cil_copy_avrulex(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
-{
-	struct cil_avrulex *orig = data;
-	struct cil_avrulex *new = NULL;
-
-	cil_avrulex_init(&new);
-
-	new->rule_kind = orig->rule_kind;
-	new->src_str = orig->src_str;
-	new->tgt_str = orig->tgt_str;
-
-	if (new->permx_str != NULL) {
-		new->permx_str = orig->permx_str;
-	} else {
-		cil_permissionx_init(&new->permx);
-		cil_copy_fill_permissionx(db, orig->permx, new->permx);
-	}
-
-	*copy = new;
-
-	return SEPOL_OK;
-}
-
 int cil_copy_type_rule(__attribute__((unused)) struct cil_db *db, void *data, void **copy, __attribute__((unused)) symtab_t *symtab)
 {
 	struct cil_type_rule  *orig = data;
@@ -1828,10 +1815,8 @@ int __cil_copy_node_helper(struct cil_tree_node *orig, __attribute__((unused)) u
 		copy_func = &cil_copy_bool;
 		break;
 	case CIL_AVRULE:
-		copy_func = &cil_copy_avrule;
-		break;
 	case CIL_AVRULEX:
-		copy_func = &cil_copy_avrulex;
+		copy_func = &cil_copy_avrule;
 		break;
 	case CIL_PERMISSIONX:
 		copy_func = &cil_copy_permissionx;
diff --git a/libsepol/cil/src/cil_find.c b/libsepol/cil/src/cil_find.c
index a76dc37..b80e1ef 100644
--- a/libsepol/cil/src/cil_find.c
+++ b/libsepol/cil/src/cil_find.c
@@ -214,20 +214,93 @@ static int cil_classperms_list_match_any(struct cil_list *cpl1, struct cil_list
 	return CIL_FALSE;
 }
 
+static void __add_classes_from_classperms_list(struct cil_list *classperms, struct cil_list *class_list)
+{
+	struct cil_list_item *curr;
+
+	cil_list_for_each(curr, classperms) {
+		if (curr->flavor == CIL_CLASSPERMS) {
+			struct cil_classperms *cp = curr->data;
+			if (FLAVOR(cp->class) == CIL_CLASS) {
+				cil_list_append(class_list, CIL_CLASS, cp->class);
+			} else { /* MAP */
+				struct cil_list_item *i = NULL;
+				cil_list_for_each(i, cp->perms) {
+					struct cil_perm *cmp = i->data;
+					__add_classes_from_classperms_list(cmp->classperms, class_list);
+				}
+			}	
+		} else { /* SET */
+			struct cil_classperms_set *cp_set = curr->data;
+			struct cil_classpermission *cp = cp_set->set;
+			__add_classes_from_classperms_list(cp->classperms, class_list);
+		}
+	}
+}
+
+static int __add_classes_from_map_perms(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	struct cil_list *class_list = args;
+	struct cil_perm *cmp = (struct cil_perm *)d;
+
+	__add_classes_from_classperms_list(cmp->classperms, class_list);
+
+	return SEPOL_OK;
+}
+
+struct cil_list *cil_expand_class(struct cil_class *class)
+{
+	struct cil_list *class_list;
+
+	cil_list_init(&class_list, CIL_CLASS);
+
+	if (FLAVOR(class) == CIL_CLASS) {
+		cil_list_append(class_list, CIL_CLASS, class);
+	} else { /* MAP */
+		cil_symtab_map(&class->perms, __add_classes_from_map_perms, class_list);
+	}
+
+	return class_list;
+}
+
+static int cil_permissionx_match_any(struct cil_permissionx *px1, struct cil_permissionx *px2)
+{
+	int rc = CIL_FALSE;
+	struct cil_list *cl1 = NULL;
+	struct cil_list *cl2 = NULL;
+
+	if (px1->kind != px2->kind) goto exit;
+
+	if (!ebitmap_match_any(px1->perms, px2->perms)) goto exit;
+
+	cl1 = cil_expand_class(px1->obj);
+	cl2 = cil_expand_class(px2->obj);
+
+	if (!cil_list_match_any(cl1, cl2)) goto exit;
+
+	rc = CIL_TRUE;
+
+exit:
+	cil_list_destroy(&cl1, CIL_FALSE);
+	cil_list_destroy(&cl2, CIL_FALSE);
+
+	return rc;
+}
+
 int cil_find_matching_avrule(struct cil_tree_node *node, struct cil_avrule *avrule, struct cil_avrule *target, struct cil_list *matching, int match_self)
 {
 	int rc = SEPOL_OK;
 	struct cil_symtab_datum *s1 = avrule->src;
 	struct cil_symtab_datum *t1 = avrule->tgt;
-	struct cil_list *cp1 = avrule->classperms;
 	struct cil_symtab_datum *s2 = target->src;
 	struct cil_symtab_datum *t2 = target->tgt;
-	struct cil_list *cp2 = target->classperms;
 
 	if (match_self != CIL_TRUE && avrule == target) goto exit;
 
 	if (avrule->rule_kind != target->rule_kind) goto exit;
 
+	if (avrule->is_extended != target->is_extended) goto exit;
+
 	if (!cil_type_match_any(s1, s2)) goto exit;
 
 	if (t1->fqn != CIL_KEY_SELF && t2->fqn != CIL_KEY_SELF) {
@@ -254,8 +327,14 @@ int cil_find_matching_avrule(struct cil_tree_node *node, struct cil_avrule *avru
 		}
 	}
 
-	if (cil_classperms_list_match_any(cp1, cp2)) {
-		cil_list_append(matching, CIL_NODE, node);
+	if (!target->is_extended) {
+		if (cil_classperms_list_match_any(avrule->perms.classperms, target->perms.classperms)) {
+			cil_list_append(matching, CIL_NODE, node);
+		}
+	} else {
+		if (cil_permissionx_match_any(avrule->perms.x.permx, target->perms.x.permx)) {
+			cil_list_append(matching, CIL_NODE, node);
+		}
 	}
 
 	rc = SEPOL_OK;
@@ -264,7 +343,7 @@ exit:
 	return rc;
 }
 
-static int __cil_find_matching_avrule_in_ast(struct cil_tree_node *node,  __attribute__((unused)) uint32_t *finished, void *extra_args)
+static int __cil_find_matching_avrule_in_ast(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
 {
 	int rc = SEPOL_OK;
 	struct cil_args_find *args = extra_args;
@@ -278,8 +357,10 @@ static int __cil_find_matching_avrule_in_ast(struct cil_tree_node *node,  __attr
 	} else if (node->flavor == CIL_MACRO) {
 		*finished = CIL_TREE_SKIP_HEAD;
 		goto exit;
-	} else if (node->flavor == CIL_AVRULE) {
-		rc = cil_find_matching_avrule(node, node->data, args->target, args->matching, args->match_self);
+	} else if (node->flavor == CIL_AVRULE || node->flavor == CIL_AVRULEX) {
+		if (node->flavor == args->flavor) {
+			rc = cil_find_matching_avrule(node, node->data, args->target, args->matching, args->match_self);
+		}
 	}
 
 exit:
diff --git a/libsepol/cil/src/cil_find.h b/libsepol/cil/src/cil_find.h
index c8ca2d2..463ef34 100644
--- a/libsepol/cil/src/cil_find.h
+++ b/libsepol/cil/src/cil_find.h
@@ -35,5 +35,6 @@
 #define CIL_FIND_H_
 
 int cil_find_matching_avrule_in_ast(struct cil_tree_node *current, enum cil_flavor flavor, void *target, struct cil_list *matching, int match_self);
+struct cil_list *cil_expand_class(struct cil_class *class);
 
 #endif
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index 7f718d0..a0a5480 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -221,6 +221,7 @@ char *CIL_KEY_PERM;
 char *CIL_KEY_ALLOWX;
 char *CIL_KEY_AUDITALLOWX;
 char *CIL_KEY_DONTAUDITX;
+char *CIL_KEY_NEVERALLOWX;
 char *CIL_KEY_PERMISSIONX;
 char *CIL_KEY_IOCTL;
 char *CIL_KEY_UNORDERED;
@@ -570,12 +571,19 @@ struct cil_tunable {
 #define CIL_AVRULE_NEVERALLOW 128
 #define CIL_AVRULE_AV         (AVRULE_ALLOWED | AVRULE_AUDITALLOW | AVRULE_DONTAUDIT | AVRULE_NEVERALLOW)
 struct cil_avrule {
+	int is_extended;
 	uint32_t rule_kind;
 	char *src_str;
 	void *src; /* type, alias, or attribute */
 	char *tgt_str;	
 	void *tgt; /* type, alias, or attribute */
-	struct cil_list *classperms;
+	union {
+		struct cil_list *classperms;
+		struct {
+			char *permx_str;
+			struct cil_permissionx *permx;
+		} x;
+	} perms;
 };
 
 #define CIL_PERMX_KIND_IOCTL 1
@@ -583,21 +591,11 @@ struct cil_permissionx {
 	struct cil_symtab_datum datum;
 	uint32_t kind;
 	char *obj_str;
-	void *obj;
+	struct cil_class *obj;
 	struct cil_list *expr_str;
 	ebitmap_t *perms;
 };
 
-struct cil_avrulex {
-	uint32_t rule_kind;
-	char *src_str;
-	void *src; /* type, alias, or attribute */
-	char *tgt_str;
-	void *tgt; /* type, alias, or attribute */
-	char *permx_str;
-	struct cil_permissionx *permx;
-};
-
 #define CIL_TYPE_TRANSITION 16
 #define CIL_TYPE_MEMBER     32
 #define CIL_TYPE_CHANGE     64
@@ -974,7 +972,6 @@ void cil_condblock_init(struct cil_condblock **cb);
 void cil_tunable_init(struct cil_tunable **ciltun);
 void cil_tunif_init(struct cil_tunableif **tif);
 void cil_avrule_init(struct cil_avrule **avrule);
-void cil_avrulex_init(struct cil_avrulex **avrulex);
 void cil_permissionx_init(struct cil_permissionx **permx);
 void cil_type_rule_init(struct cil_type_rule **type_rule);
 void cil_roletransition_init(struct cil_roletransition **roletrans);
diff --git a/libsepol/cil/src/cil_list.c b/libsepol/cil/src/cil_list.c
index dbd554c..4e7843c 100644
--- a/libsepol/cil/src/cil_list.c
+++ b/libsepol/cil/src/cil_list.c
@@ -259,3 +259,19 @@ int cil_list_contains(struct cil_list *list, void *data)
 
 	return CIL_FALSE;
 }
+
+int cil_list_match_any(struct cil_list *l1, struct cil_list *l2)
+{
+	struct cil_list_item *i1;
+	struct cil_list_item *i2;
+
+	cil_list_for_each(i1, l1) {
+		cil_list_for_each(i2, l2) {
+			if (i1->data == i2->data && i1->flavor == i2->flavor) {
+				return CIL_TRUE;
+			}
+		}
+	}
+
+	return CIL_FALSE;
+}
diff --git a/libsepol/cil/src/cil_list.h b/libsepol/cil/src/cil_list.h
index a028036..6b4708a 100644
--- a/libsepol/cil/src/cil_list.h
+++ b/libsepol/cil/src/cil_list.h
@@ -59,5 +59,6 @@ struct cil_list_item *cil_list_insert(struct cil_list *list, struct cil_list_ite
 void cil_list_append_item(struct cil_list *list, struct cil_list_item *item);
 void cil_list_prepend_item(struct cil_list *list, struct cil_list_item *item);
 int cil_list_contains(struct cil_list *list, void *data);
+int cil_list_match_any(struct cil_list *l1, struct cil_list *l2);
 
 #endif
diff --git a/libsepol/cil/src/cil_mem.c b/libsepol/cil/src/cil_mem.c
index e19bc35..12c59be 100644
--- a/libsepol/cil/src/cil_mem.c
+++ b/libsepol/cil/src/cil_mem.c
@@ -29,6 +29,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <string.h>
 
 #include "cil_log.h"
@@ -98,3 +99,19 @@ char *cil_strdup(const char *str)
 
 	return mem;
 }
+
+__attribute__ ((format (printf, 2, 3))) int cil_asprintf(char **strp, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	va_start(ap, fmt);
+	rc = vasprintf(strp, fmt, ap);
+	va_end(ap);
+
+	if (rc == -1) {
+		(*cil_mem_error_handler)();
+	}
+
+	return rc;
+}
diff --git a/libsepol/cil/src/cil_mem.h b/libsepol/cil/src/cil_mem.h
index 3e4263c..902ce13 100644
--- a/libsepol/cil/src/cil_mem.h
+++ b/libsepol/cil/src/cil_mem.h
@@ -35,6 +35,7 @@ void *cil_malloc(size_t size);
 void *cil_calloc(size_t num_elements, size_t element_size);
 void *cil_realloc(void *ptr, size_t size);
 char *cil_strdup(const char *str);
+int cil_asprintf(char **strp, const char *fmt, ...);
 void (*cil_mem_error_handler)(void);
 
 #endif /* CIL_MEM_H_ */
diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c
index a9e2426..2c9b158 100644
--- a/libsepol/cil/src/cil_policy.c
+++ b/libsepol/cil/src/cil_policy.c
@@ -598,7 +598,7 @@ int cil_avrule_to_policy(FILE **file_arr, uint32_t file_index, struct cil_avrule
 		return SEPOL_ERR;
 	}
 
-	cil_avrule_to_policy_helper(file_arr, file_index, kind_str, src_str, tgt_str, rule->classperms);
+	cil_avrule_to_policy_helper(file_arr, file_index, kind_str, src_str, tgt_str, rule->perms.classperms);
 
 	return SEPOL_OK;
 }
diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
index 8050bbb..547397c 100644
--- a/libsepol/cil/src/cil_post.c
+++ b/libsepol/cil/src/cil_post.c
@@ -1220,9 +1220,9 @@ static int __cil_post_db_attr_helper(struct cil_tree_node *node, __attribute__((
 		break;
 	}
 	case CIL_AVRULEX: {
-		struct cil_avrulex *rule = node->data;
-		if (rule->permx_str == NULL) {
-			rc = __evaluate_permissionx_expression(rule->permx, db);
+		struct cil_avrule *rule = node->data;
+		if (rule->perms.x.permx_str == NULL) {
+			rc = __evaluate_permissionx_expression(rule->perms.x.permx, db);
 			if (rc != SEPOL_OK) goto exit;
 		}
 		break;
@@ -1862,7 +1862,7 @@ static int __cil_post_db_classperms_helper(struct cil_tree_node *node, uint32_t
 	}
 	case CIL_AVRULE: {
 		struct cil_avrule *avrule = node->data;
-		rc = __evaluate_classperms_list(avrule->classperms, db);
+		rc = __evaluate_classperms_list(avrule->perms.classperms, db);
 		if (rc != SEPOL_OK) {
 			goto exit;
 		}
diff --git a/libsepol/cil/src/cil_reset_ast.c b/libsepol/cil/src/cil_reset_ast.c
index 09cff05..06146ca 100644
--- a/libsepol/cil/src/cil_reset_ast.c
+++ b/libsepol/cil/src/cil_reset_ast.c
@@ -195,7 +195,7 @@ static void cil_reset_typeattributeset(struct cil_typeattributeset *tas)
 
 static void cil_reset_avrule(struct cil_avrule *rule)
 {
-	cil_reset_classperms_list(rule->classperms);
+	cil_reset_classperms_list(rule->perms.classperms);
 }
 
 static void cil_reset_rangetransition(struct cil_rangetransition *rangetrans)
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
index 1d3fb5a..14a9c75 100644
--- a/libsepol/cil/src/cil_resolve_ast.c
+++ b/libsepol/cil/src/cil_resolve_ast.c
@@ -273,6 +273,23 @@ int cil_type_used(struct cil_symtab_datum *datum)
 	return 0;
 }
 
+int cil_resolve_permissionx(struct cil_tree_node *current, struct cil_permissionx *permx, void *extra_args)
+{
+	struct cil_symtab_datum *obj_datum = NULL;
+	int rc = SEPOL_ERR;
+
+	rc = cil_resolve_name(current, permx->obj_str, CIL_SYM_CLASSES, extra_args, &obj_datum);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+	permx->obj = (struct cil_class*)obj_datum;
+
+	return SEPOL_OK;
+
+exit:
+	return rc;
+}
+
 int cil_resolve_avrule(struct cil_tree_node *current, void *extra_args)
 {
 	struct cil_args_resolve *args = extra_args;
@@ -281,6 +298,7 @@ int cil_resolve_avrule(struct cil_tree_node *current, void *extra_args)
 	struct cil_avrule *rule = current->data;
 	struct cil_symtab_datum *src_datum = NULL;
 	struct cil_symtab_datum *tgt_datum = NULL;
+	struct cil_symtab_datum *permx_datum = NULL;
 	int rc = SEPOL_ERR;
 
 	if (args != NULL) {
@@ -309,77 +327,23 @@ int cil_resolve_avrule(struct cil_tree_node *current, void *extra_args)
 		}
 	}
 
-	rc = cil_resolve_classperms_list(current, rule->classperms, extra_args);
-	if (rc != SEPOL_OK) {
-		goto exit;
-	}
-
-	return SEPOL_OK;
-
-exit:
-	return rc;
-}
-
-int cil_resolve_permissionx(struct cil_tree_node *current, struct cil_permissionx *permx, void *extra_args)
-{
-	struct cil_symtab_datum *obj_datum = NULL;
-	int rc = SEPOL_ERR;
-
-	rc = cil_resolve_name(current, permx->obj_str, CIL_SYM_CLASSES, extra_args, &obj_datum);
-	if (rc != SEPOL_OK) {
-		goto exit;
-	}
-	permx->obj = (struct cil_class*)obj_datum;
-
-	return SEPOL_OK;
-
-exit:
-	return rc;
-}
-
-int cil_resolve_avrulex(struct cil_tree_node *current, void *extra_args)
-{
-	struct cil_args_resolve *args = extra_args;
-	struct cil_db *db = NULL;
-
-	struct cil_avrulex *rule = current->data;
-	struct cil_symtab_datum *src_datum = NULL;
-	struct cil_symtab_datum *tgt_datum = NULL;
-	struct cil_symtab_datum *permx_datum = NULL;
-	int rc = SEPOL_ERR;
-
-	if (args != NULL) {
-		db = args->db;
-	}
-
-	rc = cil_resolve_name(current, rule->src_str, CIL_SYM_TYPES, args, &src_datum);
-	if (rc != SEPOL_OK) {
-		goto exit;
-	}
-	rule->src = src_datum;
-	cil_type_used(src_datum);
-
-	if (rule->tgt_str == CIL_KEY_SELF) {
-		rule->tgt = db->selftype;
-	} else {
-		rc = cil_resolve_name(current, rule->tgt_str, CIL_SYM_TYPES, args, &tgt_datum);
-		if (rc != SEPOL_OK) {
-			goto exit;
-		}
-		rule->tgt = tgt_datum;
-		cil_type_used(tgt_datum);
-	}
-
-	if (rule->permx_str != NULL) {
-		rc = cil_resolve_name(current, rule->permx_str, CIL_SYM_PERMX, args, &permx_datum);
+	if (!rule->is_extended) {
+		rc = cil_resolve_classperms_list(current, rule->perms.classperms, extra_args);
 		if (rc != SEPOL_OK) {
 			goto exit;
 		}
-		rule->permx = (struct cil_permissionx*)permx_datum;
 	} else {
-		rc = cil_resolve_permissionx(current, rule->permx, extra_args);
-		if (rc != SEPOL_OK) {
-			goto exit;
+		if (rule->perms.x.permx_str != NULL) {
+			rc = cil_resolve_name(current, rule->perms.x.permx_str, CIL_SYM_PERMX, args, &permx_datum);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+			rule->perms.x.permx = (struct cil_permissionx*)permx_datum;
+		} else {
+			rc = cil_resolve_permissionx(current, rule->perms.x.permx, extra_args);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
 		}
 	}
 
@@ -3489,10 +3453,8 @@ int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
 			rc = cil_resolve_classmapping(node, args);
 			break;
 		case CIL_AVRULE:
-			rc = cil_resolve_avrule(node, args);
-			break;
 		case CIL_AVRULEX:
-			rc = cil_resolve_avrulex(node, args);
+			rc = cil_resolve_avrule(node, args);
 			break;
 		case CIL_PERMISSIONX:
 			rc = cil_resolve_permissionx(node, (struct cil_permissionx*)node->data, args);
diff --git a/libsepol/cil/src/cil_tree.c b/libsepol/cil/src/cil_tree.c
index f641baa..c1f3327 100644
--- a/libsepol/cil/src/cil_tree.c
+++ b/libsepol/cil/src/cil_tree.c
@@ -1091,7 +1091,7 @@ void cil_tree_print_node(struct cil_tree_node *node)
 				cil_log(CIL_INFO, " %s", rule->tgt_str);
 			}
 
-			cil_tree_print_classperms_list(rule->classperms);
+			cil_tree_print_classperms_list(rule->perms.classperms);
 
 			cil_log(CIL_INFO, "\n");
 
diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c
index 5aec658..fbf397f 100644
--- a/libsepol/src/assertion.c
+++ b/libsepol/src/assertion.c
@@ -355,7 +355,7 @@ static int check_assertion_avtab_match(avtab_key_t *k, avtab_datum_t *d, void *a
 	avrule_t *avrule = a->avrule;
 	avtab_t *avtab = a->avtab;
 
-	if (k->specified != AVTAB_ALLOWED)
+	if (k->specified != AVTAB_ALLOWED && k->specified != AVTAB_XPERMS_ALLOWED)
 		goto exit;
 
 	if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index c32fda1..3854d6f 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -111,6 +111,11 @@ avtab_insert_node(avtab_t * h, int hvalue, avtab_ptr_t prev, avtab_key_t * key,
 			*xperms = *(datum->xperms);
 
 		newnode->datum.xperms = xperms;
+		/* data is usually ignored with xperms, except in the case of
+		 * neverallow checking, which requires permission bits to be set.
+		 * So copy data so it is set in the avtab
+		 */
+		newnode->datum.data = datum->data;
 	} else {
 		newnode->datum = *datum;
 	}
