selinux: convert part of the sym_val_to_name array to use flex_array
The sym_val_to_name type array can be quite large as it grows linearly with
the number of types. With known policies having over 5k types these
allocations are growing large enough that they are likely to fail. Convert
those to flex_array so no allocation is larger than PAGE_SIZE
Signed-off-by: Eric Paris <eparis@redhat.com>
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 655fe1c..c3f845c 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -193,6 +193,7 @@
{
struct policydb *p;
struct cond_bool_datum *booldatum;
+ struct flex_array *fa;
booldatum = datum;
p = datap;
@@ -200,7 +201,10 @@
if (!booldatum->value || booldatum->value > p->p_bools.nprim)
return -EINVAL;
- p->p_bool_val_to_name[booldatum->value - 1] = key;
+ fa = p->sym_val_to_name[SYM_BOOLS];
+ if (flex_array_put_ptr(fa, booldatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
p->bool_val_to_struct[booldatum->value - 1] = booldatum;
return 0;
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index b4eff7a..1ef8e4e 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -45,7 +45,7 @@
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
int index_sens = context->range.level[l].sens;
- len += strlen(policydb.p_sens_val_to_name[index_sens - 1]);
+ len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1));
/* categories */
head = -2;
@@ -55,17 +55,17 @@
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
- nm = policydb.p_cat_val_to_name[prev];
+ nm = sym_name(&policydb, SYM_CATS, prev);
len += strlen(nm) + 1;
}
- nm = policydb.p_cat_val_to_name[i];
+ nm = sym_name(&policydb, SYM_CATS, i);
len += strlen(nm) + 1;
head = i;
}
prev = i;
}
if (prev != head) {
- nm = policydb.p_cat_val_to_name[prev];
+ nm = sym_name(&policydb, SYM_CATS, prev);
len += strlen(nm) + 1;
}
if (l == 0) {
@@ -102,8 +102,8 @@
scontextp++;
for (l = 0; l < 2; l++) {
- strcpy(scontextp,
- policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+ strcpy(scontextp, sym_name(&policydb, SYM_LEVELS,
+ context->range.level[l].sens - 1));
scontextp += strlen(scontextp);
/* categories */
@@ -118,7 +118,7 @@
*scontextp++ = '.';
else
*scontextp++ = ',';
- nm = policydb.p_cat_val_to_name[prev];
+ nm = sym_name(&policydb, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@@ -126,7 +126,7 @@
*scontextp++ = ':';
else
*scontextp++ = ',';
- nm = policydb.p_cat_val_to_name[i];
+ nm = sym_name(&policydb, SYM_CATS, i);
strcpy(scontextp, nm);
scontextp += strlen(nm);
head = i;
@@ -139,7 +139,7 @@
*scontextp++ = '.';
else
*scontextp++ = ',';
- nm = policydb.p_cat_val_to_name[prev];
+ nm = sym_name(&policydb, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@@ -166,7 +166,7 @@
if (!l->sens || l->sens > p->p_levels.nprim)
return 0;
levdatum = hashtab_search(p->p_levels.table,
- p->p_sens_val_to_name[l->sens - 1]);
+ sym_name(p, SYM_LEVELS, l->sens - 1));
if (!levdatum)
return 0;
@@ -482,7 +482,8 @@
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
- oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
+ sym_name(oldp, SYM_LEVELS,
+ c->range.level[l].sens - 1));
if (!levdatum)
return -EINVAL;
@@ -493,7 +494,7 @@
int rc;
catdatum = hashtab_search(newp->p_cats.table,
- oldp->p_cat_val_to_name[i]);
+ sym_name(oldp, SYM_CATS, i));
if (!catdatum)
return -EINVAL;
rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index af41fdf..5adca67 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -254,12 +254,17 @@
{
struct policydb *p;
struct common_datum *comdatum;
+ struct flex_array *fa;
comdatum = datum;
p = datap;
if (!comdatum->value || comdatum->value > p->p_commons.nprim)
return -EINVAL;
- p->p_common_val_to_name[comdatum->value - 1] = key;
+
+ fa = p->sym_val_to_name[SYM_COMMONS];
+ if (flex_array_put_ptr(fa, comdatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
return 0;
}
@@ -267,12 +272,16 @@
{
struct policydb *p;
struct class_datum *cladatum;
+ struct flex_array *fa;
cladatum = datum;
p = datap;
if (!cladatum->value || cladatum->value > p->p_classes.nprim)
return -EINVAL;
- p->p_class_val_to_name[cladatum->value - 1] = key;
+ fa = p->sym_val_to_name[SYM_CLASSES];
+ if (flex_array_put_ptr(fa, cladatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
p->class_val_to_struct[cladatum->value - 1] = cladatum;
return 0;
}
@@ -281,6 +290,7 @@
{
struct policydb *p;
struct role_datum *role;
+ struct flex_array *fa;
role = datum;
p = datap;
@@ -288,7 +298,11 @@
|| role->value > p->p_roles.nprim
|| role->bounds > p->p_roles.nprim)
return -EINVAL;
- p->p_role_val_to_name[role->value - 1] = key;
+
+ fa = p->sym_val_to_name[SYM_ROLES];
+ if (flex_array_put_ptr(fa, role->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
p->role_val_to_struct[role->value - 1] = role;
return 0;
}
@@ -297,6 +311,7 @@
{
struct policydb *p;
struct type_datum *typdatum;
+ struct flex_array *fa;
typdatum = datum;
p = datap;
@@ -306,10 +321,13 @@
|| typdatum->value > p->p_types.nprim
|| typdatum->bounds > p->p_types.nprim)
return -EINVAL;
- p->p_type_val_to_name[typdatum->value - 1] = key;
- /* this flex array was all preallocated, this cannot fail */
- if (flex_array_put_ptr(p->type_val_to_struct_array,
- typdatum->value - 1, typdatum,
+ fa = p->sym_val_to_name[SYM_TYPES];
+ if (flex_array_put_ptr(fa, typdatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
+
+ fa = p->type_val_to_struct_array;
+ if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum,
GFP_KERNEL | __GFP_ZERO))
BUG();
}
@@ -321,6 +339,7 @@
{
struct policydb *p;
struct user_datum *usrdatum;
+ struct flex_array *fa;
usrdatum = datum;
p = datap;
@@ -328,7 +347,11 @@
|| usrdatum->value > p->p_users.nprim
|| usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
- p->p_user_val_to_name[usrdatum->value - 1] = key;
+
+ fa = p->sym_val_to_name[SYM_USERS];
+ if (flex_array_put_ptr(fa, usrdatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
return 0;
}
@@ -337,6 +360,7 @@
{
struct policydb *p;
struct level_datum *levdatum;
+ struct flex_array *fa;
levdatum = datum;
p = datap;
@@ -345,7 +369,10 @@
if (!levdatum->level->sens ||
levdatum->level->sens > p->p_levels.nprim)
return -EINVAL;
- p->p_sens_val_to_name[levdatum->level->sens - 1] = key;
+ fa = p->sym_val_to_name[SYM_LEVELS];
+ if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
}
return 0;
@@ -355,6 +382,7 @@
{
struct policydb *p;
struct cat_datum *catdatum;
+ struct flex_array *fa;
catdatum = datum;
p = datap;
@@ -362,7 +390,10 @@
if (!catdatum->isalias) {
if (!catdatum->value || catdatum->value > p->p_cats.nprim)
return -EINVAL;
- p->p_cat_val_to_name[catdatum->value - 1] = key;
+ fa = p->sym_val_to_name[SYM_CATS];
+ if (flex_array_put_ptr(fa, catdatum->value - 1, key,
+ GFP_KERNEL | __GFP_ZERO))
+ BUG();
}
return 0;
@@ -392,9 +423,16 @@
int rc;
rc = -ENOMEM;
- p->p_common_val_to_name =
- kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL);
- if (!p->p_common_val_to_name)
+ p->sym_val_to_name[SYM_COMMONS] = flex_array_alloc(sizeof(char *),
+ p->p_commons.nprim,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!p->sym_val_to_name[SYM_COMMONS])
+ goto out;
+
+ rc = flex_array_prealloc(p->sym_val_to_name[SYM_COMMONS],
+ 0, p->p_commons.nprim - 1,
+ GFP_KERNEL | __GFP_ZERO);
+ if (rc)
goto out;
rc = hashtab_map(p->p_commons.table, common_index, p);
@@ -408,9 +446,16 @@
goto out;
rc = -ENOMEM;
- p->p_class_val_to_name =
- kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL);
- if (!p->p_class_val_to_name)
+ p->sym_val_to_name[SYM_CLASSES] = flex_array_alloc(sizeof(char *),
+ p->p_classes.nprim,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!p->sym_val_to_name[SYM_CLASSES])
+ goto out;
+
+ rc = flex_array_prealloc(p->sym_val_to_name[SYM_CLASSES],
+ 0, p->p_classes.nprim - 1,
+ GFP_KERNEL | __GFP_ZERO);
+ if (rc)
goto out;
rc = hashtab_map(p->p_classes.table, class_index, p);
@@ -507,10 +552,18 @@
for (i = SYM_ROLES; i < SYM_NUM; i++) {
rc = -ENOMEM;
- p->sym_val_to_name[i] =
- kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL);
+ p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
+ p->symtab[i].nprim,
+ GFP_KERNEL | __GFP_ZERO);
if (!p->sym_val_to_name[i])
goto out;
+
+ rc = flex_array_prealloc(p->sym_val_to_name[i],
+ 0, p->symtab[i].nprim - 1,
+ GFP_KERNEL | __GFP_ZERO);
+ if (rc)
+ goto out;
+
rc = hashtab_map(p->symtab[i].table, index_f[i], p);
if (rc)
goto out;
@@ -703,8 +756,10 @@
hashtab_destroy(p->symtab[i].table);
}
- for (i = 0; i < SYM_NUM; i++)
- kfree(p->sym_val_to_name[i]);
+ for (i = 0; i < SYM_NUM; i++) {
+ if (p->sym_val_to_name[i])
+ flex_array_free(p->sym_val_to_name[i]);
+ }
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
@@ -1566,9 +1621,9 @@
printk(KERN_ERR
"SELinux: boundary violated policy: "
"user=%s role=%s bounds=%s\n",
- p->p_user_val_to_name[user->value - 1],
- p->p_role_val_to_name[bit],
- p->p_user_val_to_name[upper->value - 1]);
+ sym_name(p, SYM_USERS, user->value - 1),
+ sym_name(p, SYM_ROLES, bit),
+ sym_name(p, SYM_USERS, upper->value - 1));
return -EINVAL;
}
@@ -1603,9 +1658,9 @@
printk(KERN_ERR
"SELinux: boundary violated policy: "
"role=%s type=%s bounds=%s\n",
- p->p_role_val_to_name[role->value - 1],
- p->p_type_val_to_name[bit],
- p->p_role_val_to_name[upper->value - 1]);
+ sym_name(p, SYM_ROLES, role->value - 1),
+ sym_name(p, SYM_TYPES, bit),
+ sym_name(p, SYM_ROLES, upper->value - 1));
return -EINVAL;
}
@@ -1637,7 +1692,7 @@
printk(KERN_ERR "SELinux: type %s: "
"bounded by attribute %s",
(char *) key,
- p->p_type_val_to_name[upper->value - 1]);
+ sym_name(p, SYM_TYPES, upper->value - 1));
return -EINVAL;
}
}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 9826a92..4e3ab9d 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -203,15 +203,7 @@
#define p_cats symtab[SYM_CATS]
/* symbol names indexed by (value - 1) */
- char **sym_val_to_name[SYM_NUM];
-#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
-#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
-#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
-#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
-#define p_user_val_to_name sym_val_to_name[SYM_USERS]
-#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
-#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
-#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
+ struct flex_array *sym_val_to_name[SYM_NUM];
/* class, role, and user attributes indexed by (value - 1) */
struct class_datum **class_val_to_struct;
@@ -321,6 +313,13 @@
return 0;
}
+static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr)
+{
+ struct flex_array *fa = p->sym_val_to_name[sym_num];
+
+ return flex_array_get_ptr(fa, element_nr);
+}
+
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);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index afcbc19..a03cfaf 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -464,7 +464,7 @@
if (!permissions)
return;
- tclass_name = policydb.p_class_val_to_name[tclass - 1];
+ tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1);
tclass_dat = policydb.class_val_to_struct[tclass - 1];
common_dat = tclass_dat->comdatum;
@@ -716,7 +716,7 @@
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_validate_transition: denied for"
" oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
- o, n, t, policydb.p_class_val_to_name[tclass-1]);
+ o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
out:
kfree(o);
kfree(n);
@@ -1012,9 +1012,9 @@
}
/* Compute the size of the context. */
- *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
- *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
- *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ *scontext_len += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1;
+ *scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1;
+ *scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1;
*scontext_len += mls_compute_context_len(context);
if (!scontext)
@@ -1030,12 +1030,12 @@
* Copy the user name, role name and type name into the context.
*/
sprintf(scontextp, "%s:%s:%s",
- policydb.p_user_val_to_name[context->user - 1],
- policydb.p_role_val_to_name[context->role - 1],
- policydb.p_type_val_to_name[context->type - 1]);
- scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
- 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
- 1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
+ sym_name(&policydb, SYM_USERS, context->user - 1),
+ sym_name(&policydb, SYM_ROLES, context->role - 1),
+ sym_name(&policydb, SYM_TYPES, context->type - 1));
+ scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) +
+ 1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) +
+ 1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1));
mls_sid_to_context(context, &scontextp);
@@ -1333,7 +1333,7 @@
" for scontext=%s"
" tcontext=%s"
" tclass=%s",
- n, s, t, policydb.p_class_val_to_name[tclass-1]);
+ n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
out:
kfree(s);
kfree(t);
@@ -1654,7 +1654,7 @@
/* Convert the user. */
rc = -EINVAL;
usrdatum = hashtab_search(args->newp->p_users.table,
- args->oldp->p_user_val_to_name[c->user - 1]);
+ sym_name(args->oldp, SYM_USERS, c->user - 1));
if (!usrdatum)
goto bad;
c->user = usrdatum->value;
@@ -1662,7 +1662,7 @@
/* Convert the role. */
rc = -EINVAL;
role = hashtab_search(args->newp->p_roles.table,
- args->oldp->p_role_val_to_name[c->role - 1]);
+ sym_name(args->oldp, SYM_ROLES, c->role - 1));
if (!role)
goto bad;
c->role = role->value;
@@ -1670,7 +1670,7 @@
/* Convert the type. */
rc = -EINVAL;
typdatum = hashtab_search(args->newp->p_types.table,
- args->oldp->p_type_val_to_name[c->type - 1]);
+ sym_name(args->oldp, SYM_TYPES, c->type - 1));
if (!typdatum)
goto bad;
c->type = typdatum->value;
@@ -2326,14 +2326,14 @@
size_t name_len;
(*values)[i] = policydb.bool_val_to_struct[i]->state;
- name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
+ name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1;
rc = -ENOMEM;
(*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
- strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
+ strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len);
(*names)[i][name_len - 1] = 0;
}
rc = 0;
@@ -2368,7 +2368,7 @@
audit_log(current->audit_context, GFP_ATOMIC,
AUDIT_MAC_CONFIG_CHANGE,
"bool=%s val=%d old_val=%d auid=%u ses=%u",
- policydb.p_bool_val_to_name[i],
+ sym_name(&policydb, SYM_BOOLS, i),
!!values[i],
policydb.bool_val_to_struct[i]->state,
audit_get_loginuid(current),
@@ -3132,7 +3132,7 @@
goto out;
rc = -ENOMEM;
- secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+ secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1),
GFP_ATOMIC);
if (secattr->domain == NULL)
goto out;