[PATCH] selinux: validate constraint expression attr and op at load time

Stephen Smalley <[email protected]> Mon, 27 Jul 2026 10:44:38 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <[email protected]>
read_cons_helper() validates the expression type and stack depth
of each constraint node but leaves e->attr and e->op unchecked,
so a policy with an invalid operator or attribute value is
accepted at load and only detected when the constraint is evaluated.
constraint_expr_eval() handles such unrecognized cases with BUG()
so the first permission check that reaches such a node oopses in
the context of the checking process or panics with panic_on_oops.

Reject these expresssions when the policy is loaded, matching what
the libsepol validator already does.

Signed-off-by: Stephen Smalley <[email protected]>
---
 security/selinux/ss/policydb.c | 35 ++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ba1c9bd9e29f..c4a81bd31e05 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1332,6 +1332,27 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
 				if (depth == (CEXPR_MAXDEPTH - 1))
 					return -EINVAL;
 				depth++;
+				switch (e->attr) {
+				case CEXPR_USER:
+				case CEXPR_TYPE:
+					if (e->op != CEXPR_EQ &&
+					    e->op != CEXPR_NEQ)
+						return -EINVAL;
+					break;
+				case CEXPR_ROLE:
+				case CEXPR_L1L2:
+				case CEXPR_L1H2:
+				case CEXPR_H1L2:
+				case CEXPR_H1H2:
+				case CEXPR_L1H1:
+				case CEXPR_L2H2:
+					if (e->op < CEXPR_EQ ||
+					    e->op > CEXPR_INCOMP)
+						return -EINVAL;
+					break;
+				default:
+					return -EINVAL;
+				}
 				break;
 			case CEXPR_NAMES:
 				if (!allowxtarget && (e->attr & CEXPR_XTARGET))
@@ -1339,6 +1360,20 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
 				if (depth == (CEXPR_MAXDEPTH - 1))
 					return -EINVAL;
 				depth++;
+				switch (e->attr &
+					~(CEXPR_TARGET|CEXPR_XTARGET)) {
+				case CEXPR_USER:
+				case CEXPR_ROLE:
+				case CEXPR_TYPE:
+					break;
+				default:
+					return -EINVAL;
+				}
+				if ((e->attr & (CEXPR_TARGET|CEXPR_XTARGET)) ==
+					(CEXPR_TARGET|CEXPR_XTARGET))
+					return -EINVAL;
+				if (e->op != CEXPR_EQ && e->op != CEXPR_NEQ)
+					return -EINVAL;
 				rc = ebitmap_read(&e->names, fp);
 				if (rc)
 					return rc;
-- 
2.55.0