Re: [PATCH] selinux: store role transitions in a hash table

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Apr 6, 2020 at 6:35 PM Ondrej Mosnacek <omosnace@xxxxxxxxxx> wrote:
> Currently, they are stored in a linked list, which adds significant
> overhead to security_transition_sid(). On Fedora, with 428 role
> transitions in policy, converting this list to a hash table cuts down
> its run time by about 50%. This was measured by running 'stress-ng -t 30
> --metrics-brief --msg 0' under perf with and without this patch.
>
> Signed-off-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx>
> ---
>  security/selinux/ss/policydb.c | 138 ++++++++++++++++++++++-----------
>  security/selinux/ss/policydb.h |   8 +-
>  security/selinux/ss/services.c |  21 +++--
>  3 files changed, 107 insertions(+), 60 deletions(-)
>
> diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> index 70ecdc78efbd..4f0cfffd008d 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -352,6 +352,13 @@ static int range_tr_destroy(void *key, void *datum, void *p)
>         return 0;
>  }
>
> +static int role_tr_destroy(void *key, void *datum, void *p)
> +{
> +       kfree(key);
> +       kfree(datum);
> +       return 0;
> +}
> +
>  static void ocontext_destroy(struct ocontext *c, int i)
>  {
>         if (!c)
> @@ -458,6 +465,30 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
>         return v;
>  }
>
> +static u32 role_trans_hash(struct hashtab *h, const void *k)
> +{
> +       const struct role_trans_key *key = k;
> +
> +       return (key->role + (key->type << 3) + (key->tclass << 5)) &
> +               (h->size - 1);
> +}
> +
> +static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2)
> +{
> +       const struct role_trans_key *key1 = k1, *key2 = k2;
> +       int v;
> +
> +       v = key1->role - key2->role;
> +       if (v)
> +               return v;
> +
> +       v = key1->type - key2->type;
> +       if (v)
> +               return v;
> +
> +       return key1->tclass - key2->tclass;
> +}
> +
>  /*
>   * Initialize a policy database structure.
>   */
> @@ -728,7 +759,6 @@ void policydb_destroy(struct policydb *p)
>         struct genfs *g, *gtmp;
>         int i;
>         struct role_allow *ra, *lra = NULL;
> -       struct role_trans *tr, *ltr = NULL;
>
>         for (i = 0; i < SYM_NUM; i++) {
>                 cond_resched();
> @@ -775,12 +805,8 @@ void policydb_destroy(struct policydb *p)
>
>         cond_policydb_destroy(p);
>
> -       for (tr = p->role_tr; tr; tr = tr->next) {
> -               cond_resched();
> -               kfree(ltr);
> -               ltr = tr;
> -       }
> -       kfree(ltr);
> +       hashtab_map(p->role_tr, role_tr_destroy, NULL);
> +       hashtab_destroy(p->role_tr);
>
>         for (ra = p->role_allow; ra; ra = ra->next) {
>                 cond_resched();
> @@ -2251,7 +2277,8 @@ out:
>  int policydb_read(struct policydb *p, void *fp)
>  {
>         struct role_allow *ra, *lra;
> -       struct role_trans *tr, *ltr;
> +       struct role_trans_key *rtk = NULL;
> +       struct role_trans_datum *rtd = NULL;
>         int i, j, rc;
>         __le32 buf[4];
>         u32 len, nprim, nel;
> @@ -2416,39 +2443,50 @@ int policydb_read(struct policydb *p, void *fp)
>         if (rc)
>                 goto bad;
>         nel = le32_to_cpu(buf[0]);
> -       ltr = NULL;
> +
> +       p->role_tr = hashtab_create(role_trans_hash, role_trans_cmp, nel);
> +       if (!p->role_tr)
> +               goto bad;
>         for (i = 0; i < nel; i++) {
>                 rc = -ENOMEM;
> -               tr = kzalloc(sizeof(*tr), GFP_KERNEL);
> -               if (!tr)
> +               rtk = kmalloc(sizeof(*rtk), GFP_KERNEL);
> +               if (!rtk)
>                         goto bad;
> -               if (ltr)
> -                       ltr->next = tr;
> -               else
> -                       p->role_tr = tr;
> +
> +               rc = -ENOMEM;
> +               rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
> +               if (!rtd)
> +                       goto bad;
> +
>                 rc = next_entry(buf, fp, sizeof(u32)*3);
>                 if (rc)
>                         goto bad;
>
>                 rc = -EINVAL;
> -               tr->role = le32_to_cpu(buf[0]);
> -               tr->type = le32_to_cpu(buf[1]);
> -               tr->new_role = le32_to_cpu(buf[2]);
> +               rtk->role = le32_to_cpu(buf[0]);
> +               rtk->type = le32_to_cpu(buf[1]);
> +               rtd->new_role = le32_to_cpu(buf[2]);
>                 if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
>                         rc = next_entry(buf, fp, sizeof(u32));
>                         if (rc)
>                                 goto bad;
> -                       tr->tclass = le32_to_cpu(buf[0]);
> +                       rtk->tclass = le32_to_cpu(buf[0]);
>                 } else
> -                       tr->tclass = p->process_class;
> +                       rtk->tclass = p->process_class;
>
>                 rc = -EINVAL;
> -               if (!policydb_role_isvalid(p, tr->role) ||
> -                   !policydb_type_isvalid(p, tr->type) ||
> -                   !policydb_class_isvalid(p, tr->tclass) ||
> -                   !policydb_role_isvalid(p, tr->new_role))
> +               if (!policydb_role_isvalid(p, rtk->role) ||
> +                   !policydb_type_isvalid(p, rtk->type) ||
> +                   !policydb_class_isvalid(p, rtk->tclass) ||
> +                   !policydb_role_isvalid(p, rtd->new_role))
> +                       goto bad;
> +
> +               rc = hashtab_insert(p->role_tr, rtk, rtd);
> +               if (rc)
>                         goto bad;
> -               ltr = tr;
> +
> +               rtk = NULL;
> +               rtd = NULL;
>         }
>
>         rc = next_entry(buf, fp, sizeof(u32));
> @@ -2536,6 +2574,8 @@ int policydb_read(struct policydb *p, void *fp)
>  out:
>         return rc;
>  bad:
> +       kfree(rtk);
> +       kfree(rtd);
>         policydb_destroy(p);
>         goto out;
>  }
> @@ -2653,39 +2693,45 @@ static int cat_write(void *vkey, void *datum, void *ptr)
>         return 0;
>  }
>
> -static int role_trans_write(struct policydb *p, void *fp)
> +static int role_trans_write_one(void *key, void *datum, void *ptr)
>  {
> -       struct role_trans *r = p->role_tr;
> -       struct role_trans *tr;
> +       struct role_trans_key *rtk = key;
> +       struct role_trans_datum *rtd = datum;
> +       struct policy_data *pd = ptr;
> +       void *fp = pd->fp;
> +       struct policydb *p = pd->p;
>         __le32 buf[3];
> -       size_t nel;
>         int rc;
>
> -       nel = 0;
> -       for (tr = r; tr; tr = tr->next)
> -               nel++;
> -       buf[0] = cpu_to_le32(nel);
> -       rc = put_entry(buf, sizeof(u32), 1, fp);
> +       buf[0] = cpu_to_le32(rtk->role);
> +       buf[1] = cpu_to_le32(rtk->type);
> +       buf[2] = cpu_to_le32(rtd->new_role);
> +       rc = put_entry(buf, sizeof(u32), 3, fp);
>         if (rc)
>                 return rc;
> -       for (tr = r; tr; tr = tr->next) {
> -               buf[0] = cpu_to_le32(tr->role);
> -               buf[1] = cpu_to_le32(tr->type);
> -               buf[2] = cpu_to_le32(tr->new_role);
> -               rc = put_entry(buf, sizeof(u32), 3, fp);
> +       if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
> +               buf[0] = cpu_to_le32(rtk->tclass);
> +               rc = put_entry(buf, sizeof(u32), 1, fp);
>                 if (rc)
>                         return rc;
> -               if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
> -                       buf[0] = cpu_to_le32(tr->tclass);
> -                       rc = put_entry(buf, sizeof(u32), 1, fp);
> -                       if (rc)
> -                               return rc;
> -               }
>         }
> -
>         return 0;
>  }
>
> +static int role_trans_write(struct policydb *p, void *fp)
> +{
> +       struct policy_data pd = { .p = p, .fp = fp };
> +       __le32 buf[1];
> +       int rc;
> +
> +       buf[0] = cpu_to_le32(p->role_tr->nel);
> +       rc = put_entry(buf, sizeof(u32), 1, fp);
> +       if (rc)
> +               return rc;
> +
> +       return hashtab_map(p->role_tr, role_trans_write_one, &pd);
> +}
> +
>  static int role_allow_write(struct role_allow *r, void *fp)
>  {
>         struct role_allow *ra;
> diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
> index 72e2932fb12d..d3adb522d3f3 100644
> --- a/security/selinux/ss/policydb.h
> +++ b/security/selinux/ss/policydb.h
> @@ -81,12 +81,14 @@ struct role_datum {
>         struct ebitmap types;           /* set of authorized types for role */
>  };
>
> -struct role_trans {
> +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 */
> +};
> +
> +struct role_trans_datum {
>         u32 new_role;           /* new role */
> -       struct role_trans *next;
>  };
>
>  struct filename_trans_key {
> @@ -261,7 +263,7 @@ struct policydb {
>         struct avtab te_avtab;
>
>         /* role transitions */
> -       struct role_trans *role_tr;
> +       struct hashtab *role_tr;
>
>         /* file transitions with the last path component */
>         /* quickly exclude lookups when parent ttype has no rules */
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index 8ad34fd031d1..3cb8ec7b09a3 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -1731,7 +1731,6 @@ static int security_compute_sid(struct selinux_state *state,
>         struct class_datum *cladatum = NULL;
>         struct context *scontext, *tcontext, newcontext;
>         struct sidtab_entry *sentry, *tentry;
> -       struct role_trans *roletr = NULL;
>         struct avtab_key avkey;
>         struct avtab_datum *avdatum;
>         struct avtab_node *node;
> @@ -1864,16 +1863,16 @@ static int security_compute_sid(struct selinux_state *state,
>         /* Check for class-specific changes. */
>         if (specified & AVTAB_TRANSITION) {
>                 /* Look for a role transition rule. */
> -               for (roletr = policydb->role_tr; roletr;
> -                    roletr = roletr->next) {
> -                       if ((roletr->role == scontext->role) &&
> -                           (roletr->type == tcontext->type) &&
> -                           (roletr->tclass == tclass)) {
> -                               /* Use the role transition rule. */
> -                               newcontext.role = roletr->new_role;
> -                               break;
> -                       }
> -               }
> +               struct role_trans_datum *rtd;
> +               struct role_trans_key rtk = {
> +                       .role = scontext->role,
> +                       .type = scontext->type,

I just spotted a copy-paste bug here - the type should come from
tcontext, not scontext... Will send a fixed version tomorrow.

> +                       .tclass = tclass,
> +               };
> +
> +               rtd = hashtab_search(policydb->role_tr, &rtk);
> +               if (rtd)
> +                       newcontext.role = rtd->new_role;
>         }
>
>         /* Set the MLS attributes.
> --
> 2.25.1
>


-- 
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux