[PATCH] selinux: check level category sets once at load time

Stephen Smalley <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <[email protected]>
As reported by Jiri Vozar, commit 7edea6e8c8e8 ("selinux: beef up
isvalid checks") introduces a new loop in mls_level_isvalid() that
causes ~89-94% throughput regression in System V IPC message queue
operations (msgsnd/msgrcv).

Move the expensive part of the ebitmap checking to policy load time
instead as the reporter suggested.

Reported-by: Jiri Vozar <[email protected]>
Link: https://lore.kernel.org/selinux/CAMgFczCi2Z011dNf84Amc0Q-qnTt0+VUjWY+Y7zPyXdaH35Jvw@mail.gmail.com/
Fixes: 7edea6e8c8e8 ("selinux: beef up isvalid checks")
Suggested-by: Jiri Vozar <[email protected]>
Signed-off-by: Stephen Smalley <[email protected]>
---
 security/selinux/ss/mls.c      | 24 +++++++-----------------
 security/selinux/ss/policydb.c | 23 +++++++++++++++++++++++
 2 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 3cd36e2015fa..40c62600650e 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -160,9 +160,6 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
 {
 	const char *name;
 	const struct level_datum *levdatum;
-	struct ebitmap_node *node;
-	u32 bit;
-	int rc;
 
 	if (!l->sens || l->sens > p->p_levels.nprim)
 		return false;
@@ -176,21 +173,14 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
 		return false;
 
 	/*
-	 * Validate that all bits set in l->cat are also be set in
-	 * levdatum->level->cat and no bit in l->cat is larger than
-	 * p->p_cats.nprim.
+	 * l is valid iff every bit in l->cat is set in levdatum->level.cat
+	 * and no bit in l->cat is larger than p->p_cats.nprim.
+	 * policydb_index() has already verified that every bit set in
+	 * levdatum->level.cat names a defined category, so containment is
+	 * sufficient here.
 	 */
-	rc = ebitmap_contains(&levdatum->level.cat, &l->cat,
-			      p->p_cats.nprim);
-	if (!rc)
-		return false;
-
-	ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
-		if (!sym_name(p, SYM_CATS, bit))
-			return false;
-	}
-
-	return true;
+	return ebitmap_contains(&levdatum->level.cat, &l->cat,
+				p->p_cats.nprim);
 }
 
 bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index f141f007d96d..b75c977c6673 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -670,6 +670,23 @@ static int cat_index(void *key, void *datum, void *datap)
 	return 0;
 }
 
+static int sens_cat_index_check(void *key, void *datum, void *datap)
+{
+	struct policydb *p = datap;
+	struct level_datum *levdatum = datum;
+	struct ebitmap_node *node;
+	u32 bit;
+
+	ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
+		if (bit >= p->p_cats.nprim || !sym_name(p, SYM_CATS, bit)) {
+			pr_err("SELinux: sensitivity %s allows undefined category %u\n",
+				(const char *)key, bit + 1);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
 /* clang-format off */
 static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = {
 	common_index,
@@ -793,6 +810,12 @@ static int policydb_index(struct policydb *p)
 		}
 	}
 
+	if (p->mls_enabled) {
+		rc = hashtab_map(&p->p_levels.table, sens_cat_index_check, p);
+		if (rc)
+			goto out;
+	}
+
 	rc = 0;
 out:
 	return rc;
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.