]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selinux: check length fields in policies
authorChristian Göttsche <cgzones@googlemail.com>
Sun, 11 May 2025 17:30:04 +0000 (19:30 +0200)
committerPaul Moore <paul@paul-moore.com>
Wed, 6 May 2026 23:43:18 +0000 (19:43 -0400)
In multiple places the binary policy announces how many items of some
kind are to be expected next.  Before reading them the kernel already
allocates enough memory for that announced size.  Validate that the
remaining input size can actually fit the announced items, to avoid OOM
issues on malformed binary policies.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
[PM: manual merge fuzz fixups, style fixes]
Signed-off-by: Paul Moore <paul@paul-moore.com>
security/selinux/ss/avtab.c
security/selinux/ss/conditional.c
security/selinux/ss/policydb.c
security/selinux/ss/policydb.h

index fb67b3600156692329d717e6b91f132de9acaaea..b708e2c9acdda80b7b674dfef2718df506905920 100644 (file)
@@ -515,6 +515,11 @@ int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol)
                goto bad;
        }
 
+       /* avtab_read_item() reads at least 96 bytes for any valid entry */
+       rc = size_check(3 * sizeof(u32), nel, fp);
+       if (rc)
+               goto bad;
+
        rc = avtab_alloc(a, nel);
        if (rc)
                goto bad;
index e2be39c10b8a0e4d37bdeb6bc2ea67fb0083b8b2..2956dd7edea78485a5acc771ce1108c8c4502bad 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "security.h"
 #include "conditional.h"
+#include "policydb.h"
 #include "services.h"
 
 /*
@@ -329,6 +330,11 @@ static int cond_read_av_list(struct policydb *p, struct policy_file *fp,
        if (len == 0)
                return 0;
 
+       /* avtab_read_item() reads at least 96 bytes for any valid entry */
+       rc = size_check(3 * sizeof(u32), len, fp);
+       if (rc)
+               return rc;
+
        list->nodes = kzalloc_objs(*list->nodes, len);
        if (!list->nodes)
                return -ENOMEM;
@@ -379,6 +385,12 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, struct pol
 
        /* expr */
        len = le32_to_cpu(buf[1]);
+
+       /* we will read 64 bytes per node */
+       rc = size_check(2 * sizeof(u32), len, fp);
+       if (rc)
+               return rc;
+
        node->expr.nodes = kzalloc_objs(*node->expr.nodes, len);
        if (!node->expr.nodes)
                return -ENOMEM;
@@ -417,6 +429,11 @@ int cond_read_list(struct policydb *p, struct policy_file *fp)
 
        len = le32_to_cpu(buf[0]);
 
+       /* cond_read_node() reads at least 128 bytes for any valid node */
+       rc = size_check(4 * sizeof(u32), len, fp);
+       if (rc)
+               return rc;
+
        p->cond_list = kzalloc_objs(*p->cond_list, len);
        if (!p->cond_list)
                return -ENOMEM;
index 69536a7912e44345d621a2b73be6eadf0224e73f..39065418bed915ceaae066ffc5316bad33e7eb9c 100644 (file)
@@ -1105,6 +1105,9 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
        if ((len == 0) || (len == (u32)-1))
                return -EINVAL;
 
+       if (size_check(sizeof(char), len, fp))
+               return -EINVAL;
+
        str = kmalloc(len + 1, flags | __GFP_NOWARN);
        if (!str)
                return -ENOMEM;
@@ -1179,6 +1182,11 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
        if (nel > SEL_VEC_MAX)
                goto bad;
 
+       /* perm_read() reads at least 64 bytes for any valid permission */
+       rc = size_check(2 * sizeof(u32), nel, fp);
+       if (rc)
+               goto bad;
+
        rc = symtab_init(&comdatum->permissions, nel);
        if (rc)
                goto bad;
@@ -1351,6 +1359,11 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
                goto bad;
        cladatum->value = val;
 
+       /* perm_read() reads at least 64 bytes for any valid permission */
+       rc = size_check(2 * sizeof(u32), nel, fp);
+       if (rc)
+               goto bad;
+
        rc = symtab_init(&cladatum->permissions, nel);
        if (rc)
                goto bad;
@@ -1924,6 +1937,13 @@ static int range_read(struct policydb *p, struct policy_file *fp)
 
        nel = le32_to_cpu(buf[0]);
 
+       /* we read at least 64 bytes and mls_read_range_helper() 32 bytes
+        * for any valid range-transition
+        */
+       rc = size_check(3 * sizeof(u32), nel, fp);
+       if (rc)
+               return rc;
+
        rc = hashtab_init(&p->range_tr, nel);
        if (rc)
                return rc;
@@ -2698,6 +2718,13 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
                nprim = le32_to_cpu(buf[0]);
                nel = le32_to_cpu(buf[1]);
 
+               /* every read_f() implementation reads at least 128 bytes
+                * for any valid entry
+                */
+               rc = size_check(4 * sizeof(u32), nel, fp);
+               if (rc)
+                       goto out;
+
                rc = symtab_init(&p->symtab[i], nel);
                if (rc)
                        goto out;
@@ -2739,6 +2766,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
                goto bad;
        nel = le32_to_cpu(buf[0]);
 
+       /* we read at least 96 bytes for any valid role-transition */
+       rc = size_check(3 * sizeof(u32), nel, fp);
+       if (rc)
+               goto bad;
+
        rc = hashtab_init(&p->role_tr, nel);
        if (rc)
                goto bad;
index 20b834581106a42258b69d85a913edd1137ed305..fdc94398a254052e49480d0f563306496049b2df 100644 (file)
@@ -355,6 +355,20 @@ struct policy_data {
        struct policy_file *fp;
 };
 
+static inline int size_check(size_t bytes, size_t num,
+                            const struct policy_file *fp)
+{
+       size_t len;
+
+       if (unlikely(check_mul_overflow(bytes, num, &len)))
+               return -EINVAL;
+
+       if (unlikely(len > fp->len))
+               return -EINVAL;
+
+       return 0;
+}
+
 static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
 {
        if (bytes > fp->len)