summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 12:41:00 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 12:41:00 +0100
commit231e9d447ea97033ae8b8dff7b910e6269d7c5af (patch)
treefe9189eb264ea2acf0319ffd4b42abc8419f31a0
parent87599bd29856ea7bfdd62591c581c8be5a4719ee (diff)
parent033182baeab63ce96a6eb8aef1a6cd444fcf9519 (diff)
Merge tag 'selinux-pr-20260615' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore: "A number of SELinux patches, almost all of which are either minor fixes or hardening patches: - Additional verifications when loading new SELinux policy Multiple patches by Christian Göttsche to add additional validations to the code responsible for loading and parsing SELinux policy as it is loaded into the kernel. - Avoid nontransitive comparisons comparisons in our sorting code Done to prevent unexpected sorting results due to overflow. Qualys documented a similar issue with glibc https://www.qualys.com/2024/01/30/qsort.txt - Consistently use u16 for SELinux security classes - Move from page allocations to kmalloc() based allocations Unfortunately one of these patches had to be reverted, but you should see a fixed version during the next merge window. - Move from kmalloc_objs() to kzalloc_objs() in the policy load code - Reorder sel_kill_sb() slightly to match other pseudo filesystems - Simplify things with QSTR() instead of QSTR_INIT() - Minor comment typo fixes" * tag 'selinux-pr-20260615' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: revert use of __getname() in selinux_genfs_get_sid() selinux: comment spelling fix in ibpkey.c selinux: comment typo fix in selinuxfs.c selinux: hooks: use __getname() to allocate path buffer selinux: use k[mz]alloc() to allocate temporary buffers selinux: check for simple types selinux: more strict bounds check selinux: beef up isvalid checks selinux: reorder policydb_index() selinux: check type attr map overflows selinux: check length fields in policies selinux: more strict policy parsing selinux: use u16 for security classes selinux: avoid nontransitive comparison selinux: switch two allocations to use kzalloc_objs() selinux: fix sel_kill_sb() selinux: use QSTR() instead of QSTR_INIT() in init_sel_fs
-rw-r--r--security/selinux/ibpkey.c2
-rw-r--r--security/selinux/include/security.h1
-rw-r--r--security/selinux/selinuxfs.c27
-rw-r--r--security/selinux/ss/avtab.c49
-rw-r--r--security/selinux/ss/avtab.h13
-rw-r--r--security/selinux/ss/conditional.c39
-rw-r--r--security/selinux/ss/constraint.h1
-rw-r--r--security/selinux/ss/ebitmap.c27
-rw-r--r--security/selinux/ss/ebitmap.h1
-rw-r--r--security/selinux/ss/hashtab.h4
-rw-r--r--security/selinux/ss/mls.c66
-rw-r--r--security/selinux/ss/mls.h6
-rw-r--r--security/selinux/ss/policydb.c358
-rw-r--r--security/selinux/ss/policydb.h56
-rw-r--r--security/selinux/ss/services.c13
-rw-r--r--security/selinux/ss/symtab.c2
-rw-r--r--security/selinux/ss/symtab.h2
17 files changed, 512 insertions, 155 deletions
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 93a5637fbcd8..ae09d59aaa06 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -2,7 +2,7 @@
/*
* Pkey table
*
- * SELinux must keep a mapping of Infinband PKEYs to labels/SIDs. This
+ * SELinux must keep a mapping of Infiniband PKEYs to labels/SIDs. This
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead.
*
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0babb8992181..a74415e3afd3 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/selinuxfs.c b/security/selinux/selinuxfs.c
index 25ca7d714014..5aaaf69410bb 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -94,9 +94,8 @@ static int selinux_fs_info_create(struct super_block *sb)
return 0;
}
-static void selinux_fs_info_free(struct super_block *sb)
+static void selinux_fs_info_free(struct selinux_fs_info *fsi)
{
- struct selinux_fs_info *fsi = sb->s_fs_info;
unsigned int i;
if (fsi) {
@@ -105,8 +104,7 @@ static void selinux_fs_info_free(struct super_block *sb)
kfree(fsi->bool_pending_names);
kfree(fsi->bool_pending_values);
}
- kfree(sb->s_fs_info);
- sb->s_fs_info = NULL;
+ kfree(fsi);
}
#define SEL_INITCON_INO_OFFSET 0x01000000
@@ -785,7 +783,7 @@ static const struct file_operations transaction_ops = {
/*
* payload - write methods
* If the method has a response, the response should be put in buf,
- * and the length returned. Otherwise return 0 or and -error.
+ * and the length returned. Otherwise return 0 or -error.
*/
static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
@@ -1244,7 +1242,7 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
char **names, *page;
u32 i, num;
- page = (char *)get_zeroed_page(GFP_KERNEL);
+ page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1290,7 +1288,7 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
ret = sel_attach_file(bool_dir, names[i], inode);
}
out:
- free_page((unsigned long)page);
+ kfree(page);
return ret;
}
@@ -1349,14 +1347,14 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
char *page;
ssize_t length;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
length = avc_get_hash_stats(page);
if (length >= 0)
length = simple_read_from_buffer(buf, count, ppos, page, length);
- free_page((unsigned long)page);
+ kfree(page);
return length;
}
@@ -1367,7 +1365,7 @@ static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf,
char *page;
ssize_t length;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1375,7 +1373,7 @@ static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf,
if (length >= 0)
length = simple_read_from_buffer(buf, count, ppos, page,
length);
- free_page((unsigned long)page);
+ kfree(page);
return length;
}
@@ -1959,8 +1957,10 @@ static int sel_init_fs_context(struct fs_context *fc)
static void sel_kill_sb(struct super_block *sb)
{
- selinux_fs_info_free(sb);
+ struct selinux_fs_info *fsi = sb->s_fs_info;
+
kill_anon_super(sb);
+ selinux_fs_info_free(fsi);
}
static struct file_system_type sel_fs_type = {
@@ -1973,8 +1973,7 @@ struct path selinux_null __ro_after_init;
int __init init_sel_fs(void)
{
- struct qstr null_name = QSTR_INIT(NULL_FILE_NAME,
- sizeof(NULL_FILE_NAME)-1);
+ struct qstr null_name = QSTR(NULL_FILE_NAME);
int err;
if (!selinux_enabled_boot)
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index d12ca337e649..0f94edd01f69 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,8 +386,20 @@ 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++]);
+
+ if ((key.specified & AVTAB_TYPE) &&
+ !policydb_simpletype_isvalid(pol, datum.u.data)) {
+ pr_err("SELinux: avtab: invalid type\n");
+ return -EINVAL;
+ }
+
rc = insertf(a, &key, &datum, p);
if (rc)
return rc;
@@ -411,9 +433,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 +464,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");
@@ -461,7 +491,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
datum.u.data = le32_to_cpu(*buf32);
}
if ((key.specified & AVTAB_TYPE) &&
- !policydb_type_isvalid(pol, datum.u.data)) {
+ !policydb_simpletype_isvalid(pol, datum.u.data)) {
pr_err("SELinux: avtab: invalid type\n");
return -EINVAL;
}
@@ -492,6 +522,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;
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 850b3453f259..1de4cce288a7 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 824c3f896151..2956dd7edea7 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -12,6 +12,7 @@
#include "security.h"
#include "conditional.h"
+#include "policydb.h"
#include "services.h"
/*
@@ -165,7 +166,7 @@ void cond_policydb_destroy(struct policydb *p)
int cond_init_bool_indexes(struct policydb *p)
{
kfree(p->bool_val_to_struct);
- p->bool_val_to_struct = kmalloc_objs(*p->bool_val_to_struct,
+ p->bool_val_to_struct = kzalloc_objs(*p->bool_val_to_struct,
p->p_bools.nprim);
if (!p->bool_val_to_struct)
return -ENOMEM;
@@ -199,19 +200,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 +217,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 +236,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;
}
@@ -334,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;
@@ -362,7 +363,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;
}
@@ -383,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;
@@ -421,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;
@@ -709,7 +722,7 @@ static int duplicate_policydb_bools(struct policydb *newdb,
struct cond_bool_datum **cond_bool_array;
int rc;
- cond_bool_array = kmalloc_objs(*orig->bool_val_to_struct,
+ cond_bool_array = kzalloc_objs(*orig->bool_val_to_struct,
orig->p_bools.nprim);
if (!cond_bool_array)
return -ENOMEM;
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 203033cfad67..1d75a8a044df 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/ebitmap.c b/security/selinux/ss/ebitmap.c
index 43bc19e21960..ea894361fed6 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -257,6 +257,33 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
return 1;
}
+u32 ebitmap_get_highest_set_bit(const struct ebitmap *e)
+{
+ const struct ebitmap_node *n;
+ unsigned long unit;
+ u32 pos = 0;
+
+ n = e->node;
+ if (!n)
+ return 0;
+
+ while (n->next)
+ n = n->next;
+
+ for (unsigned int i = EBITMAP_UNIT_NUMS; i > 0; i--) {
+ unit = n->maps[i - 1];
+ if (unit == 0)
+ continue;
+
+ pos = (i - 1) * EBITMAP_UNIT_SIZE;
+ while (unit >>= 1)
+ pos++;
+ break;
+ }
+
+ return n->startbit + pos;
+}
+
int ebitmap_get_bit(const struct ebitmap *e, u32 bit)
{
const struct ebitmap_node *n;
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index c9569998f287..ae05cc8e4672 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -126,6 +126,7 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1,
const struct ebitmap *e2);
int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
u32 last_e2bit);
+u32 ebitmap_get_highest_set_bit(const struct ebitmap *e);
int ebitmap_get_bit(const struct ebitmap *e, u32 bit);
int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value);
void ebitmap_destroy(struct ebitmap *e);
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
index deba82d78c3a..c641fb12916b 100644
--- a/security/selinux/ss/hashtab.h
+++ b/security/selinux/ss/hashtab.h
@@ -94,11 +94,11 @@ static inline int hashtab_insert(struct hashtab *h, void *key, void *datum,
* Returns NULL if no entry has the specified key or
* the datum of the entry otherwise.
*/
-static inline void *hashtab_search(struct hashtab *h, const void *key,
+static inline void *hashtab_search(const struct hashtab *h, const void *key,
struct hashtab_key_params key_params)
{
u32 hvalue;
- struct hashtab_node *cur;
+ const struct hashtab_node *cur;
if (!h->size)
return NULL;
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index a6e49269f535..3cd36e2015fa 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -32,7 +32,7 @@
int mls_compute_context_len(struct policydb *p, struct context *context)
{
int i, l, len, head, prev;
- char *nm;
+ const char *nm;
struct ebitmap *e;
struct ebitmap_node *node;
@@ -86,7 +86,8 @@ int mls_compute_context_len(struct policydb *p, struct context *context)
void mls_sid_to_context(struct policydb *p, struct context *context,
char **scontext)
{
- char *scontextp, *nm;
+ const char *nm;
+ char *scontextp;
int i, l, head, prev;
struct ebitmap *e;
struct ebitmap_node *node;
@@ -155,27 +156,44 @@ void mls_sid_to_context(struct policydb *p, struct context *context,
*scontext = scontextp;
}
-int mls_level_isvalid(struct policydb *p, struct mls_level *l)
+bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
{
- struct level_datum *levdatum;
+ const char *name;
+ const struct level_datum *levdatum;
+ struct ebitmap_node *node;
+ u32 bit;
+ int rc;
if (!l->sens || l->sens > p->p_levels.nprim)
- return 0;
- levdatum = symtab_search(&p->p_levels,
- sym_name(p, SYM_LEVELS, l->sens - 1));
+ return false;
+
+ name = sym_name(p, SYM_LEVELS, l->sens - 1);
+ if (!name)
+ return false;
+
+ levdatum = symtab_search(&p->p_levels, name);
if (!levdatum)
- return 0;
+ return false;
/*
- * Return 1 iff all the bits set in l->cat are also be set in
+ * Validate that all bits set in l->cat are also be set in
* levdatum->level->cat and no bit in l->cat is larger than
* p->p_cats.nprim.
*/
- return ebitmap_contains(&levdatum->level.cat, &l->cat,
- p->p_cats.nprim);
+ rc = ebitmap_contains(&levdatum->level.cat, &l->cat,
+ p->p_cats.nprim);
+ if (!rc)
+ return false;
+
+ ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
+ if (!sym_name(p, SYM_CATS, bit))
+ return false;
+ }
+
+ return true;
}
-int mls_range_isvalid(struct policydb *p, struct mls_range *r)
+bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r)
{
return (mls_level_isvalid(p, &r->level[0]) &&
mls_level_isvalid(p, &r->level[1]) &&
@@ -183,32 +201,32 @@ int mls_range_isvalid(struct policydb *p, struct mls_range *r)
}
/*
- * Return 1 if the MLS fields in the security context
+ * Return true if the MLS fields in the security context
* structure `c' are valid. Return 0 otherwise.
*/
-int mls_context_isvalid(struct policydb *p, struct context *c)
+bool mls_context_isvalid(const struct policydb *p, const struct context *c)
{
- struct user_datum *usrdatum;
+ const struct user_datum *usrdatum;
if (!p->mls_enabled)
- return 1;
+ return true;
if (!mls_range_isvalid(p, &c->range))
- return 0;
+ return false;
if (c->role == OBJECT_R_VAL)
- return 1;
+ return true;
/*
* User must be authorized for the MLS range.
*/
if (!c->user || c->user > p->p_users.nprim)
- return 0;
+ return false;
usrdatum = p->user_val_to_struct[c->user - 1];
- if (!mls_range_contains(usrdatum->range, c->range))
- return 0; /* user may not be associated with range */
+ if (!usrdatum || !mls_range_contains(usrdatum->range, c->range))
+ return false; /* user may not be associated with range */
- return 1;
+ return true;
}
/*
@@ -449,8 +467,8 @@ int mls_convert_context(struct policydb *oldp, struct policydb *newp,
return 0;
for (l = 0; l < 2; l++) {
- char *name = sym_name(oldp, SYM_LEVELS,
- oldc->range.level[l].sens - 1);
+ const char *name = sym_name(oldp, SYM_LEVELS,
+ oldc->range.level[l].sens - 1);
levdatum = symtab_search(&newp->p_levels, name);
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 07980636751f..93cde1b22992 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -27,9 +27,9 @@
int mls_compute_context_len(struct policydb *p, struct context *context);
void mls_sid_to_context(struct policydb *p, struct context *context,
char **scontext);
-int mls_context_isvalid(struct policydb *p, struct context *c);
-int mls_range_isvalid(struct policydb *p, struct mls_range *r);
-int mls_level_isvalid(struct policydb *p, struct mls_level *l);
+bool mls_context_isvalid(const struct policydb *p, const struct context *c);
+bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r);
+bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l);
int mls_context_to_sid(struct policydb *p, char oldc, char *scontext,
struct context *context, struct sidtab *s, u32 def_sid);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 738fd47f33e6..ead504a639e3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/audit.h>
+#include <linux/sort.h>
#include "security.h"
#include "policydb.h"
@@ -429,11 +430,11 @@ static int filenametr_cmp(const void *k1, const void *k2)
const struct filename_trans_key *ft2 = k2;
int v;
- v = ft1->ttype - ft2->ttype;
+ v = cmp_int(ft1->ttype, ft2->ttype);
if (v)
return v;
- v = ft1->tclass - ft2->tclass;
+ v = cmp_int(ft1->tclass, ft2->tclass);
if (v)
return v;
@@ -464,15 +465,15 @@ static int rangetr_cmp(const void *k1, const void *k2)
const struct range_trans *key1 = k1, *key2 = k2;
int v;
- v = key1->source_type - key2->source_type;
+ v = cmp_int(key1->source_type, key2->source_type);
if (v)
return v;
- v = key1->target_type - key2->target_type;
+ v = cmp_int(key1->target_type, key2->target_type);
if (v)
return v;
- v = key1->target_class - key2->target_class;
+ v = cmp_int(key1->target_class, key2->target_class);
return v;
}
@@ -501,15 +502,15 @@ static int role_trans_cmp(const void *k1, const void *k2)
const struct role_trans_key *key1 = k1, *key2 = k2;
int v;
- v = key1->role - key2->role;
+ v = cmp_int(key1->role, key2->role);
if (v)
return v;
- v = key1->type - key2->type;
+ v = cmp_int(key1->type, key2->type);
if (v)
return v;
- return key1->tclass - key2->tclass;
+ return cmp_int(key1->tclass, key2->tclass);
}
static const struct hashtab_key_params roletr_key_params = {
@@ -638,13 +639,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 +656,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;
}
@@ -735,7 +733,6 @@ static int policydb_index(struct policydb *p)
pr_debug("SELinux: %d classes, %d rules\n", p->p_classes.nprim,
p->te_avtab.nel);
- avtab_hash_eval(&p->te_avtab, "rules");
symtab_hash_eval(p->symtab);
p->class_val_to_struct = kzalloc_objs(*p->class_val_to_struct,
@@ -931,44 +928,76 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return 0;
}
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+bool policydb_class_isvalid(const struct policydb *p, u16 class)
{
if (!class || class > p->p_classes.nprim)
- return 0;
- return 1;
+ return false;
+ if (!p->sym_val_to_name[SYM_CLASSES][class - 1])
+ return false;
+ return true;
+}
+
+bool policydb_user_isvalid(const struct policydb *p, u32 user)
+{
+ if (!user || user > p->p_users.nprim)
+ return false;
+ if (!p->sym_val_to_name[SYM_USERS][user - 1])
+ return false;
+ return true;
}
-int policydb_role_isvalid(struct policydb *p, unsigned int role)
+bool policydb_role_isvalid(const struct policydb *p, u32 role)
{
if (!role || role > p->p_roles.nprim)
- return 0;
- return 1;
+ return false;
+ if (!p->sym_val_to_name[SYM_ROLES][role - 1])
+ return false;
+ return true;
}
-int policydb_type_isvalid(struct policydb *p, unsigned int type)
+bool policydb_type_isvalid(const struct policydb *p, u32 type)
{
if (!type || type > p->p_types.nprim)
- return 0;
- return 1;
+ return false;
+ if (!p->sym_val_to_name[SYM_TYPES][type - 1])
+ return false;
+ return true;
+}
+
+bool policydb_simpletype_isvalid(const struct policydb *p, u32 type)
+{
+ const struct type_datum *datum;
+
+ if (!type || type > p->p_types.nprim)
+ return false;
+
+ datum = p->type_val_to_struct[type - 1];
+ if (!datum)
+ return false;
+
+ if (datum->attribute)
+ return false;
+
+ return true;
}
/*
- * Return 1 if the fields in the security context
+ * Return true if the fields in the security context
* structure `c' are valid. Return 0 otherwise.
*/
-int policydb_context_isvalid(struct policydb *p, struct context *c)
+bool policydb_context_isvalid(const struct policydb *p, const struct context *c)
{
- struct role_datum *role;
- struct user_datum *usrdatum;
+ const struct role_datum *role;
+ const struct user_datum *usrdatum;
if (!c->role || c->role > p->p_roles.nprim)
- return 0;
+ return false;
if (!c->user || c->user > p->p_users.nprim)
- return 0;
+ return false;
if (!c->type || c->type > p->p_types.nprim)
- return 0;
+ return false;
if (c->role != OBJECT_R_VAL) {
/*
@@ -977,24 +1006,24 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
role = p->role_val_to_struct[c->role - 1];
if (!role || !ebitmap_get_bit(&role->types, c->type - 1))
/* role may not be associated with type */
- return 0;
+ return false;
/*
* User must be authorized for the role.
*/
usrdatum = p->user_val_to_struct[c->user - 1];
if (!usrdatum)
- return 0;
+ return false;
if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1))
/* user may not be associated with role */
- return 0;
+ return false;
}
if (!mls_context_isvalid(p, c))
- return 0;
+ return false;
- return 1;
+ return true;
}
/*
@@ -1107,6 +1136,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;
@@ -1140,6 +1172,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 +1209,14 @@ 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;
+
+ /* 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)
@@ -1323,7 +1366,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 +1379,21 @@ 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;
+
+ /* 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)
@@ -1393,16 +1449,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 +1511,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 +1704,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 +1715,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 +1735,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 +1746,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 +1758,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 +1774,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;
}
@@ -1698,6 +1811,12 @@ static int user_bounds_sanity_check(void *key, void *datum, void *datap)
return -EINVAL;
}
+ if (!policydb_user_isvalid(p, upper->bounds)) {
+ pr_err("SELinux: user %s: invalid boundary id %d\n",
+ (char *) key, upper->bounds);
+ return -EINVAL;
+ }
+
upper = p->user_val_to_struct[upper->bounds - 1];
ebitmap_for_each_positive_bit(&user->roles, node, bit)
{
@@ -1735,6 +1854,12 @@ static int role_bounds_sanity_check(void *key, void *datum, void *datap)
return -EINVAL;
}
+ if (!policydb_role_isvalid(p, upper->bounds)) {
+ pr_err("SELinux: role %s: invalid boundary id %d\n",
+ (char *) key, upper->bounds);
+ return -EINVAL;
+ }
+
upper = p->role_val_to_struct[upper->bounds - 1];
ebitmap_for_each_positive_bit(&role->types, node, bit)
{
@@ -1769,9 +1894,13 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
return -EINVAL;
}
- upper = p->type_val_to_struct[upper->bounds - 1];
- BUG_ON(!upper);
+ if (!policydb_type_isvalid(p, upper->bounds)) {
+ pr_err("SELinux: type %s: invalid boundary id %d\n",
+ (char *) key, upper->bounds);
+ return -EINVAL;
+ }
+ upper = p->type_val_to_struct[upper->bounds - 1];
if (upper->attribute) {
pr_err("SELinux: type %s: "
"bounded by attribute %s\n",
@@ -1844,7 +1973,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;
@@ -1855,6 +1984,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;
@@ -1875,7 +2011,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 +2052,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 +2062,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,12 +2081,22 @@ 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]);
+ if (!policydb_simpletype_isvalid(p, otype))
+ goto out;
last = NULL;
datum = policydb_filenametr_search(p, &key);
@@ -1997,6 +2149,9 @@ out:
kfree(ft);
kfree(name);
kfree(datum);
+
+ if (rc)
+ pr_err("SELinux: invalid compat filename transition\n");
return rc;
}
@@ -2005,7 +2160,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, val;
+ u16 tclass;
__le32 buf[3];
int rc;
@@ -2024,8 +2180,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) {
@@ -2055,6 +2218,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_simpletype_isvalid(p, datum->otype))
+ goto out;
+
dst = &datum->next;
}
@@ -2086,6 +2253,9 @@ out:
ebitmap_destroy(&datum->stypes);
kfree(datum);
}
+
+ if (rc)
+ pr_err("SELinux: invalid filename transition\n");
return rc;
}
@@ -2133,7 +2303,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;
@@ -2203,7 +2373,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)
@@ -2240,6 +2414,9 @@ out:
}
ocontext_destroy(newc, OCON_FSUSE);
+ if (rc)
+ pr_err("SELinux: invalid genfs\n");
+
return rc;
}
@@ -2248,7 +2425,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;
@@ -2312,11 +2489,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;
@@ -2434,6 +2625,8 @@ static int ocontext_read(struct policydb *p,
}
rc = 0;
out:
+ if (rc)
+ pr_err("SELinux: invalid ocon\n");
return rc;
}
@@ -2448,7 +2641,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;
@@ -2574,6 +2767,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;
@@ -2593,6 +2793,10 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
p->symtab[i].nprim = nprim;
}
+ rc = policydb_index(p);
+ if (rc)
+ goto bad;
+
rc = -EINVAL;
p->process_class = string_to_security_class(p, "process");
if (!p->process_class) {
@@ -2604,6 +2808,8 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto bad;
+ avtab_hash_eval(&p->te_avtab, "rules");
+
if (p->policyvers >= POLICYDB_VERSION_BOOL) {
rc = cond_read_list(p, fp);
if (rc)
@@ -2615,6 +2821,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;
@@ -2640,7 +2851,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;
@@ -2692,10 +2907,6 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto bad;
- rc = policydb_index(p);
- if (rc)
- goto bad;
-
rc = -EINVAL;
perm = string_to_av_perm(p, p->process_class, "transition");
if (!perm) {
@@ -2740,6 +2951,11 @@ int policydb_read(struct policydb *p, struct policy_file *fp)
if (rc)
goto bad;
}
+
+ rc = -EINVAL;
+ if (ebitmap_get_highest_set_bit(e) >= p->p_types.nprim)
+ goto bad;
+
/* add the type itself as the degenerate case */
rc = ebitmap_set_bit(e, i, 1);
if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 89a180b1742f..974180b2e3a3 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 */
@@ -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 */
};
@@ -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 {
@@ -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 */
@@ -139,7 +140,7 @@ struct cat_datum {
struct range_trans {
u32 source_type;
u32 target_type;
- u32 target_class;
+ u16 target_class;
};
/* Boolean data type */
@@ -195,7 +196,7 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 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) */
@@ -321,10 +322,13 @@ 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_type_isvalid(struct policydb *p, unsigned int type);
-extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
+extern bool policydb_context_isvalid(const struct policydb *p,
+ const struct context *c);
+extern bool policydb_class_isvalid(const struct policydb *p, u16 class);
+extern bool policydb_type_isvalid(const struct policydb *p, u32 type);
+extern bool policydb_simpletype_isvalid(const struct policydb *p, u32 type);
+extern bool policydb_role_isvalid(const struct policydb *p, u32 role);
+extern bool policydb_user_isvalid(const struct policydb *p, u32 user);
extern int policydb_read(struct policydb *p, struct policy_file *fp);
extern int policydb_write(struct policydb *p, struct policy_file *fp);
@@ -354,6 +358,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)
@@ -382,15 +400,29 @@ static inline int put_entry(const void *buf, size_t bytes, size_t num,
return 0;
}
-static inline char *sym_name(struct policydb *p, unsigned int sym_num,
+static inline const char *sym_name(const struct policydb *p, unsigned int sym_num,
unsigned int element_nr)
{
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 143021c5e326..2d828548f3db 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;
@@ -463,10 +461,10 @@ static void security_dump_masked_av(struct policydb *policydb,
struct common_datum *common_dat;
struct class_datum *tclass_dat;
struct audit_buffer *ab;
- char *tclass_name;
+ const 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)
@@ -717,6 +715,9 @@ static void context_struct_compute_av(struct policydb *policydb,
* If the given source and target types have boundary
* constraint, lazy checks have to mask any violated
* permission and notice it to userspace via audit.
+ *
+ * Infinite recursion is avoided via a depth pre-check in
+ * type_bounds_sanity_check().
*/
type_attribute_bounds_av(policydb, scontext, tcontext,
tclass, avd);
@@ -3288,7 +3289,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])
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
index 832660fd84a9..a756554e7f1d 100644
--- a/security/selinux/ss/symtab.c
+++ b/security/selinux/ss/symtab.c
@@ -50,7 +50,7 @@ int symtab_insert(struct symtab *s, char *name, void *datum)
return hashtab_insert(&s->table, name, datum, symtab_key_params);
}
-void *symtab_search(struct symtab *s, const char *name)
+void *symtab_search(const struct symtab *s, const char *name)
{
return hashtab_search(&s->table, name, symtab_key_params);
}
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h
index 8e667cdbf38f..7cfa3b44953a 100644
--- a/security/selinux/ss/symtab.h
+++ b/security/selinux/ss/symtab.h
@@ -21,6 +21,6 @@ struct symtab {
int symtab_init(struct symtab *s, u32 size);
int symtab_insert(struct symtab *s, char *name, void *datum);
-void *symtab_search(struct symtab *s, const char *name);
+void *symtab_search(const struct symtab *s, const char *name);
#endif /* _SS_SYMTAB_H_ */