FAILED: patch "[PATCH] selinux: require a class's permission values to cover its" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-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 <[email protected]>.
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.1.y
git checkout FETCH_HEAD
git cherry-pick -x b98a8ac50775540f3804397ed08f61ef9910bcab
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026082022-lyrically-condiment-ea5e@gregkh' --subject-prefix 'PATCH 6.1.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 <[email protected]>
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.
security/selinux/ss/policydb.c +46 -5
@@ -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_filechar *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_fileif (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);
Sasha Levin
maintainerPATCH 1/3 selinux: use known type instead of void pointer
From: Christian Göttsche <[email protected]>
[ Upstream commit f07586160fd5492f8d48e7667e7a5d8797aa5090 ]
Improve type safety and readability by using the known type.
Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")
--- security/selinux/ss/avtab.c | 8 +-- security/selinux/ss/avtab.h | 10 ++-- security/selinux/ss/conditional.c | 12 ++--- security/selinux/ss/conditional.h | 6 +-- security/selinux/ss/ebitmap.c | 4 +- security/selinux/ss/ebitmap.h | 5 +- security/selinux/ss/policydb.c | 90 +++++++++++++++---------------- security/selinux/ss/policydb.h | 16 +++--- 8 files changed, 77 insertions(+), 74 deletions(-)
security/selinux/ss/avtab.c +4 -4
@@ -397,7 +397,7 @@ static const uint16_t spec_order[] = { AVTAB_XPERMS_DONTAUDIT }; -int avtab_read_item (struct avtab *a,void *fp,struct policydb *pol, +int avtab_read_item (struct avtab *a,struct policy_file *fp,struct policydb *pol,int (*insertf)(struct avtab *a,const struct avtab_key *k,const struct avtab_datum *d,void *p),void *p)
@@ -565,7 +565,7 @@ static int avtab_insertf(struct avtab *a, const struct avtab_key *k,return avtab_insert (a, k, d); } -int avtab_read (struct avtab *a,void *fp,struct policydb *pol) +int avtab_read (struct avtab *a,struct policy_file *fp,struct policydb *pol) {int rc;__le32 buf[1 ];
@@ -609,7 +609,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) goto out; } -int avtab_write_item (struct policydb *p,const struct avtab_node *cur,void *fp) +int avtab_write_item (struct policydb *p,const struct avtab_node *cur,struct policy_file *fp) {__le16 buf16[4 ];__le32 buf32[ARRAY_SIZE (cur->datum.u.xperms->perms.p)];
@@ -644,7 +644,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)return 0 ; } -int avtab_write (struct policydb *p,struct avtab *a,void *fp) +int avtab_write (struct policydb *p,struct avtab *a,struct policy_file *fp) {unsigned int i;int rc =0 ;
security/selinux/ss/avtab.h +6 -4
@@ -95,14 +95,16 @@ void avtab_destroy(struct avtab *h);void avtab_hash_eval (struct avtab *h,char *tag);struct policydb ; -int avtab_read_item (struct avtab *a,void *fp,struct policydb *pol, +struct policy_file ; +int avtab_read_item (struct avtab *a,struct policy_file *fp,struct policydb *pol,int (*insert)(struct avtab *a,const struct avtab_key *k,const struct avtab_datum *d,void *p),void *p); -int avtab_read (struct avtab *a,void *fp,struct policydb *pol); -int avtab_write_item (struct policydb *p,const struct avtab_node *cur,void *fp); -int avtab_write (struct policydb *p,struct avtab *a,void *fp); +int avtab_read (struct avtab *a,struct policy_file *fp,struct policydb *pol); +int avtab_write_item (struct policydb *p,const struct avtab_node *cur, +struct policy_file *fp); +int avtab_write (struct policydb *p,struct avtab *a,struct policy_file *fp); struct avtab_node *avtab_insert_nonunique(struct avtab *h, const struct avtab_key *key,
security/selinux/ss/conditional.c +6 -6
@@ -205,7 +205,7 @@ static int bool_isvalid(struct cond_bool_datum *b)return 1 ; } -int cond_read_bool (struct policydb *p,struct symtab *s,void *fp) +int cond_read_bool (struct policydb *p,struct symtab *s,struct policy_file *fp) {char *key =NULL ;struct cond_bool_datum *booldatum;
@@ -321,7 +321,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,return 0 ; } -static int cond_read_av_list (struct policydb *p,void *fp, +static int cond_read_av_list (struct policydb *p,struct policy_file *fp,struct cond_av_list *list,struct cond_av_list *other) {
@@ -373,7 +373,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)return 1 ; } -static int cond_read_node (struct policydb *p,struct cond_node *node,void *fp) +static int cond_read_node (struct policydb *p,struct cond_node *node,struct policy_file *fp) {__le32 buf[2 ];u32 i, len;
@@ -413,7 +413,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)return cond_read_av_list (p, fp, &node->false_list, &node->true_list); } -int cond_read_list (struct policydb *p,void *fp) +int cond_read_list (struct policydb *p,struct policy_file *fp) {__le32 buf[1 ];u32 i, len;
@@ -451,7 +451,7 @@ int cond_write_bool(void *vkey, void *datum, void *ptr)char *key = vkey;struct cond_bool_datum *booldatum = datum;struct policy_data *pd = ptr; -void *fp = pd->fp; +struct policy_file *fp = pd->fp;__le32 buf[3 ];u32 len;int rc;
@@ -534,7 +534,7 @@ static int cond_write_node(struct policydb *p, struct cond_node *node,return 0 ; } -int cond_write_list (struct policydb *p,void *fp) +int cond_write_list (struct policydb *p,struct policy_file *fp) {u32 i;__le32 buf[1 ];
security/selinux/ss/conditional.h +3 -3
@@ -69,10 +69,10 @@ int cond_destroy_bool(void *key, void *datum, void *p);int cond_index_bool (void *key,void *datum,void *datap); -int cond_read_bool (struct policydb *p,struct symtab *s,void *fp); -int cond_read_list (struct policydb *p,void *fp); +int cond_read_bool (struct policydb *p,struct symtab *s,struct policy_file *fp); +int cond_read_list (struct policydb *p,struct policy_file *fp);int cond_write_bool (void *key,void *datum,void *ptr); -int cond_write_list (struct policydb *p,void *fp); +int cond_write_list (struct policydb *p,struct policy_file *fp);void cond_compute_av (struct avtab *ctab,struct avtab_key *key,struct av_decision *avd,struct extended_perms *xperms);
security/selinux/ss/ebitmap.c +2 -2
@@ -362,7 +362,7 @@ void ebitmap_destroy(struct ebitmap *e) e->node =NULL ; } -int ebitmap_read (struct ebitmap *e,void *fp) +int ebitmap_read (struct ebitmap *e,struct policy_file *fp) {struct ebitmap_node *n =NULL ;u32 mapunit, count, startbit, index;
@@ -468,7 +468,7 @@ int ebitmap_read(struct ebitmap *e, void *fp) goto out; } -int ebitmap_write (const struct ebitmap *e,void *fp) +int ebitmap_write (const struct ebitmap *e,struct policy_file *fp) {struct ebitmap_node *n;u32 count;
security/selinux/ss/ebitmap.h +3 -2
@@ -129,8 +129,9 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 lasint ebitmap_get_bit (const struct ebitmap *e,unsigned long bit);int ebitmap_set_bit (struct ebitmap *e,unsigned long bit,int value);void ebitmap_destroy (struct ebitmap *e); -int ebitmap_read (struct ebitmap *e,void *fp); -int ebitmap_write (const struct ebitmap *e,void *fp); +struct policy_file ; +int ebitmap_read (struct ebitmap *e,struct policy_file *fp); +int ebitmap_write (const struct ebitmap *e,struct policy_file *fp);u32 ebitmap_hash (const struct ebitmap *e,u32 hash);#ifdef CONFIG_NETLABEL
security/selinux/ss/policydb.c +45 -45
360 lines not on this page · raw message
security/selinux/ss/policydb.h +8 -8
@@ -315,14 +315,19 @@ struct policydb {u32 process_trans_perms; } __randomize_layout; +struct policy_file { +char *data; +size_t len; +}; +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_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,void *fp); -extern int policydb_write (struct policydb *p,void *fp); +extern int policydb_read (struct policydb *p,struct policy_file *fp); +extern int policydb_write (struct policydb *p,struct policy_file *fp);extern struct filename_trans_datum *policydb_filenametr_search (struct policydb *p,struct filename_trans_key *key);
@@ -345,14 +350,9 @@ extern struct role_trans_datum *policydb_roletr_search(#define POLICYDB_MAGIC SELINUX_MAGIC#define POLICYDB_STRING "SE Linux" -struct policy_file { -char *data; -size_t len; -}; -struct policy_data {struct policydb *p; -void *fp; +struct policy_file *fp; };static inline int next_entry (void *buf,struct policy_file *fp,size_t bytes)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 selinux: reject a permission value exceeding the class permission count
From: Bryam Vargas <[email protected]>
[ Upstream commit d14b5d0e97fccd27974fedc03b903408872907fd ]
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.
Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")
--- security/selinux/ss/policydb.c | 6 ++++++ 1 file changed, 6 insertions(+)
security/selinux/ss/policydb.c +6 -0
@@ -1126,6 +1126,12 @@ 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; +/* 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)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 selinux: require a class's permission values to cover its permission count
In reply to Sasha LevinFrom: Bryam Vargas <[email protected]>
[ 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.
--- security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-)
security/selinux/ss/policydb.c +46 -5
@@ -1108,7 +1108,18 @@ static 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;
@@ -1132,6 +1143,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)
@@ -1152,7 +1167,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_filechar *key =NULL ;struct common_datum *comdatum;__le32 buf[4 ]; -u32 len, nel; +u32 len, nel, claimed =0 ;int i, rc; comdatum =kzalloc (sizeof (*comdatum),GFP_KERNEL );
@@ -1171,17 +1186,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_fileif (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; + } + rc =symtab_insert (s, key, comdatum);if (rc) goto bad;
@@ -1316,7 +1342,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 len, len2, ncons, nel; +u32 len, len2, ncons, nel, claimed =0 , inherited =0 ;int i, rc; cladatum =kzalloc (sizeof (*cladatum),GFP_KERNEL );
@@ -1336,6 +1362,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 ]);
@@ -1370,11 +1400,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; + } + rc =read_cons_helper (p, &cladatum->constraints, ncons,0 , fp);if (rc) goto bad;
Signature · 1 line
2.53.0