* FAILED: patch "[PATCH] selinux: require a class's permission values to cover its" failed to apply to 6.18-stable tree
@ 2026-08-20 10:41 gregkh
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
0 siblings, 1 reply; 37+ messages in thread
From: gregkh @ 2026-08-20 10:41 UTC (permalink / raw)
To: hexlabsecurity, paul, stephen.smalley.work; +Cc: stable
The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x b98a8ac50775540f3804397ed08f61ef9910bcab
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026082020-deputize-mosaic-579e@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From b98a8ac50775540f3804397ed08f61ef9910bcab Mon Sep 17 00:00:00 2001
From: Bryam Vargas <hexlabsecurity@proton.me>
Date: Fri, 31 Jul 2026 12:44:10 -0500
Subject: [PATCH] selinux: require a class's permission values to cover its
permission count
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 69777e885ae7..d358200817bd 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1154,7 +1154,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1178,6 +1189,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1198,7 +1213,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc_obj(*comdatum);
@@ -1225,17 +1240,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1369,7 +1395,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc_obj(*cladatum);
@@ -1402,6 +1428,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1436,11 +1466,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 1/3] selinux: use u16 for security classes
2026-08-20 10:41 FAILED: patch "[PATCH] selinux: require a class's permission values to cover its" failed to apply to 6.18-stable tree gregkh
@ 2026-08-24 22:00 ` Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
0 siblings, 2 replies; 37+ messages in thread
From: Sasha Levin @ 2026-08-24 22:00 UTC (permalink / raw)
To: stable; +Cc: Christian Göttsche, Stephen Smalley, Paul Moore, Sasha Levin
From: Christian Göttsche <cgzones@googlemail.com>
[ Upstream commit fa79a596848fe38c55ccab8832ac35dac07fb00c ]
Security class identifiers are limited to 2^16, thus use the appropriate
type u16 consistently.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
security/selinux/ss/policydb.c | 5 +++--
security/selinux/ss/policydb.h | 10 +++++-----
security/selinux/ss/services.c | 2 +-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fdb8a62da04ad..37e0735b0dc8d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -951,7 +951,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -2042,7 +2042,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, tclass, ndatum, i;
+ u32 len, ttype, ndatum, i;
+ u16 tclass;
__le32 buf[3];
int rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742fb..a49275d1168d8 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
+ u16 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u16 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 82608b34260fd..cbc101b095a30 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3290,7 +3290,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u32 value = datum->value - 1;
+ u16 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
--
2.53.0
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 2/3] selinux: more strict policy parsing
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
@ 2026-08-24 22:00 ` Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
1 sibling, 0 replies; 37+ messages in thread
From: Sasha Levin @ 2026-08-24 22:00 UTC (permalink / raw)
To: stable; +Cc: Christian Göttsche, Stephen Smalley, Paul Moore, Sasha Levin
From: Christian Göttsche <cgzones@googlemail.com>
[ Upstream commit 18fa21f10d008a0fc22565109c7d38f304295912 ]
Be more strict during parsing of policies and reject invalid values.
Add some error messages in the case of policy parse failures, to
enhance debugging, either on a malformed policy or a too strict check.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: fixed checkpatch.pl warnings, style problems]
Signed-off-by: Paul Moore <paul@paul-moore.com>
Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
security/selinux/include/security.h | 1 +
security/selinux/ss/avtab.c | 35 ++++-
security/selinux/ss/avtab.h | 13 ++
security/selinux/ss/conditional.c | 18 +--
security/selinux/ss/constraint.h | 1 +
security/selinux/ss/policydb.c | 196 +++++++++++++++++++++++-----
security/selinux/ss/policydb.h | 23 +++-
security/selinux/ss/services.c | 6 +-
8 files changed, 233 insertions(+), 60 deletions(-)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 30e3fa962f6fa..f698ff7962282 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -225,6 +225,7 @@ int security_read_policy(void **data, size_t *len);
int security_read_state_kernel(void **data, size_t *len);
int security_policycap_supported(unsigned int req_cap);
+/* Maximum supported number of permissions per class */
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace1..33556922f15ed 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -349,7 +349,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int rc;
- unsigned int set, vers = pol->policyvers;
+ unsigned int vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -360,9 +360,12 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ /* Read five or more items: source type, target type,
+ * target class, AV type, and at least one datum.
+ */
items2 = le32_to_cpu(buf32[0]);
- if (items2 > ARRAY_SIZE(buf32)) {
- pr_err("SELinux: avtab: entry overflow\n");
+ if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
+ pr_err("SELinux: avtab: invalid item count\n");
return -EINVAL;
}
rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -391,6 +394,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ pr_err("SELinux: avtab: invalid type or class\n");
+ return -EINVAL;
+ }
+
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
@@ -409,6 +419,11 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
+ if (items >= items2) {
+ pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
+ items + 1, items2);
+ return -EINVAL;
+ }
key.specified = spec_order[i] | enabled;
datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
@@ -444,9 +459,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
- if (!set || set > 1) {
- pr_err("SELinux: avtab: more than one specifier\n");
+ if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
+ pr_err("SELinux: avtab: not exactly one specifier\n");
+ return -EINVAL;
+ }
+
+ if (key.specified & ~AVTAB_SPECIFIER_MASK) {
+ pr_err("SELinux: avtab: invalid specifier\n");
return -EINVAL;
}
@@ -471,6 +490,10 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ if (!avtab_is_valid_xperm_specified(xperms.specified))
+ pr_warn_once_policyload(pol,
+ "SELinux: avtab: unsupported xperm specifier %#x\n",
+ xperms.specified);
rc = next_entry(&xperms.driver, fp, sizeof(u8));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 850b3453f2598..1de4cce288a76 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -44,6 +44,7 @@ struct avtab_key {
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
u16 specified; /* what field is specified */
};
@@ -68,6 +69,18 @@ struct avtab_extended_perms {
struct extended_perms_data perms;
};
+static inline bool avtab_is_valid_xperm_specified(u8 specified)
+{
+ switch (specified) {
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ case AVTAB_XPERMS_NLMSG:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct avtab_datum {
union {
u32 data; /* access vector or type value */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 1bebfcb9c6a12..db30462ed6a32 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -199,19 +199,12 @@ int cond_index_bool(void *key, void *datum, void *datap)
return 0;
}
-static int bool_isvalid(struct cond_bool_datum *b)
-{
- if (!(b->state == 0 || b->state == 1))
- return 0;
- return 1;
-}
-
int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
- u32 len;
+ u32 len, val;
int rc;
booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL);
@@ -223,11 +216,12 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
- booldatum->state = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
rc = -EINVAL;
- if (!bool_isvalid(booldatum))
+ if (!val_is_boolean(val))
goto err;
+ booldatum->state = (int)val;
len = le32_to_cpu(buf[2]);
@@ -241,6 +235,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
return 0;
err:
+ pr_err("SELinux: conditional: failed to read boolean\n");
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
@@ -362,7 +357,8 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 0;
}
- if (expr->boolean > p->p_bools.nprim) {
+ if (expr->expr_type == COND_BOOL &&
+ (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
pr_err("SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 203033cfad672..1d75a8a044df9 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -50,6 +50,7 @@ struct constraint_expr {
u32 op; /* operator */
struct ebitmap names; /* names */
+ /* internally unused, only forwarded via policydb_write() */
struct type_set *type_names;
struct constraint_expr *next; /* next expression */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 37e0735b0dc8d..fac3b61bfa202 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,13 +638,11 @@ static int sens_index(void *key, void *datum, void *datap)
levdatum = datum;
p = datap;
- if (!levdatum->isalias) {
- if (!levdatum->level.sens ||
- levdatum->level.sens > p->p_levels.nprim)
- return -EINVAL;
+ if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
+ return -EINVAL;
+ if (!levdatum->isalias)
p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
- }
return 0;
}
@@ -657,12 +655,11 @@ static int cat_index(void *key, void *datum, void *datap)
catdatum = datum;
p = datap;
- if (!catdatum->isalias) {
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ if (!catdatum->isalias)
p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
- }
return 0;
}
@@ -1160,6 +1157,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1197,6 +1197,9 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
@@ -1348,7 +1351,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1361,8 +1364,16 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
- cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
+
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (val > U16_MAX)
+ goto bad;
+ cladatum->value = val;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
@@ -1430,16 +1441,59 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- cladatum->default_user = le32_to_cpu(buf[0]);
- cladatum->default_role = le32_to_cpu(buf[1]);
- cladatum->default_range = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_user = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[1]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_role = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[2]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE_LOW:
+ case DEFAULT_SOURCE_HIGH:
+ case DEFAULT_SOURCE_LOW_HIGH:
+ case DEFAULT_TARGET_LOW:
+ case DEFAULT_TARGET_HIGH:
+ case DEFAULT_TARGET_LOW_HIGH:
+ case DEFAULT_GLBLUB:
+ cladatum->default_range = val;
+ break;
+ default:
+ goto bad;
+ }
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- cladatum->default_type = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_TARGET:
+ case DEFAULT_SOURCE:
+ cladatum->default_type = val;
+ break;
+ default:
+ goto bad;
+ }
}
rc = symtab_insert(s, key, cladatum);
@@ -1449,6 +1503,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1640,7 +1696,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len;
+ u32 len, val;
levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
@@ -1651,7 +1707,11 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- levdatum->isalias = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ levdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1667,6 +1727,8 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1676,7 +1738,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len;
+ u32 len, val;
catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
@@ -1688,7 +1750,11 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- catdatum->isalias = le32_to_cpu(buf[2]);
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ catdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1700,6 +1766,8 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1881,7 +1949,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel;
+ u32 i, nel, val;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1912,7 +1980,11 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rt->target_class = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto out;
+ rt->target_class = val;
} else
rt->target_class = p->process_class;
@@ -1949,6 +2021,8 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
+ if (rc)
+ pr_err("SELinux: invalid range\n");
return rc;
}
@@ -1957,7 +2031,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype;
+ u32 len, stype, otype, val;
__le32 buf[4];
int rc;
@@ -1976,9 +2050,17 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
+ rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
+ if (!policydb_type_isvalid(p, stype))
+ goto out;
key.ttype = le32_to_cpu(buf[1]);
- key.tclass = le32_to_cpu(buf[2]);
+ if (!policydb_type_isvalid(p, key.ttype))
+ goto out;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ key.tclass = val;
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -2034,6 +2116,9 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2042,7 +2127,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i;
+ u32 len, ttype, ndatum, i, val;
u16 tclass;
__le32 buf[3];
int rc;
@@ -2062,8 +2147,15 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
+ rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- tclass = le32_to_cpu(buf[1]);
+ if (!policydb_type_isvalid(p, ttype))
+ goto out;
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ tclass = val;
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2093,6 +2185,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ if (!policydb_type_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2124,6 +2220,9 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2171,7 +2270,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2;
+ u32 i, j, nel, nel2, len, len2, val;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2241,7 +2340,11 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- newc->v.sclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
+ goto out;
+ newc->v.sclass = val;
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2278,6 +2381,9 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2286,7 +2392,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len;
+ u32 j, nel, len, val;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2350,11 +2456,25 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
- c->u.port.protocol = le32_to_cpu(buf[0]);
- c->u.port.low_port = le32_to_cpu(buf[1]);
- c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0],
- p, fp);
+
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U8_MAX)
+ goto out;
+ c->u.port.protocol = val;
+ val = le32_to_cpu(buf[1]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.low_port = val;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.high_port = val;
+ if (c->u.port.low_port == 0 ||
+ c->u.port.low_port > c->u.port.high_port)
+ goto out;
+
+ rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto out;
break;
@@ -2472,6 +2592,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2486,7 +2608,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm, val;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2678,7 +2800,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rtk->tclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto bad;
+ rtk->tclass = val;
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index a49275d1168d8..20b834581106a 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
+ u32 bounds; /* boundary of role, 0 for none */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -110,7 +110,8 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
+ u32 bounds; /* boundary of type, 0 for none */
+ /* internally unused, only forwarded via policydb_write() */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -118,7 +119,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
+ u32 bounds; /* bounds of user, 0 for none */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs (can be 0 for wildcard) */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -388,9 +389,23 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+static inline bool val_is_boolean(u32 value)
+{
+ return value == 0 || value == 1;
+}
+
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
+#define pr_warn_once_policyload(policy, fmt, ...) \
+ do { \
+ static const void *prev_policy__; \
+ if (prev_policy__ != policy) { \
+ pr_warn(fmt, ##__VA_ARGS__); \
+ prev_policy__ = policy; \
+ } \
+ } while (0)
+
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index cbc101b095a30..fb150025359b0 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
- BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[32];
+ char *permission_names[SEL_VEC_MAX];
int index;
u32 length;
bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < 32; index++) {
+ for (index = 0; index < SEL_VEC_MAX; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
--
2.53.0
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
@ 2026-08-24 22:00 ` Sasha Levin
2026-08-25 10:02 ` Wentao Guan
` (7 more replies)
1 sibling, 8 replies; 37+ messages in thread
From: Sasha Levin @ 2026-08-24 22:00 UTC (permalink / raw)
To: stable; +Cc: Bryam Vargas, Stephen Smalley, Paul Moore, Sasha Levin
From: Bryam Vargas <hexlabsecurity@proton.me>
[ Upstream commit b98a8ac50775540f3804397ed08f61ef9910bcab ]
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 5 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fac3b61bfa202..ef7f767a682b3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1139,7 +1139,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1171,6 +1182,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
return 0;
bad:
@@ -1183,7 +1198,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
@@ -1205,17 +1220,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1351,7 +1377,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1379,6 +1405,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1413,11 +1443,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
--
2.53.0
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
@ 2026-08-25 10:02 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
` (6 subsequent siblings)
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:02 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work
Hi,
Thanks for your effort to take the backport series, but it can goes better.
What happened in my side is Github Copliot review point out the issue:
"This validation runs after symtab_insert(), so the table already owns key
and perdatum. On an out-of-range value, the bad path frees both while leaving
dangling pointers in the hash table, and rc is still 0, so malformed policy
data is accepted and later accesses can use freed memory.
Validate the value before inserting it and return -EINVAL."
In continues review, there LLM point out the mainline don`t had the issuse,
for correct sequence is that git log --oneline
(v6.18.46-1) selinux: require a class's permission values to cover its permission count
selinux: reject a class permission count below its inherited common
selinux: reject a permission value exceeding the class permission count
selinux: more strict policy parsing
selinux: use u16 for security classes
Revert "selinux: reject a class permission count below its inherited common"
Revert "selinux: reject a permission value exceeding the class permission count"
(tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
You can try to revert 9b46fba7528f58ca09eac67ec7d474ff1e9af066 and
next revert acd5b09be98fd38b7392307880156fb0452a7276, then pick
1.fa79a596848fe38c55ccab8832ac35dac07fb00c
2.18fa21f10d008a0fc22565109c7d38f304295912
3.d14b5d0e97fccd27974fedc03b903408872907fd
4.9a82dcd98b6e6e11cfd162410967951f12152528
5.b98a8ac50775540f3804397ed08f61ef9910bcab
I will send the patches in my side to show it, note these problem may existed
in other branch I don`t seen before.
BRs
Wentao Guan
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count"
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
2026-08-25 10:02 ` Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-27 5:17 ` Sasha Levin
2026-08-25 10:03 ` [PATCH 6.18.y 2/7] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
` (5 subsequent siblings)
7 siblings, 1 reply; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work, Wentao Guan
This reverts commit 62be2361da29505d3183da854f1c98d2e208e852.
This pick was applied without its upstream prerequisites (commit
fa79a596848f "selinux: use u16 for security classes" and commit
18fa21f10d00 "selinux: more strict policy parsing") and is therefore
incomplete. Drop it here; it is re-applied later in this series in the
correct upstream order.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fdb8a62da04ad..69c19ef4b8817 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1168,9 +1168,6 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
- /* indexes an nprim-sized array in security_get_permissions() */
- if (perdatum->value > s->nprim)
- goto bad;
return 0;
bad:
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 2/7] Revert "selinux: reject a class permission count below its inherited common"
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
2026-08-25 10:02 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 3/7] selinux: use u16 for security classes Wentao Guan
` (4 subsequent siblings)
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work, Wentao Guan
This reverts commit 9f721b5d9e1d956ffe965bc09375989eb51881fd.
This pick was applied without its upstream prerequisites (commit
fa79a596848f "selinux: use u16 for security classes" and commit
18fa21f10d00 "selinux: more strict policy parsing") and is therefore
incomplete. Drop it here; it is re-applied later in this series in the
correct upstream order.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 69c19ef4b8817..48ee87070ab0d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1385,18 +1385,6 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
-
- /*
- * security_get_permissions() maps the common's permissions
- * into an array sized by this class's nprim, so a class must
- * declare at least as many as the common it inherits.
- */
- if (cladatum->permissions.nprim <
- cladatum->comdatum->permissions.nprim) {
- pr_err("SELinux: class %s has fewer permissions than common %s\n",
- key, cladatum->comkey);
- goto bad;
- }
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, &cladatum->permissions, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 3/7] selinux: use u16 for security classes
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
` (2 preceding siblings ...)
2026-08-25 10:03 ` [PATCH 6.18.y 2/7] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 4/7] selinux: more strict policy parsing Wentao Guan
` (3 subsequent siblings)
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal
Cc: hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche, Wentao Guan
From: Christian Göttsche <cgzones@googlemail.com>
Security class identifiers are limited to 2^16, thus use the appropriate
type u16 consistently.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit fa79a596848fe38c55ccab8832ac35dac07fb00c)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 5 +++--
security/selinux/ss/policydb.h | 10 +++++-----
security/selinux/ss/services.c | 2 +-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 48ee87070ab0d..fdee7e22462aa 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -951,7 +951,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -2027,7 +2027,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, tclass, ndatum, i;
+ u32 len, ttype, ndatum, i;
+ u16 tclass;
__le32 buf[3];
int rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742fb..a49275d1168d8 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
+ u16 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u16 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 82608b34260fd..cbc101b095a30 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3290,7 +3290,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u32 value = datum->value - 1;
+ u16 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 4/7] selinux: more strict policy parsing
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
` (3 preceding siblings ...)
2026-08-25 10:03 ` [PATCH 6.18.y 3/7] selinux: use u16 for security classes Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 5/7] selinux: reject a permission value exceeding the class permission count Wentao Guan
` (2 subsequent siblings)
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal
Cc: hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche, Wentao Guan
From: Christian Göttsche <cgzones@googlemail.com>
Be more strict during parsing of policies and reject invalid values.
Add some error messages in the case of policy parse failures, to
enhance debugging, either on a malformed policy or a too strict check.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: fixed checkpatch.pl warnings, style problems]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 18fa21f10d008a0fc22565109c7d38f304295912)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/include/security.h | 1 +
security/selinux/ss/avtab.c | 35 ++++-
security/selinux/ss/avtab.h | 13 ++
security/selinux/ss/conditional.c | 18 +--
security/selinux/ss/constraint.h | 1 +
security/selinux/ss/policydb.c | 196 +++++++++++++++++++++++-----
security/selinux/ss/policydb.h | 23 +++-
security/selinux/ss/services.c | 6 +-
8 files changed, 233 insertions(+), 60 deletions(-)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 30e3fa962f6fa..f698ff7962282 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -225,6 +225,7 @@ int security_read_policy(void **data, size_t *len);
int security_read_state_kernel(void **data, size_t *len);
int security_policycap_supported(unsigned int req_cap);
+/* Maximum supported number of permissions per class */
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace1..33556922f15ed 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -349,7 +349,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int rc;
- unsigned int set, vers = pol->policyvers;
+ unsigned int vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -360,9 +360,12 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ /* Read five or more items: source type, target type,
+ * target class, AV type, and at least one datum.
+ */
items2 = le32_to_cpu(buf32[0]);
- if (items2 > ARRAY_SIZE(buf32)) {
- pr_err("SELinux: avtab: entry overflow\n");
+ if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
+ pr_err("SELinux: avtab: invalid item count\n");
return -EINVAL;
}
rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -391,6 +394,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ pr_err("SELinux: avtab: invalid type or class\n");
+ return -EINVAL;
+ }
+
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
@@ -409,6 +419,11 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
+ if (items >= items2) {
+ pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
+ items + 1, items2);
+ return -EINVAL;
+ }
key.specified = spec_order[i] | enabled;
datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
@@ -444,9 +459,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
- if (!set || set > 1) {
- pr_err("SELinux: avtab: more than one specifier\n");
+ if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
+ pr_err("SELinux: avtab: not exactly one specifier\n");
+ return -EINVAL;
+ }
+
+ if (key.specified & ~AVTAB_SPECIFIER_MASK) {
+ pr_err("SELinux: avtab: invalid specifier\n");
return -EINVAL;
}
@@ -471,6 +490,10 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ if (!avtab_is_valid_xperm_specified(xperms.specified))
+ pr_warn_once_policyload(pol,
+ "SELinux: avtab: unsupported xperm specifier %#x\n",
+ xperms.specified);
rc = next_entry(&xperms.driver, fp, sizeof(u8));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 850b3453f2598..1de4cce288a76 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -44,6 +44,7 @@ struct avtab_key {
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
u16 specified; /* what field is specified */
};
@@ -68,6 +69,18 @@ struct avtab_extended_perms {
struct extended_perms_data perms;
};
+static inline bool avtab_is_valid_xperm_specified(u8 specified)
+{
+ switch (specified) {
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ case AVTAB_XPERMS_NLMSG:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct avtab_datum {
union {
u32 data; /* access vector or type value */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 1bebfcb9c6a12..db30462ed6a32 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -199,19 +199,12 @@ int cond_index_bool(void *key, void *datum, void *datap)
return 0;
}
-static int bool_isvalid(struct cond_bool_datum *b)
-{
- if (!(b->state == 0 || b->state == 1))
- return 0;
- return 1;
-}
-
int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
- u32 len;
+ u32 len, val;
int rc;
booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL);
@@ -223,11 +216,12 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
- booldatum->state = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
rc = -EINVAL;
- if (!bool_isvalid(booldatum))
+ if (!val_is_boolean(val))
goto err;
+ booldatum->state = (int)val;
len = le32_to_cpu(buf[2]);
@@ -241,6 +235,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
return 0;
err:
+ pr_err("SELinux: conditional: failed to read boolean\n");
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
@@ -362,7 +357,8 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 0;
}
- if (expr->boolean > p->p_bools.nprim) {
+ if (expr->expr_type == COND_BOOL &&
+ (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
pr_err("SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 203033cfad672..1d75a8a044df9 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -50,6 +50,7 @@ struct constraint_expr {
u32 op; /* operator */
struct ebitmap names; /* names */
+ /* internally unused, only forwarded via policydb_write() */
struct type_set *type_names;
struct constraint_expr *next; /* next expression */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fdee7e22462aa..8dc77e347a77f 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,13 +638,11 @@ static int sens_index(void *key, void *datum, void *datap)
levdatum = datum;
p = datap;
- if (!levdatum->isalias) {
- if (!levdatum->level.sens ||
- levdatum->level.sens > p->p_levels.nprim)
- return -EINVAL;
+ if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
+ return -EINVAL;
+ if (!levdatum->isalias)
p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
- }
return 0;
}
@@ -657,12 +655,11 @@ static int cat_index(void *key, void *datum, void *datap)
catdatum = datum;
p = datap;
- if (!catdatum->isalias) {
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ if (!catdatum->isalias)
p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
- }
return 0;
}
@@ -1160,6 +1157,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1194,6 +1194,9 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
@@ -1345,7 +1348,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1358,8 +1361,16 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
- cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
+
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (val > U16_MAX)
+ goto bad;
+ cladatum->value = val;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
@@ -1415,16 +1426,59 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- cladatum->default_user = le32_to_cpu(buf[0]);
- cladatum->default_role = le32_to_cpu(buf[1]);
- cladatum->default_range = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_user = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[1]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_role = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[2]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE_LOW:
+ case DEFAULT_SOURCE_HIGH:
+ case DEFAULT_SOURCE_LOW_HIGH:
+ case DEFAULT_TARGET_LOW:
+ case DEFAULT_TARGET_HIGH:
+ case DEFAULT_TARGET_LOW_HIGH:
+ case DEFAULT_GLBLUB:
+ cladatum->default_range = val;
+ break;
+ default:
+ goto bad;
+ }
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- cladatum->default_type = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_TARGET:
+ case DEFAULT_SOURCE:
+ cladatum->default_type = val;
+ break;
+ default:
+ goto bad;
+ }
}
rc = symtab_insert(s, key, cladatum);
@@ -1434,6 +1488,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1625,7 +1681,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len;
+ u32 len, val;
levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
@@ -1636,7 +1692,11 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- levdatum->isalias = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ levdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1652,6 +1712,8 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1661,7 +1723,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len;
+ u32 len, val;
catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
@@ -1673,7 +1735,11 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- catdatum->isalias = le32_to_cpu(buf[2]);
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ catdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1685,6 +1751,8 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1866,7 +1934,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel;
+ u32 i, nel, val;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1897,7 +1965,11 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rt->target_class = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto out;
+ rt->target_class = val;
} else
rt->target_class = p->process_class;
@@ -1934,6 +2006,8 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
+ if (rc)
+ pr_err("SELinux: invalid range\n");
return rc;
}
@@ -1942,7 +2016,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype;
+ u32 len, stype, otype, val;
__le32 buf[4];
int rc;
@@ -1961,9 +2035,17 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
+ rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
+ if (!policydb_type_isvalid(p, stype))
+ goto out;
key.ttype = le32_to_cpu(buf[1]);
- key.tclass = le32_to_cpu(buf[2]);
+ if (!policydb_type_isvalid(p, key.ttype))
+ goto out;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ key.tclass = val;
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -2019,6 +2101,9 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2027,7 +2112,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i;
+ u32 len, ttype, ndatum, i, val;
u16 tclass;
__le32 buf[3];
int rc;
@@ -2047,8 +2132,15 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
+ rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- tclass = le32_to_cpu(buf[1]);
+ if (!policydb_type_isvalid(p, ttype))
+ goto out;
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ tclass = val;
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2078,6 +2170,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ if (!policydb_type_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2109,6 +2205,9 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2156,7 +2255,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2;
+ u32 i, j, nel, nel2, len, len2, val;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2226,7 +2325,11 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- newc->v.sclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
+ goto out;
+ newc->v.sclass = val;
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2263,6 +2366,9 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2271,7 +2377,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len;
+ u32 j, nel, len, val;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2335,11 +2441,25 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
- c->u.port.protocol = le32_to_cpu(buf[0]);
- c->u.port.low_port = le32_to_cpu(buf[1]);
- c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0],
- p, fp);
+
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U8_MAX)
+ goto out;
+ c->u.port.protocol = val;
+ val = le32_to_cpu(buf[1]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.low_port = val;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.high_port = val;
+ if (c->u.port.low_port == 0 ||
+ c->u.port.low_port > c->u.port.high_port)
+ goto out;
+
+ rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto out;
break;
@@ -2457,6 +2577,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2471,7 +2593,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm, val;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2663,7 +2785,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rtk->tclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto bad;
+ rtk->tclass = val;
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index a49275d1168d8..20b834581106a 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
+ u32 bounds; /* boundary of role, 0 for none */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -110,7 +110,8 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
+ u32 bounds; /* boundary of type, 0 for none */
+ /* internally unused, only forwarded via policydb_write() */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -118,7 +119,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
+ u32 bounds; /* bounds of user, 0 for none */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs (can be 0 for wildcard) */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -388,9 +389,23 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+static inline bool val_is_boolean(u32 value)
+{
+ return value == 0 || value == 1;
+}
+
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
+#define pr_warn_once_policyload(policy, fmt, ...) \
+ do { \
+ static const void *prev_policy__; \
+ if (prev_policy__ != policy) { \
+ pr_warn(fmt, ##__VA_ARGS__); \
+ prev_policy__ = policy; \
+ } \
+ } while (0)
+
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index cbc101b095a30..fb150025359b0 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
- BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[32];
+ char *permission_names[SEL_VEC_MAX];
int index;
u32 length;
bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < 32; index++) {
+ for (index = 0; index < SEL_VEC_MAX; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 5/7] selinux: reject a permission value exceeding the class permission count
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
` (4 preceding siblings ...)
2026-08-25 10:03 ` [PATCH 6.18.y 4/7] selinux: more strict policy parsing Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 6/7] selinux: reject a class permission count below its inherited common Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 7/7] selinux: require a class's permission values to cover its permission count Wentao Guan
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work, Wentao Guan
From: Bryam Vargas <hexlabsecurity@proton.me>
perm_read() bounds a permission value by SEL_VEC_MAX but never by the
nprim of the owning class or common, which is taken verbatim from the
policy image. security_get_permissions() then writes perms[value - 1]
into an nprim-sized kcalloc() array, so a class declaring fewer
permissions than its largest permission value drives an out-of-bounds
heap write. The top-level symbol tables are validated this way; the
nested per-class permission table is not.
Reject a permission whose value exceeds nprim, which is already set when
perm_read() runs. Well-formed policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: tweak comment for line length]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit d14b5d0e97fccd27974fedc03b903408872907fd)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 8dc77e347a77f..a3d52beb65487 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1160,6 +1160,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 6/7] selinux: reject a class permission count below its inherited common
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
` (5 preceding siblings ...)
2026-08-25 10:03 ` [PATCH 6.18.y 5/7] selinux: reject a permission value exceeding the class permission count Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 7/7] selinux: require a class's permission values to cover its permission count Wentao Guan
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work, Wentao Guan
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() maps an inherited common's permissions into
an array sized by the class's own permissions.nprim, but class_read()
takes that nprim verbatim from the policy image and never checks that it
covers the common. A class that inherits a common of N permissions while
declaring a smaller nprim is accepted, and on load the common's
permissions are written past the class-sized array -- an out-of-bounds
heap write.
Reject a class whose permission count is below its inherited common's.
Well-formed policies, where the class count already includes the
inherited permissions, are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 9a82dcd98b6e6e11cfd162410967951f12152528)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index a3d52beb65487..926dae3731a0d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1399,6 +1399,18 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
+
+ /*
+ * security_get_permissions() maps the common's permissions
+ * into an array sized by this class's nprim, so a class must
+ * declare at least as many as the common it inherits.
+ */
+ if (cladatum->permissions.nprim <
+ cladatum->comdatum->permissions.nprim) {
+ pr_err("SELinux: class %s has fewer permissions than common %s\n",
+ key, cladatum->comkey);
+ goto bad;
+ }
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, &cladatum->permissions, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 7/7] selinux: require a class's permission values to cover its permission count
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
` (6 preceding siblings ...)
2026-08-25 10:03 ` [PATCH 6.18.y 6/7] selinux: reject a class permission count below its inherited common Wentao Guan
@ 2026-08-25 10:03 ` Wentao Guan
7 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-25 10:03 UTC (permalink / raw)
To: sashal; +Cc: hexlabsecurity, paul, stable, stephen.smalley.work, Wentao Guan
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit b98a8ac50775540f3804397ed08f61ef9910bcab)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 5 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 926dae3731a0d..483d7d54642a3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1139,7 +1139,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1163,6 +1174,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1183,7 +1198,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
@@ -1205,17 +1220,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1351,7 +1377,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1379,6 +1405,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1413,11 +1443,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count"
2026-08-25 10:03 ` [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
@ 2026-08-27 5:17 ` Sasha Levin
2026-08-27 5:47 ` [PATCH 7.1.y 1/5] selinux: use u16 for security classes Wentao Guan
` (14 more replies)
0 siblings, 15 replies; 37+ messages in thread
From: Sasha Levin @ 2026-08-27 5:17 UTC (permalink / raw)
To: Wentao Guan
Cc: Sasha Levin, hexlabsecurity, paul, stable, stephen.smalley.work
> This reverts commit 62be2361da29505d3183da854f1c98d2e208e852.
>
> This pick was applied without its upstream prerequisites (commit
> fa79a596848f "selinux: use u16 for security classes" and commit
> 18fa21f10d00 "selinux: more strict policy parsing") and is therefore
> incomplete.
The series no longer applies...
Could you respin against the current 6.18 -rc, and add a 7.1.y version?
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 7.1.y 1/5] selinux: use u16 for security classes
2026-08-27 5:17 ` Sasha Levin
@ 2026-08-27 5:47 ` Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 2/5] selinux: more strict policy parsing Wentao Guan
` (13 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 5:47 UTC (permalink / raw)
To: sashal
Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche
From: Christian Göttsche <cgzones@googlemail.com>
Security class identifiers are limited to 2^16, thus use the appropriate
type u16 consistently.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit fa79a596848fe38c55ccab8832ac35dac07fb00c)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 5 +++--
security/selinux/ss/policydb.h | 10 +++++-----
security/selinux/ss/services.c | 2 +-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 738fd47f33e68..3afea36dfcdf0 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -931,7 +931,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -2005,7 +2005,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, tclass, ndatum, i;
+ u32 len, ttype, ndatum, i;
+ u16 tclass;
__le32 buf[3];
int rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742fb..a49275d1168d8 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
+ u16 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u16 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 143021c5e326d..b84d99c3fed98 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3288,7 +3288,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u32 value = datum->value - 1;
+ u16 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 7.1.y 2/5] selinux: more strict policy parsing
2026-08-27 5:17 ` Sasha Levin
2026-08-27 5:47 ` [PATCH 7.1.y 1/5] selinux: use u16 for security classes Wentao Guan
@ 2026-08-27 5:47 ` Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 3/5] selinux: reject a permission value exceeding the class permission count Wentao Guan
` (12 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 5:47 UTC (permalink / raw)
To: sashal
Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche
From: Christian Göttsche <cgzones@googlemail.com>
Be more strict during parsing of policies and reject invalid values.
Add some error messages in the case of policy parse failures, to
enhance debugging, either on a malformed policy or a too strict check.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: fixed checkpatch.pl warnings, style problems]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 18fa21f10d008a0fc22565109c7d38f304295912)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/include/security.h | 1 +
security/selinux/ss/avtab.c | 35 ++++-
security/selinux/ss/avtab.h | 13 ++
security/selinux/ss/conditional.c | 18 +--
security/selinux/ss/constraint.h | 1 +
security/selinux/ss/policydb.c | 196 +++++++++++++++++++++++-----
security/selinux/ss/policydb.h | 23 +++-
security/selinux/ss/services.c | 6 +-
8 files changed, 233 insertions(+), 60 deletions(-)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0babb89921816..a74415e3afd3a 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -236,6 +236,7 @@ int security_read_policy(void **data, size_t *len);
int security_read_state_kernel(void **data, size_t *len);
int security_policycap_supported(unsigned int req_cap);
+/* Maximum supported number of permissions per class */
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index d12ca337e6498..fb67b36001566 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -316,7 +316,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int rc;
- unsigned int set, vers = pol->policyvers;
+ unsigned int vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -327,9 +327,12 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ /* Read five or more items: source type, target type,
+ * target class, AV type, and at least one datum.
+ */
items2 = le32_to_cpu(buf32[0]);
- if (items2 > ARRAY_SIZE(buf32)) {
- pr_err("SELinux: avtab: entry overflow\n");
+ if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
+ pr_err("SELinux: avtab: invalid item count\n");
return -EINVAL;
}
rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -358,6 +361,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ pr_err("SELinux: avtab: invalid type or class\n");
+ return -EINVAL;
+ }
+
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
@@ -376,6 +386,11 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
+ if (items >= items2) {
+ pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
+ items + 1, items2);
+ return -EINVAL;
+ }
key.specified = spec_order[i] | enabled;
datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
@@ -411,9 +426,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
- if (!set || set > 1) {
- pr_err("SELinux: avtab: more than one specifier\n");
+ if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
+ pr_err("SELinux: avtab: not exactly one specifier\n");
+ return -EINVAL;
+ }
+
+ if (key.specified & ~AVTAB_SPECIFIER_MASK) {
+ pr_err("SELinux: avtab: invalid specifier\n");
return -EINVAL;
}
@@ -438,6 +457,10 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ if (!avtab_is_valid_xperm_specified(xperms.specified))
+ pr_warn_once_policyload(pol,
+ "SELinux: avtab: unsupported xperm specifier %#x\n",
+ xperms.specified);
rc = next_entry(&xperms.driver, fp, sizeof(u8));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 850b3453f2598..1de4cce288a76 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -44,6 +44,7 @@ struct avtab_key {
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
u16 specified; /* what field is specified */
};
@@ -68,6 +69,18 @@ struct avtab_extended_perms {
struct extended_perms_data perms;
};
+static inline bool avtab_is_valid_xperm_specified(u8 specified)
+{
+ switch (specified) {
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ case AVTAB_XPERMS_NLMSG:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct avtab_datum {
union {
u32 data; /* access vector or type value */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 824c3f8961518..f0a9866f01659 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -199,19 +199,12 @@ int cond_index_bool(void *key, void *datum, void *datap)
return 0;
}
-static int bool_isvalid(struct cond_bool_datum *b)
-{
- if (!(b->state == 0 || b->state == 1))
- return 0;
- return 1;
-}
-
int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
- u32 len;
+ u32 len, val;
int rc;
booldatum = kzalloc_obj(*booldatum);
@@ -223,11 +216,12 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
- booldatum->state = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
rc = -EINVAL;
- if (!bool_isvalid(booldatum))
+ if (!val_is_boolean(val))
goto err;
+ booldatum->state = (int)val;
len = le32_to_cpu(buf[2]);
@@ -241,6 +235,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
return 0;
err:
+ pr_err("SELinux: conditional: failed to read boolean\n");
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
@@ -362,7 +357,8 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 0;
}
- if (expr->boolean > p->p_bools.nprim) {
+ if (expr->expr_type == COND_BOOL &&
+ (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
pr_err("SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 203033cfad672..1d75a8a044df9 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -50,6 +50,7 @@ struct constraint_expr {
u32 op; /* operator */
struct ebitmap names; /* names */
+ /* internally unused, only forwarded via policydb_write() */
struct type_set *type_names;
struct constraint_expr *next; /* next expression */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 3afea36dfcdf0..f0be99481c585 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,13 +638,11 @@ static int sens_index(void *key, void *datum, void *datap)
levdatum = datum;
p = datap;
- if (!levdatum->isalias) {
- if (!levdatum->level.sens ||
- levdatum->level.sens > p->p_levels.nprim)
- return -EINVAL;
+ if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
+ return -EINVAL;
+ if (!levdatum->isalias)
p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
- }
return 0;
}
@@ -657,12 +655,11 @@ static int cat_index(void *key, void *datum, void *datap)
catdatum = datum;
p = datap;
- if (!catdatum->isalias) {
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ if (!catdatum->isalias)
p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
- }
return 0;
}
@@ -1140,6 +1137,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1174,6 +1174,9 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
@@ -1323,7 +1326,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc_obj(*cladatum);
@@ -1336,8 +1339,16 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
- cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
+
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (val > U16_MAX)
+ goto bad;
+ cladatum->value = val;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
@@ -1393,16 +1404,59 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- cladatum->default_user = le32_to_cpu(buf[0]);
- cladatum->default_role = le32_to_cpu(buf[1]);
- cladatum->default_range = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_user = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[1]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_role = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[2]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE_LOW:
+ case DEFAULT_SOURCE_HIGH:
+ case DEFAULT_SOURCE_LOW_HIGH:
+ case DEFAULT_TARGET_LOW:
+ case DEFAULT_TARGET_HIGH:
+ case DEFAULT_TARGET_LOW_HIGH:
+ case DEFAULT_GLBLUB:
+ cladatum->default_range = val;
+ break;
+ default:
+ goto bad;
+ }
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- cladatum->default_type = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_TARGET:
+ case DEFAULT_SOURCE:
+ cladatum->default_type = val;
+ break;
+ default:
+ goto bad;
+ }
}
rc = symtab_insert(s, key, cladatum);
@@ -1412,6 +1466,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1603,7 +1659,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len;
+ u32 len, val;
levdatum = kzalloc_obj(*levdatum);
if (!levdatum)
@@ -1614,7 +1670,11 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- levdatum->isalias = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ levdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1630,6 +1690,8 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1639,7 +1701,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len;
+ u32 len, val;
catdatum = kzalloc_obj(*catdatum);
if (!catdatum)
@@ -1651,7 +1713,11 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- catdatum->isalias = le32_to_cpu(buf[2]);
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ catdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1663,6 +1729,8 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1844,7 +1912,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel;
+ u32 i, nel, val;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1875,7 +1943,11 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rt->target_class = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto out;
+ rt->target_class = val;
} else
rt->target_class = p->process_class;
@@ -1912,6 +1984,8 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
+ if (rc)
+ pr_err("SELinux: invalid range\n");
return rc;
}
@@ -1920,7 +1994,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype;
+ u32 len, stype, otype, val;
__le32 buf[4];
int rc;
@@ -1939,9 +2013,17 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
+ rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
+ if (!policydb_type_isvalid(p, stype))
+ goto out;
key.ttype = le32_to_cpu(buf[1]);
- key.tclass = le32_to_cpu(buf[2]);
+ if (!policydb_type_isvalid(p, key.ttype))
+ goto out;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ key.tclass = val;
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -1997,6 +2079,9 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2005,7 +2090,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i;
+ u32 len, ttype, ndatum, i, val;
u16 tclass;
__le32 buf[3];
int rc;
@@ -2025,8 +2110,15 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
+ rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- tclass = le32_to_cpu(buf[1]);
+ if (!policydb_type_isvalid(p, ttype))
+ goto out;
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ tclass = val;
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2056,6 +2148,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ if (!policydb_type_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2087,6 +2183,9 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2134,7 +2233,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2;
+ u32 i, j, nel, nel2, len, len2, val;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2204,7 +2303,11 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- newc->v.sclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
+ goto out;
+ newc->v.sclass = val;
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2241,6 +2344,9 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2249,7 +2355,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len;
+ u32 j, nel, len, val;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2313,11 +2419,25 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
- c->u.port.protocol = le32_to_cpu(buf[0]);
- c->u.port.low_port = le32_to_cpu(buf[1]);
- c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0],
- p, fp);
+
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U8_MAX)
+ goto out;
+ c->u.port.protocol = val;
+ val = le32_to_cpu(buf[1]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.low_port = val;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.high_port = val;
+ if (c->u.port.low_port == 0 ||
+ c->u.port.low_port > c->u.port.high_port)
+ goto out;
+
+ rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto out;
break;
@@ -2435,6 +2555,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2449,7 +2571,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm, val;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2641,7 +2763,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rtk->tclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto bad;
+ rtk->tclass = val;
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index a49275d1168d8..20b834581106a 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
+ u32 bounds; /* boundary of role, 0 for none */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -110,7 +110,8 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
+ u32 bounds; /* boundary of type, 0 for none */
+ /* internally unused, only forwarded via policydb_write() */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -118,7 +119,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
+ u32 bounds; /* bounds of user, 0 for none */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs (can be 0 for wildcard) */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -388,9 +389,23 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+static inline bool val_is_boolean(u32 value)
+{
+ return value == 0 || value == 1;
+}
+
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
+#define pr_warn_once_policyload(policy, fmt, ...) \
+ do { \
+ static const void *prev_policy__; \
+ if (prev_policy__ != policy) { \
+ pr_warn(fmt, ##__VA_ARGS__); \
+ prev_policy__ = policy; \
+ } \
+ } while (0)
+
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index b84d99c3fed98..1d117d9dfc346 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
- BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[32];
+ char *permission_names[SEL_VEC_MAX];
int index;
u32 length;
bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < 32; index++) {
+ for (index = 0; index < SEL_VEC_MAX; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 7.1.y 3/5] selinux: reject a permission value exceeding the class permission count
2026-08-27 5:17 ` Sasha Levin
2026-08-27 5:47 ` [PATCH 7.1.y 1/5] selinux: use u16 for security classes Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 2/5] selinux: more strict policy parsing Wentao Guan
@ 2026-08-27 5:47 ` Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 4/5] selinux: reject a class permission count below its inherited common Wentao Guan
` (11 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 5:47 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
perm_read() bounds a permission value by SEL_VEC_MAX but never by the
nprim of the owning class or common, which is taken verbatim from the
policy image. security_get_permissions() then writes perms[value - 1]
into an nprim-sized kcalloc() array, so a class declaring fewer
permissions than its largest permission value drives an out-of-bounds
heap write. The top-level symbol tables are validated this way; the
nested per-class permission table is not.
Reject a permission whose value exceeds nprim, which is already set when
perm_read() runs. Well-formed policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: tweak comment for line length]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit d14b5d0e97fccd27974fedc03b903408872907fd)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index f0be99481c585..73fd6f06ec98a 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1140,6 +1140,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 7.1.y 4/5] selinux: reject a class permission count below its inherited common
2026-08-27 5:17 ` Sasha Levin
` (2 preceding siblings ...)
2026-08-27 5:47 ` [PATCH 7.1.y 3/5] selinux: reject a permission value exceeding the class permission count Wentao Guan
@ 2026-08-27 5:47 ` Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 5/5] selinux: require a class's permission values to cover its permission count Wentao Guan
` (10 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 5:47 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() maps an inherited common's permissions into
an array sized by the class's own permissions.nprim, but class_read()
takes that nprim verbatim from the policy image and never checks that it
covers the common. A class that inherits a common of N permissions while
declaring a smaller nprim is accepted, and on load the common's
permissions are written past the class-sized array -- an out-of-bounds
heap write.
Reject a class whose permission count is below its inherited common's.
Well-formed policies, where the class count already includes the
inherited permissions, are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 9a82dcd98b6e6e11cfd162410967951f12152528)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 73fd6f06ec98a..6f085f4307bf2 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1377,6 +1377,18 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
+
+ /*
+ * security_get_permissions() maps the common's permissions
+ * into an array sized by this class's nprim, so a class must
+ * declare at least as many as the common it inherits.
+ */
+ if (cladatum->permissions.nprim <
+ cladatum->comdatum->permissions.nprim) {
+ pr_err("SELinux: class %s has fewer permissions than common %s\n",
+ key, cladatum->comkey);
+ goto bad;
+ }
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, &cladatum->permissions, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 7.1.y 5/5] selinux: require a class's permission values to cover its permission count
2026-08-27 5:17 ` Sasha Levin
` (3 preceding siblings ...)
2026-08-27 5:47 ` [PATCH 7.1.y 4/5] selinux: reject a class permission count below its inherited common Wentao Guan
@ 2026-08-27 5:47 ` Wentao Guan
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
` (9 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 5:47 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit b98a8ac50775540f3804397ed08f61ef9910bcab)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 5 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 6f085f4307bf2..0867c0af1d6a7 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1119,7 +1119,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1143,6 +1154,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1163,7 +1178,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc_obj(*comdatum);
@@ -1185,17 +1200,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1329,7 +1355,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc_obj(*cladatum);
@@ -1357,6 +1383,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1391,11 +1421,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 5:17 ` Sasha Levin
` (4 preceding siblings ...)
2026-08-27 5:47 ` [PATCH 7.1.y 5/5] selinux: require a class's permission values to cover its permission count Wentao Guan
@ 2026-08-27 8:37 ` Wentao Guan
2026-08-27 8:59 ` Greg KH
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 02/10] Revert "selinux: more strict policy parsing" Wentao Guan
` (8 subsequent siblings)
14 siblings, 1 reply; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:37 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
This reverts commit b6336a9b52baa35136f024ab30929fdda83b3160.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 51 ++++------------------------------
1 file changed, 5 insertions(+), 46 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ef7f767a682b3..fac3b61bfa202 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1139,18 +1139,7 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-/*
- * Bitmap of the permission values a symtab has claimed. Values are 1-based
- * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
- * fits in a u32 and the callers reject an nprim past that width.
- */
-static u32 perm_claimed_mask(u32 nprim)
-{
- return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
-}
-
-static int perm_read(struct policydb *p, struct symtab *s,
- struct policy_file *fp, u32 *claimed)
+static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1182,10 +1171,6 @@ static int perm_read(struct policydb *p, struct symtab *s,
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
- /* two permissions cannot share one slot of that array */
- if (*claimed & (1U << (perdatum->value - 1)))
- goto bad;
- *claimed |= 1U << (perdatum->value - 1);
return 0;
bad:
@@ -1198,7 +1183,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel, claimed = 0;
+ u32 i, len, nel;
int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
@@ -1220,28 +1205,17 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
- /* no permission value can reach a slot past SEL_VEC_MAX */
- rc = -EINVAL;
- if (comdatum->permissions.nprim > SEL_VEC_MAX)
- goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp, &claimed);
+ rc = perm_read(p, &comdatum->permissions, fp);
if (rc)
goto bad;
}
- rc = -EINVAL;
- if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
- pr_err("SELinux: common %s does not define every permission it declares\n",
- key);
- goto bad;
- }
-
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1377,7 +1351,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1405,10 +1379,6 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
- /* no permission value can reach a slot past SEL_VEC_MAX */
- rc = -EINVAL;
- if (cladatum->permissions.nprim > SEL_VEC_MAX)
- goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1443,22 +1413,11 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp, &claimed);
+ rc = perm_read(p, &cladatum->permissions, fp);
if (rc)
goto bad;
}
- /* the class's own permissions must claim the slots the common leaves */
- if (cladatum->comdatum)
- inherited = cladatum->comdatum->permissions.nprim;
- rc = -EINVAL;
- if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
- ~perm_claimed_mask(inherited))) {
- pr_err("SELinux: class %s does not define every permission it declares\n",
- key);
- goto bad;
- }
-
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.47-rc1 02/10] Revert "selinux: more strict policy parsing"
2026-08-27 5:17 ` Sasha Levin
` (5 preceding siblings ...)
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
@ 2026-08-27 8:37 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes" Wentao Guan
` (7 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:37 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
This reverts commit f750d11bcf18ed4774cd1f160ec7f7be9da2d52b.
NOTE the patch revert commit which in stable-rc/linux-6.18.y.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/include/security.h | 1 -
security/selinux/ss/avtab.c | 35 +----
security/selinux/ss/avtab.h | 13 --
security/selinux/ss/conditional.c | 18 ++-
security/selinux/ss/constraint.h | 1 -
security/selinux/ss/policydb.c | 196 +++++-----------------------
security/selinux/ss/policydb.h | 23 +---
security/selinux/ss/services.c | 6 +-
8 files changed, 60 insertions(+), 233 deletions(-)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index f698ff7962282..30e3fa962f6fa 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -225,7 +225,6 @@ int security_read_policy(void **data, size_t *len);
int security_read_state_kernel(void **data, size_t *len);
int security_policycap_supported(unsigned int req_cap);
-/* Maximum supported number of permissions per class */
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 33556922f15ed..c2c31521cace1 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -349,7 +349,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int rc;
- unsigned int vers = pol->policyvers;
+ unsigned int set, vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -360,12 +360,9 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
- /* Read five or more items: source type, target type,
- * target class, AV type, and at least one datum.
- */
items2 = le32_to_cpu(buf32[0]);
- if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
- pr_err("SELinux: avtab: invalid item count\n");
+ if (items2 > ARRAY_SIZE(buf32)) {
+ pr_err("SELinux: avtab: entry overflow\n");
return -EINVAL;
}
rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -394,13 +391,6 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- if (!policydb_type_isvalid(pol, key.source_type) ||
- !policydb_type_isvalid(pol, key.target_type) ||
- !policydb_class_isvalid(pol, key.target_class)) {
- pr_err("SELinux: avtab: invalid type or class\n");
- return -EINVAL;
- }
-
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
@@ -419,11 +409,6 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
- if (items >= items2) {
- pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
- items + 1, items2);
- return -EINVAL;
- }
key.specified = spec_order[i] | enabled;
datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
@@ -459,13 +444,9 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
- pr_err("SELinux: avtab: not exactly one specifier\n");
- return -EINVAL;
- }
-
- if (key.specified & ~AVTAB_SPECIFIER_MASK) {
- pr_err("SELinux: avtab: invalid specifier\n");
+ set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
+ if (!set || set > 1) {
+ pr_err("SELinux: avtab: more than one specifier\n");
return -EINVAL;
}
@@ -490,10 +471,6 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
- if (!avtab_is_valid_xperm_specified(xperms.specified))
- pr_warn_once_policyload(pol,
- "SELinux: avtab: unsupported xperm specifier %#x\n",
- xperms.specified);
rc = next_entry(&xperms.driver, fp, sizeof(u8));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 1de4cce288a76..850b3453f2598 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -44,7 +44,6 @@ struct avtab_key {
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
-#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
u16 specified; /* what field is specified */
};
@@ -69,18 +68,6 @@ struct avtab_extended_perms {
struct extended_perms_data perms;
};
-static inline bool avtab_is_valid_xperm_specified(u8 specified)
-{
- switch (specified) {
- case AVTAB_XPERMS_IOCTLFUNCTION:
- case AVTAB_XPERMS_IOCTLDRIVER:
- case AVTAB_XPERMS_NLMSG:
- return true;
- default:
- return false;
- }
-}
-
struct avtab_datum {
union {
u32 data; /* access vector or type value */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index db30462ed6a32..1bebfcb9c6a12 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -199,12 +199,19 @@ int cond_index_bool(void *key, void *datum, void *datap)
return 0;
}
+static int bool_isvalid(struct cond_bool_datum *b)
+{
+ if (!(b->state == 0 || b->state == 1))
+ return 0;
+ return 1;
+}
+
int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
- u32 len, val;
+ u32 len;
int rc;
booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL);
@@ -216,12 +223,11 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
- val = le32_to_cpu(buf[1]);
+ booldatum->state = le32_to_cpu(buf[1]);
rc = -EINVAL;
- if (!val_is_boolean(val))
+ if (!bool_isvalid(booldatum))
goto err;
- booldatum->state = (int)val;
len = le32_to_cpu(buf[2]);
@@ -235,7 +241,6 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
return 0;
err:
- pr_err("SELinux: conditional: failed to read boolean\n");
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
@@ -357,8 +362,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 0;
}
- if (expr->expr_type == COND_BOOL &&
- (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
+ if (expr->boolean > p->p_bools.nprim) {
pr_err("SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 1d75a8a044df9..203033cfad672 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -50,7 +50,6 @@ struct constraint_expr {
u32 op; /* operator */
struct ebitmap names; /* names */
- /* internally unused, only forwarded via policydb_write() */
struct type_set *type_names;
struct constraint_expr *next; /* next expression */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fac3b61bfa202..37e0735b0dc8d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,11 +638,13 @@ static int sens_index(void *key, void *datum, void *datap)
levdatum = datum;
p = datap;
- if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
- return -EINVAL;
+ if (!levdatum->isalias) {
+ if (!levdatum->level.sens ||
+ levdatum->level.sens > p->p_levels.nprim)
+ return -EINVAL;
- if (!levdatum->isalias)
p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
+ }
return 0;
}
@@ -655,11 +657,12 @@ static int cat_index(void *key, void *datum, void *datap)
catdatum = datum;
p = datap;
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
+ if (!catdatum->isalias) {
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
- if (!catdatum->isalias)
p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
+ }
return 0;
}
@@ -1157,9 +1160,6 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
- rc = -EINVAL;
- if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
- goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1197,9 +1197,6 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
- rc = -EINVAL;
- if (nel > SEL_VEC_MAX)
- goto bad;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
@@ -1351,7 +1348,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1364,16 +1361,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
+ cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
- rc = -EINVAL;
- if (nel > SEL_VEC_MAX)
- goto bad;
-
- val = le32_to_cpu(buf[2]);
- rc = -EINVAL;
- if (val > U16_MAX)
- goto bad;
- cladatum->value = val;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
@@ -1441,59 +1430,16 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- switch (val) {
- case 0:
- case DEFAULT_SOURCE:
- case DEFAULT_TARGET:
- cladatum->default_user = val;
- break;
- default:
- goto bad;
- }
- val = le32_to_cpu(buf[1]);
- switch (val) {
- case 0:
- case DEFAULT_SOURCE:
- case DEFAULT_TARGET:
- cladatum->default_role = val;
- break;
- default:
- goto bad;
- }
- val = le32_to_cpu(buf[2]);
- switch (val) {
- case 0:
- case DEFAULT_SOURCE_LOW:
- case DEFAULT_SOURCE_HIGH:
- case DEFAULT_SOURCE_LOW_HIGH:
- case DEFAULT_TARGET_LOW:
- case DEFAULT_TARGET_HIGH:
- case DEFAULT_TARGET_LOW_HIGH:
- case DEFAULT_GLBLUB:
- cladatum->default_range = val;
- break;
- default:
- goto bad;
- }
+ cladatum->default_user = le32_to_cpu(buf[0]);
+ cladatum->default_role = le32_to_cpu(buf[1]);
+ cladatum->default_range = le32_to_cpu(buf[2]);
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- switch (val) {
- case 0:
- case DEFAULT_TARGET:
- case DEFAULT_SOURCE:
- cladatum->default_type = val;
- break;
- default:
- goto bad;
- }
+ cladatum->default_type = le32_to_cpu(buf[0]);
}
rc = symtab_insert(s, key, cladatum);
@@ -1503,8 +1449,6 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
- if (rc)
- pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1696,7 +1640,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len, val;
+ u32 len;
levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
@@ -1707,11 +1651,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- val = le32_to_cpu(buf[1]);
- rc = -EINVAL;
- if (!val_is_boolean(val))
- goto bad;
- levdatum->isalias = val;
+ levdatum->isalias = le32_to_cpu(buf[1]);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1727,8 +1667,6 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
- if (rc)
- pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1738,7 +1676,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len, val;
+ u32 len;
catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
@@ -1750,11 +1688,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- val = le32_to_cpu(buf[2]);
- rc = -EINVAL;
- if (!val_is_boolean(val))
- goto bad;
- catdatum->isalias = val;
+ catdatum->isalias = le32_to_cpu(buf[2]);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1766,8 +1700,6 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
- if (rc)
- pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1949,7 +1881,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel, val;
+ u32 i, nel;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1980,11 +1912,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- if (val > U16_MAX)
- goto out;
- rt->target_class = val;
+ rt->target_class = le32_to_cpu(buf[0]);
} else
rt->target_class = p->process_class;
@@ -2021,8 +1949,6 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
- if (rc)
- pr_err("SELinux: invalid range\n");
return rc;
}
@@ -2031,7 +1957,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype, val;
+ u32 len, stype, otype;
__le32 buf[4];
int rc;
@@ -2050,17 +1976,9 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
- rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
- if (!policydb_type_isvalid(p, stype))
- goto out;
key.ttype = le32_to_cpu(buf[1]);
- if (!policydb_type_isvalid(p, key.ttype))
- goto out;
- val = le32_to_cpu(buf[2]);
- if (val > U16_MAX || !policydb_class_isvalid(p, val))
- goto out;
- key.tclass = val;
+ key.tclass = le32_to_cpu(buf[2]);
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -2116,9 +2034,6 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
kfree(ft);
kfree(name);
kfree(datum);
-
- if (rc)
- pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2127,7 +2042,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i, val;
+ u32 len, ttype, ndatum, i;
u16 tclass;
__le32 buf[3];
int rc;
@@ -2147,15 +2062,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
- rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- if (!policydb_type_isvalid(p, ttype))
- goto out;
- val = le32_to_cpu(buf[1]);
- rc = -EINVAL;
- if (val > U16_MAX || !policydb_class_isvalid(p, val))
- goto out;
- tclass = val;
+ tclass = le32_to_cpu(buf[1]);
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2185,10 +2093,6 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
- rc = -EINVAL;
- if (!policydb_type_isvalid(p, datum->otype))
- goto out;
-
dst = &datum->next;
}
@@ -2220,9 +2124,6 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
-
- if (rc)
- pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2270,7 +2171,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2, val;
+ u32 i, j, nel, nel2, len, len2;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2340,11 +2241,7 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
- goto out;
- newc->v.sclass = val;
+ newc->v.sclass = le32_to_cpu(buf[0]);
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2381,9 +2278,6 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
}
ocontext_destroy(newc, OCON_FSUSE);
- if (rc)
- pr_err("SELinux: invalid genfs\n");
-
return rc;
}
@@ -2392,7 +2286,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len, val;
+ u32 j, nel, len;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2456,25 +2350,11 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
-
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- if (val > U8_MAX)
- goto out;
- c->u.port.protocol = val;
- val = le32_to_cpu(buf[1]);
- if (val > U16_MAX)
- goto out;
- c->u.port.low_port = val;
- val = le32_to_cpu(buf[2]);
- if (val > U16_MAX)
- goto out;
- c->u.port.high_port = val;
- if (c->u.port.low_port == 0 ||
- c->u.port.low_port > c->u.port.high_port)
- goto out;
-
- rc = context_read_and_validate(&c->context[0], p, fp);
+ c->u.port.protocol = le32_to_cpu(buf[0]);
+ c->u.port.low_port = le32_to_cpu(buf[1]);
+ c->u.port.high_port = le32_to_cpu(buf[2]);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2592,8 +2472,6 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
- if (rc)
- pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2608,7 +2486,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm, val;
+ u32 i, j, len, nprim, nel, perm;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2800,11 +2678,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rc = -EINVAL;
- val = le32_to_cpu(buf[0]);
- if (val > U16_MAX)
- goto bad;
- rtk->tclass = val;
+ rtk->tclass = le32_to_cpu(buf[0]);
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 20b834581106a..a49275d1168d8 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role, 0 for none */
+ u32 bounds; /* boundary of role */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -110,8 +110,7 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type, 0 for none */
- /* internally unused, only forwarded via policydb_write() */
+ u32 bounds; /* boundary of type */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -119,7 +118,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user, 0 for none */
+ u32 bounds; /* bounds of user */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -196,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs (can be 0 for wildcard) */
+ u16 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -389,23 +388,9 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
-static inline bool val_is_boolean(u32 value)
-{
- return value == 0 || value == 1;
-}
-
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
-#define pr_warn_once_policyload(policy, fmt, ...) \
- do { \
- static const void *prev_policy__; \
- if (prev_policy__ != policy) { \
- pr_warn(fmt, ##__VA_ARGS__); \
- prev_policy__ = policy; \
- } \
- } while (0)
-
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index fb150025359b0..cbc101b095a30 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,6 +446,8 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
+ BUG_ON(pdatum->value < 1 || pdatum->value > 32);
+
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -464,7 +466,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[SEL_VEC_MAX];
+ char *permission_names[32];
int index;
u32 length;
bool need_comma = false;
@@ -505,7 +507,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < SEL_VEC_MAX; index++) {
+ for (index = 0; index < 32; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes"
2026-08-27 5:17 ` Sasha Levin
` (6 preceding siblings ...)
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 02/10] Revert "selinux: more strict policy parsing" Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:58 ` Greg KH
2026-08-27 8:38 ` [PATCH 6.18.y 04/10] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
` (6 subsequent siblings)
14 siblings, 1 reply; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
This reverts commit 3683f35c14816f827a80e217072018eb6c37422e.
NOTE the commit revert the commit in stable-rc/linux-6.18.y
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 5 ++---
security/selinux/ss/policydb.h | 10 +++++-----
security/selinux/ss/services.c | 2 +-
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 37e0735b0dc8d..fdb8a62da04ad 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -951,7 +951,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, u16 class)
+int policydb_class_isvalid(struct policydb *p, unsigned int class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -2042,8 +2042,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i;
- u16 tclass;
+ u32 len, ttype, tclass, ndatum, i;
__le32 buf[3];
int rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index a49275d1168d8..89a180b1742fb 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u16 value; /* class value */
+ u32 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u16 tclass; /* process class, or new object class */
+ u32 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u16 target_class;
+ u32 target_class;
};
/* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs */
+ u32 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, u16 class);
+extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index cbc101b095a30..82608b34260fd 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3290,7 +3290,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u16 value = datum->value - 1;
+ u32 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 04/10] Revert "selinux: reject a permission value exceeding the class permission count"
2026-08-27 5:17 ` Sasha Levin
` (7 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes" Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 05/10] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
` (5 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
This reverts commit 62be2361da29505d3183da854f1c98d2e208e852.
This pick was applied without its upstream prerequisites (commit
fa79a596848f "selinux: use u16 for security classes" and commit
18fa21f10d00 "selinux: more strict policy parsing") and is therefore
incomplete. Drop it here; it is re-applied later in this series in the
correct upstream order.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fdb8a62da04ad..69c19ef4b8817 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1168,9 +1168,6 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
- /* indexes an nprim-sized array in security_get_permissions() */
- if (perdatum->value > s->nprim)
- goto bad;
return 0;
bad:
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 05/10] Revert "selinux: reject a class permission count below its inherited common"
2026-08-27 5:17 ` Sasha Levin
` (8 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 04/10] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 06/10] selinux: use u16 for security classes Wentao Guan
` (4 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
This reverts commit 9f721b5d9e1d956ffe965bc09375989eb51881fd.
This pick was applied without its upstream prerequisites (commit
fa79a596848f "selinux: use u16 for security classes" and commit
18fa21f10d00 "selinux: more strict policy parsing") and is therefore
incomplete. Drop it here; it is re-applied later in this series in the
correct upstream order.
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 69c19ef4b8817..48ee87070ab0d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1385,18 +1385,6 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
-
- /*
- * security_get_permissions() maps the common's permissions
- * into an array sized by this class's nprim, so a class must
- * declare at least as many as the common it inherits.
- */
- if (cladatum->permissions.nprim <
- cladatum->comdatum->permissions.nprim) {
- pr_err("SELinux: class %s has fewer permissions than common %s\n",
- key, cladatum->comkey);
- goto bad;
- }
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, &cladatum->permissions, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 06/10] selinux: use u16 for security classes
2026-08-27 5:17 ` Sasha Levin
` (9 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 05/10] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 07/10] selinux: more strict policy parsing Wentao Guan
` (3 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal
Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche
From: Christian Göttsche <cgzones@googlemail.com>
Security class identifiers are limited to 2^16, thus use the appropriate
type u16 consistently.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit fa79a596848fe38c55ccab8832ac35dac07fb00c)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 5 +++--
security/selinux/ss/policydb.h | 10 +++++-----
security/selinux/ss/services.c | 2 +-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 48ee87070ab0d..fdee7e22462aa 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -951,7 +951,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
return 0;
@@ -2027,7 +2027,8 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, tclass, ndatum, i;
+ u32 len, ttype, ndatum, i;
+ u16 tclass;
__le32 buf[3];
int rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742fb..a49275d1168d8 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -48,7 +48,7 @@ struct common_datum {
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
+ u16 value; /* class value */
char *comkey; /* common name */
struct common_datum *comdatum; /* common datum */
struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
struct role_trans_key {
u32 role; /* current role */
u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u16 tclass; /* process class, or new object class */
};
struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 82608b34260fd..cbc101b095a30 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3290,7 +3290,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- u32 value = datum->value - 1;
+ u16 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 07/10] selinux: more strict policy parsing
2026-08-27 5:17 ` Sasha Levin
` (10 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 06/10] selinux: use u16 for security classes Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 08/10] selinux: reject a permission value exceeding the class permission count Wentao Guan
` (2 subsequent siblings)
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal
Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work,
Christian Göttsche
From: Christian Göttsche <cgzones@googlemail.com>
Be more strict during parsing of policies and reject invalid values.
Add some error messages in the case of policy parse failures, to
enhance debugging, either on a malformed policy or a too strict check.
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: fixed checkpatch.pl warnings, style problems]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 18fa21f10d008a0fc22565109c7d38f304295912)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/include/security.h | 1 +
security/selinux/ss/avtab.c | 35 ++++-
security/selinux/ss/avtab.h | 13 ++
security/selinux/ss/conditional.c | 18 +--
security/selinux/ss/constraint.h | 1 +
security/selinux/ss/policydb.c | 196 +++++++++++++++++++++++-----
security/selinux/ss/policydb.h | 23 +++-
security/selinux/ss/services.c | 6 +-
8 files changed, 233 insertions(+), 60 deletions(-)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 30e3fa962f6fa..f698ff7962282 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -225,6 +225,7 @@ int security_read_policy(void **data, size_t *len);
int security_read_state_kernel(void **data, size_t *len);
int security_policycap_supported(unsigned int req_cap);
+/* Maximum supported number of permissions per class */
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace1..33556922f15ed 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -349,7 +349,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int rc;
- unsigned int set, vers = pol->policyvers;
+ unsigned int vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -360,9 +360,12 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ /* Read five or more items: source type, target type,
+ * target class, AV type, and at least one datum.
+ */
items2 = le32_to_cpu(buf32[0]);
- if (items2 > ARRAY_SIZE(buf32)) {
- pr_err("SELinux: avtab: entry overflow\n");
+ if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
+ pr_err("SELinux: avtab: invalid item count\n");
return -EINVAL;
}
rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -391,6 +394,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ pr_err("SELinux: avtab: invalid type or class\n");
+ return -EINVAL;
+ }
+
val = le32_to_cpu(buf32[items++]);
enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
@@ -409,6 +419,11 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
+ if (items >= items2) {
+ pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
+ items + 1, items2);
+ return -EINVAL;
+ }
key.specified = spec_order[i] | enabled;
datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
@@ -444,9 +459,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
return -EINVAL;
}
- set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
- if (!set || set > 1) {
- pr_err("SELinux: avtab: more than one specifier\n");
+ if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
+ pr_err("SELinux: avtab: not exactly one specifier\n");
+ return -EINVAL;
+ }
+
+ if (key.specified & ~AVTAB_SPECIFIER_MASK) {
+ pr_err("SELinux: avtab: invalid specifier\n");
return -EINVAL;
}
@@ -471,6 +490,10 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
+ if (!avtab_is_valid_xperm_specified(xperms.specified))
+ pr_warn_once_policyload(pol,
+ "SELinux: avtab: unsupported xperm specifier %#x\n",
+ xperms.specified);
rc = next_entry(&xperms.driver, fp, sizeof(u8));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 850b3453f2598..1de4cce288a76 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -44,6 +44,7 @@ struct avtab_key {
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
u16 specified; /* what field is specified */
};
@@ -68,6 +69,18 @@ struct avtab_extended_perms {
struct extended_perms_data perms;
};
+static inline bool avtab_is_valid_xperm_specified(u8 specified)
+{
+ switch (specified) {
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ case AVTAB_XPERMS_NLMSG:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct avtab_datum {
union {
u32 data; /* access vector or type value */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 1bebfcb9c6a12..db30462ed6a32 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -199,19 +199,12 @@ int cond_index_bool(void *key, void *datum, void *datap)
return 0;
}
-static int bool_isvalid(struct cond_bool_datum *b)
-{
- if (!(b->state == 0 || b->state == 1))
- return 0;
- return 1;
-}
-
int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
- u32 len;
+ u32 len, val;
int rc;
booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL);
@@ -223,11 +216,12 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
- booldatum->state = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
rc = -EINVAL;
- if (!bool_isvalid(booldatum))
+ if (!val_is_boolean(val))
goto err;
+ booldatum->state = (int)val;
len = le32_to_cpu(buf[2]);
@@ -241,6 +235,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
return 0;
err:
+ pr_err("SELinux: conditional: failed to read boolean\n");
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
@@ -362,7 +357,8 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 0;
}
- if (expr->boolean > p->p_bools.nprim) {
+ if (expr->expr_type == COND_BOOL &&
+ (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
pr_err("SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 203033cfad672..1d75a8a044df9 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -50,6 +50,7 @@ struct constraint_expr {
u32 op; /* operator */
struct ebitmap names; /* names */
+ /* internally unused, only forwarded via policydb_write() */
struct type_set *type_names;
struct constraint_expr *next; /* next expression */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index fdee7e22462aa..8dc77e347a77f 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -638,13 +638,11 @@ static int sens_index(void *key, void *datum, void *datap)
levdatum = datum;
p = datap;
- if (!levdatum->isalias) {
- if (!levdatum->level.sens ||
- levdatum->level.sens > p->p_levels.nprim)
- return -EINVAL;
+ if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
+ return -EINVAL;
+ if (!levdatum->isalias)
p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
- }
return 0;
}
@@ -657,12 +655,11 @@ static int cat_index(void *key, void *datum, void *datap)
catdatum = datum;
p = datap;
- if (!catdatum->isalias) {
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ if (!catdatum->isalias)
p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
- }
return 0;
}
@@ -1160,6 +1157,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1194,6 +1194,9 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
@@ -1345,7 +1348,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel;
+ u32 i, len, len2, ncons, nel, val;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1358,8 +1361,16 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
- cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
+ rc = -EINVAL;
+ if (nel > SEL_VEC_MAX)
+ goto bad;
+
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (val > U16_MAX)
+ goto bad;
+ cladatum->value = val;
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
@@ -1415,16 +1426,59 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
- cladatum->default_user = le32_to_cpu(buf[0]);
- cladatum->default_role = le32_to_cpu(buf[1]);
- cladatum->default_range = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_user = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[1]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE:
+ case DEFAULT_TARGET:
+ cladatum->default_role = val;
+ break;
+ default:
+ goto bad;
+ }
+ val = le32_to_cpu(buf[2]);
+ switch (val) {
+ case 0:
+ case DEFAULT_SOURCE_LOW:
+ case DEFAULT_SOURCE_HIGH:
+ case DEFAULT_SOURCE_LOW_HIGH:
+ case DEFAULT_TARGET_LOW:
+ case DEFAULT_TARGET_HIGH:
+ case DEFAULT_TARGET_LOW_HIGH:
+ case DEFAULT_GLBLUB:
+ cladatum->default_range = val;
+ break;
+ default:
+ goto bad;
+ }
}
if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
rc = next_entry(buf, fp, sizeof(u32) * 1);
if (rc)
goto bad;
- cladatum->default_type = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ switch (val) {
+ case 0:
+ case DEFAULT_TARGET:
+ case DEFAULT_SOURCE:
+ cladatum->default_type = val;
+ break;
+ default:
+ goto bad;
+ }
}
rc = symtab_insert(s, key, cladatum);
@@ -1434,6 +1488,8 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
return 0;
bad:
cls_destroy(key, cladatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid class\n");
return rc;
}
@@ -1625,7 +1681,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
struct level_datum *levdatum;
int rc;
__le32 buf[2];
- u32 len;
+ u32 len, val;
levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
@@ -1636,7 +1692,11 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
goto bad;
len = le32_to_cpu(buf[0]);
- levdatum->isalias = le32_to_cpu(buf[1]);
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ levdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1652,6 +1712,8 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
return 0;
bad:
sens_destroy(key, levdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid sensitivity\n");
return rc;
}
@@ -1661,7 +1723,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
struct cat_datum *catdatum;
int rc;
__le32 buf[3];
- u32 len;
+ u32 len, val;
catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
@@ -1673,7 +1735,11 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
- catdatum->isalias = le32_to_cpu(buf[2]);
+ val = le32_to_cpu(buf[2]);
+ rc = -EINVAL;
+ if (!val_is_boolean(val))
+ goto bad;
+ catdatum->isalias = val;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1685,6 +1751,8 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
return 0;
bad:
cat_destroy(key, catdatum, NULL);
+ if (rc)
+ pr_err("SELinux: invalid category\n");
return rc;
}
@@ -1866,7 +1934,7 @@ static int range_read(struct policydb *p, struct policy_file *fp)
struct mls_range *r = NULL;
int rc;
__le32 buf[2];
- u32 i, nel;
+ u32 i, nel, val;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -1897,7 +1965,11 @@ static int range_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
- rt->target_class = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto out;
+ rt->target_class = val;
} else
rt->target_class = p->process_class;
@@ -1934,6 +2006,8 @@ static int range_read(struct policydb *p, struct policy_file *fp)
out:
kfree(rt);
kfree(r);
+ if (rc)
+ pr_err("SELinux: invalid range\n");
return rc;
}
@@ -1942,7 +2016,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
- u32 len, stype, otype;
+ u32 len, stype, otype, val;
__le32 buf[4];
int rc;
@@ -1961,9 +2035,17 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
if (rc)
goto out;
+ rc = -EINVAL;
stype = le32_to_cpu(buf[0]);
+ if (!policydb_type_isvalid(p, stype))
+ goto out;
key.ttype = le32_to_cpu(buf[1]);
- key.tclass = le32_to_cpu(buf[2]);
+ if (!policydb_type_isvalid(p, key.ttype))
+ goto out;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ key.tclass = val;
key.name = name;
otype = le32_to_cpu(buf[3]);
@@ -2019,6 +2101,9 @@ static int filename_trans_read_helper_compat(struct policydb *p, struct policy_f
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2027,7 +2112,7 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
char *name = NULL;
- u32 len, ttype, ndatum, i;
+ u32 len, ttype, ndatum, i, val;
u16 tclass;
__le32 buf[3];
int rc;
@@ -2047,8 +2132,15 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
if (rc)
goto out;
+ rc = -EINVAL;
ttype = le32_to_cpu(buf[0]);
- tclass = le32_to_cpu(buf[1]);
+ if (!policydb_type_isvalid(p, ttype))
+ goto out;
+ val = le32_to_cpu(buf[1]);
+ rc = -EINVAL;
+ if (val > U16_MAX || !policydb_class_isvalid(p, val))
+ goto out;
+ tclass = val;
ndatum = le32_to_cpu(buf[2]);
if (ndatum == 0) {
@@ -2078,6 +2170,10 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
datum->otype = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ if (!policydb_type_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2109,6 +2205,9 @@ static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2156,7 +2255,7 @@ static int filename_trans_read(struct policydb *p, struct policy_file *fp)
static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
- u32 i, j, nel, nel2, len, len2;
+ u32 i, j, nel, nel2, len, len2, val;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2226,7 +2325,11 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto out;
- newc->v.sclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
+ goto out;
+ newc->v.sclass = val;
rc = context_read_and_validate(&newc->context[0], p,
fp);
if (rc)
@@ -2263,6 +2366,9 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2271,7 +2377,7 @@ static int ocontext_read(struct policydb *p,
{
int rc;
unsigned int i;
- u32 j, nel, len;
+ u32 j, nel, len, val;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2335,11 +2441,25 @@ static int ocontext_read(struct policydb *p,
rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
- c->u.port.protocol = le32_to_cpu(buf[0]);
- c->u.port.low_port = le32_to_cpu(buf[1]);
- c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0],
- p, fp);
+
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U8_MAX)
+ goto out;
+ c->u.port.protocol = val;
+ val = le32_to_cpu(buf[1]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.low_port = val;
+ val = le32_to_cpu(buf[2]);
+ if (val > U16_MAX)
+ goto out;
+ c->u.port.high_port = val;
+ if (c->u.port.low_port == 0 ||
+ c->u.port.low_port > c->u.port.high_port)
+ goto out;
+
+ rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto out;
break;
@@ -2457,6 +2577,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2471,7 +2593,7 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
struct role_trans_datum *rtd = NULL;
int rc;
__le32 buf[4];
- u32 i, j, len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm, val;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -2663,7 +2785,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
- rtk->tclass = le32_to_cpu(buf[0]);
+ rc = -EINVAL;
+ val = le32_to_cpu(buf[0]);
+ if (val > U16_MAX)
+ goto bad;
+ rtk->tclass = val;
} else
rtk->tclass = p->process_class;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index a49275d1168d8..20b834581106a 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -74,7 +74,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
+ u32 bounds; /* boundary of role, 0 for none */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -110,7 +110,8 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
+ u32 bounds; /* boundary of type, 0 for none */
+ /* internally unused, only forwarded via policydb_write() */
unsigned char primary; /* primary name? */
unsigned char attribute; /* attribute ?*/
};
@@ -118,7 +119,7 @@ struct type_datum {
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
+ u32 bounds; /* bounds of user, 0 for none */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u16 sclass; /* security class for genfs */
+ u16 sclass; /* security class for genfs (can be 0 for wildcard) */
u32 behavior; /* labeling behavior for fs_use */
} v;
struct context context[2]; /* security context(s) */
@@ -388,9 +389,23 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+static inline bool val_is_boolean(u32 value)
+{
+ return value == 0 || value == 1;
+}
+
extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
+#define pr_warn_once_policyload(policy, fmt, ...) \
+ do { \
+ static const void *prev_policy__; \
+ if (prev_policy__ != policy) { \
+ pr_warn(fmt, ##__VA_ARGS__); \
+ prev_policy__ = policy; \
+ } \
+ } while (0)
+
#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index cbc101b095a30..fb150025359b0 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
struct perm_datum *pdatum = d;
char **permission_names = args;
- BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
permission_names[pdatum->value - 1] = (char *)k;
return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(struct policydb *policydb,
char *tclass_name;
char *scontext_name = NULL;
char *tcontext_name = NULL;
- char *permission_names[32];
+ char *permission_names[SEL_VEC_MAX];
int index;
u32 length;
bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(struct policydb *policydb,
"scontext=%s tcontext=%s tclass=%s perms=",
reason, scontext_name, tcontext_name, tclass_name);
- for (index = 0; index < 32; index++) {
+ for (index = 0; index < SEL_VEC_MAX; index++) {
u32 mask = (1 << index);
if ((mask & permissions) == 0)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 08/10] selinux: reject a permission value exceeding the class permission count
2026-08-27 5:17 ` Sasha Levin
` (11 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 07/10] selinux: more strict policy parsing Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 09/10] selinux: reject a class permission count below its inherited common Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 10/10] selinux: require a class's permission values to cover its permission count Wentao Guan
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
perm_read() bounds a permission value by SEL_VEC_MAX but never by the
nprim of the owning class or common, which is taken verbatim from the
policy image. security_get_permissions() then writes perms[value - 1]
into an nprim-sized kcalloc() array, so a class declaring fewer
permissions than its largest permission value drives an out-of-bounds
heap write. The top-level symbol tables are validated this way; the
nested per-class permission table is not.
Reject a permission whose value exceeds nprim, which is already set when
perm_read() runs. Well-formed policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: tweak comment for line length]
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit d14b5d0e97fccd27974fedc03b903408872907fd)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 8dc77e347a77f..a3d52beb65487 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1160,6 +1160,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 09/10] selinux: reject a class permission count below its inherited common
2026-08-27 5:17 ` Sasha Levin
` (12 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 08/10] selinux: reject a permission value exceeding the class permission count Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 10/10] selinux: require a class's permission values to cover its permission count Wentao Guan
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() maps an inherited common's permissions into
an array sized by the class's own permissions.nprim, but class_read()
takes that nprim verbatim from the policy image and never checks that it
covers the common. A class that inherits a common of N permissions while
declaring a smaller nprim is accepted, and on load the common's
permissions are written past the class-sized array -- an out-of-bounds
heap write.
Reject a class whose permission count is below its inherited common's.
Well-formed policies, where the class count already includes the
inherited permissions, are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit 9a82dcd98b6e6e11cfd162410967951f12152528)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index a3d52beb65487..926dae3731a0d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1399,6 +1399,18 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
+
+ /*
+ * security_get_permissions() maps the common's permissions
+ * into an array sized by this class's nprim, so a class must
+ * declare at least as many as the common it inherits.
+ */
+ if (cladatum->permissions.nprim <
+ cladatum->comdatum->permissions.nprim) {
+ pr_err("SELinux: class %s has fewer permissions than common %s\n",
+ key, cladatum->comkey);
+ goto bad;
+ }
}
for (i = 0; i < nel; i++) {
rc = perm_read(p, &cladatum->permissions, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [PATCH 6.18.y 10/10] selinux: require a class's permission values to cover its permission count
2026-08-27 5:17 ` Sasha Levin
` (13 preceding siblings ...)
2026-08-27 8:38 ` [PATCH 6.18.y 09/10] selinux: reject a class permission count below its inherited common Wentao Guan
@ 2026-08-27 8:38 ` Wentao Guan
14 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 8:38 UTC (permalink / raw)
To: sashal; +Cc: guanwentao, hexlabsecurity, paul, stable, stephen.smalley.work
From: Bryam Vargas <hexlabsecurity@proton.me>
security_get_permissions() sizes an array by the class's permissions.nprim
and fills it at value - 1, from the inherited common's permission table and
then the class's own. A value no permission defines leaves a NULL that
sel_make_perm_files() passes to d_alloc_name(), an oops inside
sel_write_load() that strands selinux_state.policy_mutex and leaves every
later load in uninterruptible sleep; two permissions sharing a value
overwrite the first kstrdup(). Bounding each value by nprim catches
neither, and neither would a count: the symbol table is keyed on the
permission name, so duplicates pass.
Track the values each permission table claims and require them to cover
exactly what its count declares, rejecting a count no value can reach.
Conforming policies are unaffected.
Cc: stable@vger.kernel.org
Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
(cherry picked from commit b98a8ac50775540f3804397ed08f61ef9910bcab)
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 5 deletions(-)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 926dae3731a0d..483d7d54642a3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1139,7 +1139,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1163,6 +1174,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
/* indexes an nprim-sized array in security_get_permissions() */
if (perdatum->value > s->nprim)
goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1183,7 +1198,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
@@ -1205,17 +1220,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1351,7 +1377,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
@@ -1379,6 +1405,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1413,11 +1443,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
}
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
--
2.30.2
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes"
2026-08-27 8:38 ` [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes" Wentao Guan
@ 2026-08-27 8:58 ` Greg KH
2026-08-27 9:17 ` Wentao Guan
0 siblings, 1 reply; 37+ messages in thread
From: Greg KH @ 2026-08-27 8:58 UTC (permalink / raw)
To: Wentao Guan; +Cc: sashal, hexlabsecurity, paul, stable, stephen.smalley.work
On Thu, Aug 27, 2026 at 04:38:00PM +0800, Wentao Guan wrote:
> This reverts commit 3683f35c14816f827a80e217072018eb6c37422e.
> NOTE the commit revert the commit in stable-rc/linux-6.18.y
But why?
And a blank line should go after the "this reverts". Or just use the
standard git text here?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
@ 2026-08-27 8:59 ` Greg KH
2026-08-27 9:06 ` [PATCH 6.6 00/87] 6.6.154-rc1 review Wentao Guan
2026-08-27 9:07 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
0 siblings, 2 replies; 37+ messages in thread
From: Greg KH @ 2026-08-27 8:59 UTC (permalink / raw)
To: Wentao Guan; +Cc: sashal, hexlabsecurity, paul, stable, stephen.smalley.work
On Thu, Aug 27, 2026 at 04:37:55PM +0800, Wentao Guan wrote:
> This reverts commit b6336a9b52baa35136f024ab30929fdda83b3160.
Why? And this is not a real git id in the linux-stable tree.
Do you want me to just drop all of the selinux patches from the 6.18.y
queue right now before I do the release? I'll be glad to do so, but I
need to know why.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.6 00/87] 6.6.154-rc1 review
2026-08-27 8:59 ` Greg KH
@ 2026-08-27 9:06 ` Wentao Guan
2026-08-27 9:07 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
1 sibling, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 9:06 UTC (permalink / raw)
To: gregkh
Cc: guanwentao, hexlabsecurity, paul, sashal, stable,
stephen.smalley.work
> Why? And this is not a real git id in the linux-stable tree.
in stable-rc git tree.
>
> Do you want me to just drop all of the selinux patches from the 6.18.y
> queue right now before I do the release? I'll be glad to do so, but I
> need to know why.
fair,
What happened in my side is Github Copliot review point out the issue:
"This validation runs after symtab_insert(), so the table already owns key
and perdatum. On an out-of-range value, the bad path frees both while leaving
dangling pointers in the hash table, and rc is still 0, so malformed policy
data is accepted and later accesses can use freed memory.
Validate the value before inserting it and return -EINVAL."
In continues review, there LLM point out the mainline don`t had the issuse,
for correct sequence is that git log --oneline
(v6.18.46-1) selinux: require a class's permission values to cover its permission count
selinux: reject a class permission count below its inherited common
selinux: reject a permission value exceeding the class permission count
selinux: more strict policy parsing
selinux: use u16 for security classes
Revert "selinux: reject a class permission count below its inherited common"
Revert "selinux: reject a permission value exceeding the class permission count"
(tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
This instead of Sasha`s series:
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
The different in all applied different is
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ef7f767a682b3..483d7d54642a3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1171,6 +1171,13 @@ static int perm_read(struct policydb *p, struct symtab *s,
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1179,13 +1186,6 @@ static int perm_read(struct policydb *p, struct symtab *s,
rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
- /* indexes an nprim-sized array in security_get_permissions() */
- if (perdatum->value > s->nprim)
- goto bad;
- /* two permissions cannot share one slot of that array */
- if (*claimed & (1U << (perdatum->value - 1)))
- goto bad;
- *claimed |= 1U << (perdatum->value - 1);
return 0;
bad:
BRs
Wentao Guan
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 8:59 ` Greg KH
2026-08-27 9:06 ` [PATCH 6.6 00/87] 6.6.154-rc1 review Wentao Guan
@ 2026-08-27 9:07 ` Wentao Guan
2026-08-27 9:23 ` Greg KH
1 sibling, 1 reply; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 9:07 UTC (permalink / raw)
To: gregkh
Cc: guanwentao, hexlabsecurity, paul, sashal, stable,
stephen.smalley.work
Sorry for prev email title is garbaged...
> Why? And this is not a real git id in the linux-stable tree.
in stable-rc git tree.
>
> Do you want me to just drop all of the selinux patches from the 6.18.y
> queue right now before I do the release? I'll be glad to do so, but I
> need to know why.
fair,
What happened in my side is Github Copliot review point out the issue:
"This validation runs after symtab_insert(), so the table already owns key
and perdatum. On an out-of-range value, the bad path frees both while leaving
dangling pointers in the hash table, and rc is still 0, so malformed policy
data is accepted and later accesses can use freed memory.
Validate the value before inserting it and return -EINVAL."
In continues review, there LLM point out the mainline don`t had the issuse,
for correct sequence is that git log --oneline
(v6.18.46-1) selinux: require a class's permission values to cover its permission count
selinux: reject a class permission count below its inherited common
selinux: reject a permission value exceeding the class permission count
selinux: more strict policy parsing
selinux: use u16 for security classes
Revert "selinux: reject a class permission count below its inherited common"
Revert "selinux: reject a permission value exceeding the class permission count"
(tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
This instead of Sasha`s series:
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
The different in all applied different is
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ef7f767a682b3..483d7d54642a3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1171,6 +1171,13 @@ static int perm_read(struct policydb *p, struct symtab *s,
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1179,13 +1186,6 @@ static int perm_read(struct policydb *p, struct symtab *s,
rc = symtab_insert(s, key, perdatum);
if (rc)
goto bad;
- /* indexes an nprim-sized array in security_get_permissions() */
- if (perdatum->value > s->nprim)
- goto bad;
- /* two permissions cannot share one slot of that array */
- if (*claimed & (1U << (perdatum->value - 1)))
- goto bad;
- *claimed |= 1U << (perdatum->value - 1);
return 0;
bad:
BRs
Wentao Guan
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes"
2026-08-27 8:58 ` Greg KH
@ 2026-08-27 9:17 ` Wentao Guan
0 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 9:17 UTC (permalink / raw)
To: gregkh
Cc: guanwentao, hexlabsecurity, paul, sashal, stable,
stephen.smalley.work
> On Thu, Aug 27, 2026 at 04:38:00PM +0800, Wentao Guan wrote:
> > This reverts commit 3683f35c14816f827a80e217072018eb6c37422e.
> > NOTE the commit revert the commit in stable-rc/linux-6.18.y
>
> But why?
Revert and cleanly applied and not olny follow the mainline seq,
but also final state is aligned with mainline instead of context different
in security/selinux/ss/policydb.c perm_read():
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
>
> And a blank line should go after the "this reverts". Or just use the
> standard git text here?
OK, and i seem there can replace the patches in 6.18.47-rc1.
BRs
Wentao Guan
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 9:07 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
@ 2026-08-27 9:23 ` Greg KH
2026-08-27 9:32 ` Wentao Guan
0 siblings, 1 reply; 37+ messages in thread
From: Greg KH @ 2026-08-27 9:23 UTC (permalink / raw)
To: Wentao Guan; +Cc: hexlabsecurity, paul, sashal, stable, stephen.smalley.work
On Thu, Aug 27, 2026 at 05:07:59PM +0800, Wentao Guan wrote:
> Sorry for prev email title is garbaged...
>
> > Why? And this is not a real git id in the linux-stable tree.
> in stable-rc git tree.
>
> >
> > Do you want me to just drop all of the selinux patches from the 6.18.y
> > queue right now before I do the release? I'll be glad to do so, but I
> > need to know why.
>
> fair,
>
> What happened in my side is Github Copliot review point out the issue:
> "This validation runs after symtab_insert(), so the table already owns key
> and perdatum. On an out-of-range value, the bad path frees both while leaving
> dangling pointers in the hash table, and rc is still 0, so malformed policy
> data is accepted and later accesses can use freed memory.
> Validate the value before inserting it and return -EINVAL."
I have no idea what that means at all as I can't parse LLM output well.
> In continues review, there LLM point out the mainline don`t had the issuse,
> for correct sequence is that git log --oneline
> (v6.18.46-1) selinux: require a class's permission values to cover its permission count
> selinux: reject a class permission count below its inherited common
> selinux: reject a permission value exceeding the class permission count
> selinux: more strict policy parsing
> selinux: use u16 for security classes
> Revert "selinux: reject a class permission count below its inherited common"
> Revert "selinux: reject a permission value exceeding the class permission count"
> (tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
>
> This instead of Sasha`s series:
> 2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
> 2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
> 2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
Ok, I'll drop these for now, and wait for your backports.
But note, I'll drop this for 6.18 and older, so I need backports for ALL
of those branches.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 9:23 ` Greg KH
@ 2026-08-27 9:32 ` Wentao Guan
2026-08-27 10:32 ` Greg KH
0 siblings, 1 reply; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 9:32 UTC (permalink / raw)
To: gregkh
Cc: guanwentao, hexlabsecurity, paul, sashal, stable,
stephen.smalley.work
> On Thu, Aug 27, 2026 at 05:07:59PM +0800, Wentao Guan wrote:
> > Sorry for prev email title is garbaged...
> >
> > > Why? And this is not a real git id in the linux-stable tree.
> > in stable-rc git tree.
> >
> > >
> > > Do you want me to just drop all of the selinux patches from the 6.18.y
> > > queue right now before I do the release? I'll be glad to do so, but I
> > > need to know why.
> >
> > fair,
> >
> > What happened in my side is Github Copliot review point out the issue:
> > "This validation runs after symtab_insert(), so the table already owns key
> > and perdatum. On an out-of-range value, the bad path frees both while leaving
> > dangling pointers in the hash table, and rc is still 0, so malformed policy
> > data is accepted and later accesses can use freed memory.
> > Validate the value before inserting it and return -EINVAL."
>
> I have no idea what that means at all as I can't parse LLM output well.
>
> > In continues review, there LLM point out the mainline don`t had the issuse,
> > for correct sequence is that git log --oneline
> > (v6.18.46-1) selinux: require a class's permission values to cover its permission count
> > selinux: reject a class permission count below its inherited common
> > selinux: reject a permission value exceeding the class permission count
> > selinux: more strict policy parsing
> > selinux: use u16 for security classes
> > Revert "selinux: reject a class permission count below its inherited common"
> > Revert "selinux: reject a permission value exceeding the class permission count"
> > (tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
> >
> > This instead of Sasha`s series:
> > 2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
> > 2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
> > 2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
>
> Ok, I'll drop these for now, and wait for your backports.
>
> But note, I'll drop this for 6.18 and older, so I need backports for ALL
> of those branches.
for 6.18.y:
1. https://lore.kernel.org/stable/20260825100338.4039846-1-guanwentao@uniontech.com/
2 .https://lore.kernel.org/stable/20260825100338.4039846-2-guanwentao@uniontech.com/
3 .https://lore.kernel.org/stable/20260825100338.4039846-3-guanwentao@uniontech.com/
4 .https://lore.kernel.org/stable/20260825100338.4039846-4-guanwentao@uniontech.com/
5 .https://lore.kernel.org/stable/20260825100338.4039846-5-guanwentao@uniontech.com/
6 .https://lore.kernel.org/stable/20260825100338.4039846-6-guanwentao@uniontech.com/
7 .https://lore.kernel.org/stable/20260825100338.4039846-7-guanwentao@uniontech.com/
for 7.1.y:
1. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
2. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
3. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
4. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
5. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
There only different between 6.18.y and 7.1.y are 6.18 revert which applied not
cleanly:
62be2361da29505d3183da854f1c98d2e208e852
9f721b5d9e1d956ffe965bc09375989eb51881fd
and next reapplied in next series cleanly.
BRs
Wentao Guan
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 9:32 ` Wentao Guan
@ 2026-08-27 10:32 ` Greg KH
2026-08-27 10:37 ` Wentao Guan
0 siblings, 1 reply; 37+ messages in thread
From: Greg KH @ 2026-08-27 10:32 UTC (permalink / raw)
To: Wentao Guan; +Cc: hexlabsecurity, paul, sashal, stable, stephen.smalley.work
On Thu, Aug 27, 2026 at 05:32:27PM +0800, Wentao Guan wrote:
> > On Thu, Aug 27, 2026 at 05:07:59PM +0800, Wentao Guan wrote:
> > > Sorry for prev email title is garbaged...
> > >
> > > > Why? And this is not a real git id in the linux-stable tree.
> > > in stable-rc git tree.
> > >
> > > >
> > > > Do you want me to just drop all of the selinux patches from the 6.18.y
> > > > queue right now before I do the release? I'll be glad to do so, but I
> > > > need to know why.
> > >
> > > fair,
> > >
> > > What happened in my side is Github Copliot review point out the issue:
> > > "This validation runs after symtab_insert(), so the table already owns key
> > > and perdatum. On an out-of-range value, the bad path frees both while leaving
> > > dangling pointers in the hash table, and rc is still 0, so malformed policy
> > > data is accepted and later accesses can use freed memory.
> > > Validate the value before inserting it and return -EINVAL."
> >
> > I have no idea what that means at all as I can't parse LLM output well.
> >
> > > In continues review, there LLM point out the mainline don`t had the issuse,
> > > for correct sequence is that git log --oneline
> > > (v6.18.46-1) selinux: require a class's permission values to cover its permission count
> > > selinux: reject a class permission count below its inherited common
> > > selinux: reject a permission value exceeding the class permission count
> > > selinux: more strict policy parsing
> > > selinux: use u16 for security classes
> > > Revert "selinux: reject a class permission count below its inherited common"
> > > Revert "selinux: reject a permission value exceeding the class permission count"
> > > (tag: v6.18.46, stable-gh/linux-6.18.y) Linux 6.18.46
> > >
> > > This instead of Sasha`s series:
> > > 2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
> > > 2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
> > > 2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
> >
> > Ok, I'll drop these for now, and wait for your backports.
> >
> > But note, I'll drop this for 6.18 and older, so I need backports for ALL
> > of those branches.
>
> for 6.18.y:
> 1. https://lore.kernel.org/stable/20260825100338.4039846-1-guanwentao@uniontech.com/
> 2 .https://lore.kernel.org/stable/20260825100338.4039846-2-guanwentao@uniontech.com/
> 3 .https://lore.kernel.org/stable/20260825100338.4039846-3-guanwentao@uniontech.com/
> 4 .https://lore.kernel.org/stable/20260825100338.4039846-4-guanwentao@uniontech.com/
> 5 .https://lore.kernel.org/stable/20260825100338.4039846-5-guanwentao@uniontech.com/
> 6 .https://lore.kernel.org/stable/20260825100338.4039846-6-guanwentao@uniontech.com/
> 7 .https://lore.kernel.org/stable/20260825100338.4039846-7-guanwentao@uniontech.com/
>
> for 7.1.y:
> 1. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
> 2. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
> 3. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
> 4. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
> 5. https://lore.kernel.org/stable/20260827054709.4027356-1-guanwentao@uniontech.com/
>
> There only different between 6.18.y and 7.1.y are 6.18 revert which applied not
> cleanly:
> 62be2361da29505d3183da854f1c98d2e208e852
> 9f721b5d9e1d956ffe965bc09375989eb51881fd
> and next reapplied in next series cleanly.
Again, I am totally confused. I'll go drop ALL of the selinux changes
in ALL of the queues and wait for the next round of releases before I
pick up the above ones (and we need that same series for older trees
too.)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count"
2026-08-27 10:32 ` Greg KH
@ 2026-08-27 10:37 ` Wentao Guan
0 siblings, 0 replies; 37+ messages in thread
From: Wentao Guan @ 2026-08-27 10:37 UTC (permalink / raw)
To: Greg KH; +Cc: hexlabsecurity, paul, Sasha Levin, stable, stephen.smalley.work
> Again, I am totally confused. I'll go drop ALL of the selinux changes
> in ALL of the queues and wait for the next round of releases before I
> pick up the above ones (and we need that same series for older trees
too.)
OK, I will take a look for older tree and send backports.
BRs
Wentao Guan
^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2026-08-27 10:37 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-20 10:41 FAILED: patch "[PATCH] selinux: require a class's permission values to cover its" failed to apply to 6.18-stable tree gregkh
2026-08-24 22:00 ` [PATCH 6.18.y 1/3] selinux: use u16 for security classes Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 2/3] selinux: more strict policy parsing Sasha Levin
2026-08-24 22:00 ` [PATCH 6.18.y 3/3] selinux: require a class's permission values to cover its permission count Sasha Levin
2026-08-25 10:02 ` Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 1/7] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
2026-08-27 5:17 ` Sasha Levin
2026-08-27 5:47 ` [PATCH 7.1.y 1/5] selinux: use u16 for security classes Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 2/5] selinux: more strict policy parsing Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 3/5] selinux: reject a permission value exceeding the class permission count Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 4/5] selinux: reject a class permission count below its inherited common Wentao Guan
2026-08-27 5:47 ` [PATCH 7.1.y 5/5] selinux: require a class's permission values to cover its permission count Wentao Guan
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
2026-08-27 8:59 ` Greg KH
2026-08-27 9:06 ` [PATCH 6.6 00/87] 6.6.154-rc1 review Wentao Guan
2026-08-27 9:07 ` [PATCH 6.18.47-rc1 01/10] Revert "selinux: require a class's permission values to cover its permission count" Wentao Guan
2026-08-27 9:23 ` Greg KH
2026-08-27 9:32 ` Wentao Guan
2026-08-27 10:32 ` Greg KH
2026-08-27 10:37 ` Wentao Guan
2026-08-27 8:37 ` [PATCH 6.18.47-rc1 02/10] Revert "selinux: more strict policy parsing" Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.47-rc1 03/10] Revert "selinux: use u16 for security classes" Wentao Guan
2026-08-27 8:58 ` Greg KH
2026-08-27 9:17 ` Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 04/10] Revert "selinux: reject a permission value exceeding the class permission count" Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 05/10] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 06/10] selinux: use u16 for security classes Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 07/10] selinux: more strict policy parsing Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 08/10] selinux: reject a permission value exceeding the class permission count Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 09/10] selinux: reject a class permission count below its inherited common Wentao Guan
2026-08-27 8:38 ` [PATCH 6.18.y 10/10] selinux: require a class's permission values to cover its permission count Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 2/7] Revert "selinux: reject a class permission count below its inherited common" Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 3/7] selinux: use u16 for security classes Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 4/7] selinux: more strict policy parsing Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 5/7] selinux: reject a permission value exceeding the class permission count Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 6/7] selinux: reject a class permission count below its inherited common Wentao Guan
2026-08-25 10:03 ` [PATCH 6.18.y 7/7] selinux: require a class's permission values to cover its permission count Wentao Guan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox