diff mbox series

[v2,1/2] libsepol,checkpolicy: optimize storage of filename transitions

Message ID 20200328124550.199568-2-omosnace@redhat.com (mailing list archive)
State Superseded
Headers show
Series userspace: Implement new format of filename trans rules | expand

Commit Message

Ondrej Mosnacek March 28, 2020, 12:45 p.m. UTC
In preparation to support a new policy format with a more optimal
representation of filename transition rules, this patch applies an
equivalent change from kernel commit c3a276111ea2 ("selinux: optimize
storage of filename transitions").

See the kernel commit's description [1] for the rationale behind this
representation. This change doesn't bring any measurable difference of
policy build performance (semodule -B) on Fedora.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/commit/?id=c3a276111ea2572399281988b3129683e2a6b60b

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
---
 checkpolicy/policy_define.c                |  52 +++------
 checkpolicy/test/dispol.c                  |  27 +++--
 libsepol/cil/src/cil_binary.c              |  29 ++---
 libsepol/include/sepol/policydb/policydb.h |  15 ++-
 libsepol/src/expand.c                      |  60 +++-------
 libsepol/src/kernel_to_cil.c               |  24 +++-
 libsepol/src/kernel_to_conf.c              |  24 +++-
 libsepol/src/policydb.c                    | 126 ++++++++++++++-------
 libsepol/src/write.c                       |  46 ++++----
 9 files changed, 223 insertions(+), 180 deletions(-)

Comments

James Carter March 31, 2020, 8:30 p.m. UTC | #1
On Sat, Mar 28, 2020 at 8:46 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> In preparation to support a new policy format with a more optimal
> representation of filename transition rules, this patch applies an
> equivalent change from kernel commit c3a276111ea2 ("selinux: optimize
> storage of filename transitions").
>
> See the kernel commit's description [1] for the rationale behind this
> representation. This change doesn't bring any measurable difference of
> policy build performance (semodule -B) on Fedora.
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/commit/?id=c3a276111ea2572399281988b3129683e2a6b60b
>
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>

Acked-by: James Carter <jwcart2@gmail.com>

> ---
>  checkpolicy/policy_define.c                |  52 +++------
>  checkpolicy/test/dispol.c                  |  27 +++--
>  libsepol/cil/src/cil_binary.c              |  29 ++---
>  libsepol/include/sepol/policydb/policydb.h |  15 ++-
>  libsepol/src/expand.c                      |  60 +++-------
>  libsepol/src/kernel_to_cil.c               |  24 +++-
>  libsepol/src/kernel_to_conf.c              |  24 +++-
>  libsepol/src/policydb.c                    | 126 ++++++++++++++-------
>  libsepol/src/write.c                       |  46 ++++----
>  9 files changed, 223 insertions(+), 180 deletions(-)
>
> diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> index c6733fa4..01a90438 100644
> --- a/checkpolicy/policy_define.c
> +++ b/checkpolicy/policy_define.c
> @@ -3303,10 +3303,9 @@ int define_filename_trans(void)
>         ebitmap_t e_stypes, e_ttypes;
>         ebitmap_t e_tclasses;
>         ebitmap_node_t *snode, *tnode, *cnode;
> -       filename_trans_t *ft;
> -       filename_trans_datum_t *ftdatum;
>         filename_trans_rule_t *ftr;
>         type_datum_t *typdatum;
> +       char *dup_name;
>         uint32_t otype;
>         unsigned int c, s, t;
>         int add, rc;
> @@ -3388,40 +3387,21 @@ int define_filename_trans(void)
>         ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) {
>                 ebitmap_for_each_positive_bit(&e_stypes, snode, s) {
>                         ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) {
> -                               ft = calloc(1, sizeof(*ft));
> -                               if (!ft) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -                               ft->stype = s+1;
> -                               ft->ttype = t+1;
> -                               ft->tclass = c+1;
> -                               ft->name = strdup(name);
> -                               if (!ft->name) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -
> -                               ftdatum = hashtab_search(policydbp->filename_trans,
> -                                                        (hashtab_key_t)ft);
> -                               if (ftdatum) {
> -                                       yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> -                                                name,
> -                                                policydbp->p_type_val_to_name[s],
> -                                                policydbp->p_type_val_to_name[t],
> -                                                policydbp->p_class_val_to_name[c]);
> -                                       goto bad;
> -                               }
> -
> -                               ftdatum = calloc(1, sizeof(*ftdatum));
> -                               if (!ftdatum) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -                               rc = hashtab_insert(policydbp->filename_trans,
> -                                                   (hashtab_key_t)ft,
> -                                                   ftdatum);
> -                               if (rc) {
> +                               dup_name = NULL;
> +                               rc = policydb_filetrans_insert(
> +                                       policydbp, s+1, t+1, c+1, name,
> +                                       &dup_name, otype, NULL
> +                               );
> +                               free(dup_name);
> +                               if (rc != SEPOL_OK) {
> +                                       if (rc == SEPOL_EEXIST) {
> +                                               yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> +                                                       name,
> +                                                       policydbp->p_type_val_to_name[s],
> +                                                       policydbp->p_type_val_to_name[t],
> +                                                       policydbp->p_class_val_to_name[c]);
> +                                               goto bad;
> +                                       }
>                                         yyerror("out of memory");
>                                         goto bad;
>                                 }
> diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
> index d72d9fb3..7c74e9cf 100644
> --- a/checkpolicy/test/dispol.c
> +++ b/checkpolicy/test/dispol.c
> @@ -329,26 +329,39 @@ static void display_role_trans(policydb_t *p, FILE *fp)
>  struct filenametr_display_args {
>         policydb_t *p;
>         FILE *fp;
> +       filename_trans_key_t *ft;
>  };
>
> -static int filenametr_display(hashtab_key_t key,
> -                             hashtab_datum_t datum,
> -                             void *ptr)
> +static int filenametr_display_sub(hashtab_key_t key,
> +                                 hashtab_datum_t datum,
> +                                 void *ptr)
>  {
> -       struct filename_trans *ft = (struct filename_trans *)key;
> -       struct filename_trans_datum *ftdatum = datum;
>         struct filenametr_display_args *args = ptr;
> +       filename_trans_key_t *ft = args->ft;
>         policydb_t *p = args->p;
>         FILE *fp = args->fp;
> +       uint32_t stype = (uintptr_t)key;
> +       uint32_t otype = (uintptr_t)datum;
>
> -       display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
> +       display_id(p, fp, SYM_TYPES, stype - 1, "");
>         display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
>         display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
> -       display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, "");
> +       display_id(p, fp, SYM_TYPES, otype - 1, "");
>         fprintf(fp, " %s\n", ft->name);
>         return 0;
>  }
>
> +static int filenametr_display(hashtab_key_t key,
> +                             hashtab_datum_t datum,
> +                             void *ptr)
> +{
> +       struct filenametr_display_args *args = ptr;
> +       hashtab_t subhash = datum;
> +
> +       args->ft = (filename_trans_key_t *)key;
> +       return hashtab_map(subhash, filenametr_display_sub, args);
> +}
> +
>
>  static void display_filename_trans(policydb_t *p, FILE *fp)
>  {
> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> index 62178d99..bedff628 100644
> --- a/libsepol/cil/src/cil_binary.c
> +++ b/libsepol/cil/src/cil_binary.c
> @@ -1131,13 +1131,13 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>         class_datum_t *sepol_obj = NULL;
>         struct cil_list *class_list;
>         type_datum_t *sepol_result = NULL;
> -       filename_trans_t *newkey = NULL;
> -       filename_trans_datum_t *newdatum = NULL, *otype = NULL;
>         ebitmap_t src_bitmap, tgt_bitmap;
>         ebitmap_node_t *node1, *node2;
>         unsigned int i, j;
> +       uint32_t otype;
>         struct cil_list_item *c;
>         char *name = DATUM(typetrans->name)->name;
> +       char *dup_name;
>
>         if (name == CIL_KEY_STAR) {
>                 struct cil_type_rule trans;
> @@ -1176,22 +1176,16 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>                                 rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj);
>                                 if (rc != SEPOL_OK) goto exit;
>
> -                               newkey = cil_calloc(1, sizeof(*newkey));
> -                               newdatum = cil_calloc(1, sizeof(*newdatum));
> -                               newkey->stype = sepol_src->s.value;
> -                               newkey->ttype = sepol_tgt->s.value;
> -                               newkey->tclass = sepol_obj->s.value;
> -                               newkey->name = cil_strdup(name);
> -                               newdatum->otype = sepol_result->s.value;
> -
> -                               rc = hashtab_insert(pdb->filename_trans,
> -                                                   (hashtab_key_t)newkey,
> -                                                   newdatum);
> +                               dup_name = NULL;
> +                               rc = policydb_filetrans_insert(
> +                                       pdb, sepol_src->s.value, sepol_tgt->s.value,
> +                                       sepol_obj->s.value, name, &dup_name,
> +                                       sepol_result->s.value, &otype
> +                               );
> +                               free(dup_name);
>                                 if (rc != SEPOL_OK) {
>                                         if (rc == SEPOL_EEXIST) {
> -                                               otype = hashtab_search(pdb->filename_trans,
> -                                                               (hashtab_key_t)newkey);
> -                                               if (newdatum->otype != otype->otype) {
> +                                               if (sepol_result->s.value!= otype) {
>                                                         cil_log(CIL_ERR, "Conflicting name type transition rules\n");
>                                                 } else {
>                                                         rc = SEPOL_OK;
> @@ -1199,9 +1193,6 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>                                         } else {
>                                                 cil_log(CIL_ERR, "Out of memory\n");
>                                         }
> -                                       free(newkey->name);
> -                                       free(newkey);
> -                                       free(newdatum);
>                                         if (rc != SEPOL_OK) {
>                                                 goto exit;
>                                         }
> diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
> index 81b63fef..c3180c61 100644
> --- a/libsepol/include/sepol/policydb/policydb.h
> +++ b/libsepol/include/sepol/policydb/policydb.h
> @@ -162,15 +162,16 @@ typedef struct role_allow {
>  } role_allow_t;
>
>  /* filename_trans rules */
> -typedef struct filename_trans {
> -       uint32_t stype;
> +typedef struct filename_trans_key {
>         uint32_t ttype;
>         uint32_t tclass;
>         char *name;
> -} filename_trans_t;
> +} filename_trans_key_t;
>
>  typedef struct filename_trans_datum {
> -       uint32_t otype;         /* expected of new object */
> +       ebitmap_t stypes;
> +       uint32_t otype;
> +       struct filename_trans_datum *next;
>  } filename_trans_datum_t;
>
>  /* Type attributes */
> @@ -591,6 +592,7 @@ typedef struct policydb {
>
>         /* file transitions with the last path component */
>         hashtab_t filename_trans;
> +       uint32_t filename_trans_count;
>
>         ebitmap_t *type_attr_map;
>
> @@ -650,6 +652,11 @@ extern int policydb_load_isids(policydb_t * p, sidtab_t * s);
>
>  extern int policydb_sort_ocontexts(policydb_t *p);
>
> +extern int policydb_filetrans_insert(policydb_t *p, uint32_t stype,
> +                                    uint32_t ttype, uint32_t tclass,
> +                                    const char *name, char **name_alloc,
> +                                    uint32_t otype, uint32_t *present_otype);
> +
>  /* Deprecated */
>  extern int policydb_context_isvalid(const policydb_t * p,
>                                     const context_struct_t * c);
> diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> index 529e1d35..28f93acb 100644
> --- a/libsepol/src/expand.c
> +++ b/libsepol/src/expand.c
> @@ -1371,16 +1371,15 @@ static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules)
>  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
>  {
>         unsigned int i, j;
> -       filename_trans_t key, *new_trans;
> -       filename_trans_datum_t *otype;
>         filename_trans_rule_t *cur_rule;
>         ebitmap_t stypes, ttypes;
>         ebitmap_node_t *snode, *tnode;
> +       char *name;
>         int rc;
>
>         cur_rule = rules;
>         while (cur_rule) {
> -               uint32_t mapped_otype;
> +               uint32_t mapped_otype, present_otype;
>
>                 ebitmap_init(&stypes);
>                 ebitmap_init(&ttypes);
> @@ -1401,15 +1400,17 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
>
>                 ebitmap_for_each_positive_bit(&stypes, snode, i) {
>                         ebitmap_for_each_positive_bit(&ttypes, tnode, j) {
> -                               key.stype = i + 1;
> -                               key.ttype = j + 1;
> -                               key.tclass = cur_rule->tclass;
> -                               key.name = cur_rule->name;
> -                               otype = hashtab_search(state->out->filename_trans,
> -                                                      (hashtab_key_t) &key);
> -                               if (otype) {
> +                               name = NULL;
> +
> +                               rc = policydb_filetrans_insert(
> +                                       state->out, i + 1, j + 1,
> +                                       cur_rule->tclass, cur_rule->name,
> +                                       &name, mapped_otype, &present_otype
> +                               );
> +                               free(name);
> +                               if (rc == SEPOL_EEXIST) {
>                                         /* duplicate rule, ignore */
> -                                       if (otype->otype == mapped_otype)
> +                                       if (present_otype == mapped_otype)
>                                                 continue;
>
>                                         ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
> @@ -1417,44 +1418,11 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
>                                             state->out->p_type_val_to_name[j],
>                                             state->out->p_class_val_to_name[cur_rule->tclass - 1],
>                                             cur_rule->name,
> -                                           state->out->p_type_val_to_name[otype->otype - 1],
> +                                           state->out->p_type_val_to_name[present_otype - 1],
>                                             state->out->p_type_val_to_name[mapped_otype - 1]);
>                                         return -1;
> -                               }
> -
> -                               new_trans = calloc(1, sizeof(*new_trans));
> -                               if (!new_trans) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       return -1;
> -                               }
> -
> -                               new_trans->name = strdup(cur_rule->name);
> -                               if (!new_trans->name) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       free(new_trans);
> -                                       return -1;
> -                               }
> -                               new_trans->stype = i + 1;
> -                               new_trans->ttype = j + 1;
> -                               new_trans->tclass = cur_rule->tclass;
> -
> -                               otype = calloc(1, sizeof(*otype));
> -                               if (!otype) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       free(new_trans->name);
> -                                       free(new_trans);
> -                                       return -1;
> -                               }
> -                               otype->otype = mapped_otype;
> -
> -                               rc = hashtab_insert(state->out->filename_trans,
> -                                                   (hashtab_key_t)new_trans,
> -                                                   otype);
> -                               if (rc) {
> +                               } else if (rc < 0) {
>                                         ERR(state->handle, "Out of memory!");
> -                                       free(otype);
> -                                       free(new_trans->name);
> -                                       free(new_trans);
>                                         return -1;
>                                 }
>                         }
> diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> index ede78a20..718d3481 100644
> --- a/libsepol/src/kernel_to_cil.c
> +++ b/libsepol/src/kernel_to_cil.c
> @@ -1821,21 +1821,35 @@ struct map_filename_trans_args {
>
>  static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
>  {
> -       filename_trans_t *ft = (filename_trans_t *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
>         filename_trans_datum_t *datum = data;
>         struct map_filename_trans_args *map_args = arg;
>         struct policydb *pdb = map_args->pdb;
>         struct strs *strs = map_args->strs;
>         char *src, *tgt, *class, *filename, *new;
> +       struct ebitmap_node *node;
> +       uint32_t bit;
> +       int rc;
>
> -       src = pdb->p_type_val_to_name[ft->stype - 1];
>         tgt = pdb->p_type_val_to_name[ft->ttype - 1];
>         class = pdb->p_class_val_to_name[ft->tclass - 1];
>         filename = ft->name;
> -       new =  pdb->p_type_val_to_name[datum->otype - 1];
> +       do {
> +               new = pdb->p_type_val_to_name[datum->otype - 1];
> +
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       src = pdb->p_type_val_to_name[bit];
> +                       rc = strs_create_and_add(strs,
> +                                                "(typetransition %s %s %s %s %s)",
> +                                                5, src, tgt, class, filename, new);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
> -       return strs_create_and_add(strs, "(typetransition %s %s %s %s %s)", 5,
> -                                  src, tgt, class, filename, new);
> +       return 0;
>  }
>
>  static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb)
> diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
> index 9de64832..9fb3ed22 100644
> --- a/libsepol/src/kernel_to_conf.c
> +++ b/libsepol/src/kernel_to_conf.c
> @@ -1801,21 +1801,35 @@ struct map_filename_trans_args {
>
>  static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
>  {
> -       filename_trans_t *ft = (filename_trans_t *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
>         filename_trans_datum_t *datum = data;
>         struct map_filename_trans_args *map_args = arg;
>         struct policydb *pdb = map_args->pdb;
>         struct strs *strs = map_args->strs;
>         char *src, *tgt, *class, *filename, *new;
> +       struct ebitmap_node *node;
> +       uint32_t bit;
> +       int rc;
>
> -       src = pdb->p_type_val_to_name[ft->stype - 1];
>         tgt = pdb->p_type_val_to_name[ft->ttype - 1];
>         class = pdb->p_class_val_to_name[ft->tclass - 1];
>         filename = ft->name;
> -       new =  pdb->p_type_val_to_name[datum->otype - 1];
> +       do {
> +               new = pdb->p_type_val_to_name[datum->otype - 1];
> +
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       src = pdb->p_type_val_to_name[bit];
> +                       rc = strs_create_and_add(strs,
> +                                                "type_transition %s %s:%s %s \"%s\";",
> +                                                5, src, tgt, class, new, filename);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
> -       return strs_create_and_add(strs, "type_transition %s %s:%s %s \"%s\";", 5,
> -                                  src, tgt, class, new, filename);
> +       return 0;
>  }
>
>  static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb)
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index 5b289a52..6b121d66 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -789,12 +789,12 @@ partial_name_hash(unsigned long c, unsigned long prevhash)
>
>  static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
>  {
> -       const struct filename_trans *ft = (const struct filename_trans *)k;
> +       const filename_trans_key_t *ft = (const filename_trans_key_t *)k;
>         unsigned long hash;
>         unsigned int byte_num;
>         unsigned char focus;
>
> -       hash = ft->stype ^ ft->ttype ^ ft->tclass;
> +       hash = ft->ttype ^ ft->tclass;
>
>         byte_num = 0;
>         while ((focus = ft->name[byte_num++]))
> @@ -805,14 +805,10 @@ static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
>  static int filenametr_cmp(hashtab_t h __attribute__ ((unused)),
>                           const_hashtab_key_t k1, const_hashtab_key_t k2)
>  {
> -       const struct filename_trans *ft1 = (const struct filename_trans *)k1;
> -       const struct filename_trans *ft2 = (const struct filename_trans *)k2;
> +       const filename_trans_key_t *ft1 = (const filename_trans_key_t *)k1;
> +       const filename_trans_key_t *ft2 = (const filename_trans_key_t *)k2;
>         int v;
>
> -       v = ft1->stype - ft2->stype;
> -       if (v)
> -               return v;
> -
>         v = ft1->ttype - ft2->ttype;
>         if (v)
>                 return v;
> @@ -1409,9 +1405,12 @@ common_destroy, class_destroy, role_destroy, type_destroy, user_destroy,
>  static int filenametr_destroy(hashtab_key_t key, hashtab_datum_t datum,
>                               void *p __attribute__ ((unused)))
>  {
> -       struct filename_trans *ft = (struct filename_trans *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
> +       filename_trans_datum_t *fd = datum;
> +
>         free(ft->name);
>         free(key);
> +       ebitmap_destroy(&fd->stypes);
>         free(datum);
>         return 0;
>  }
> @@ -2595,12 +2594,77 @@ int role_allow_read(role_allow_t ** r, struct policy_file *fp)
>         return 0;
>  }
>
> +int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype,
> +                             uint32_t tclass, const char *name,
> +                             char **name_alloc, uint32_t otype,
> +                             uint32_t *present_otype)
> +{
> +       filename_trans_key_t *ft, key;
> +       filename_trans_datum_t *datum, *last;
> +
> +       key.ttype = ttype;
> +       key.tclass = tclass;
> +       key.name = (char *)name;
> +
> +       last = NULL;
> +       datum = hashtab_search(p->filename_trans, (hashtab_key_t)&key);
> +       while (datum) {
> +               if (ebitmap_get_bit(&datum->stypes, stype - 1)) {
> +                       if (present_otype)
> +                               *present_otype = datum->otype;
> +                       return SEPOL_EEXIST;
> +               }
> +               if (datum->otype == otype)
> +                       break;
> +               last = datum;
> +               datum = datum->next;
> +       }
> +       if (!datum) {
> +               if (!*name_alloc) {
> +                       *name_alloc = strdup(name);
> +                       if (!*name_alloc)
> +                               return SEPOL_ENOMEM;
> +               }
> +
> +               datum = malloc(sizeof(*datum));
> +               if (!datum)
> +                       return SEPOL_ENOMEM;
> +
> +               ebitmap_init(&datum->stypes);
> +               datum->otype = otype;
> +               datum->next = NULL;
> +
> +               if (last) {
> +                       last->next = datum;
> +               } else {
> +                       ft = malloc(sizeof(*ft));
> +                       if (!ft) {
> +                               free(datum);
> +                               return SEPOL_ENOMEM;
> +                       }
> +
> +                       ft->ttype = ttype;
> +                       ft->tclass = tclass;
> +                       ft->name = *name_alloc;
> +
> +                       if (hashtab_insert(p->filename_trans, (hashtab_key_t)ft,
> +                                          (hashtab_datum_t)datum)) {
> +                               free(datum);
> +                               free(ft);
> +                               return SEPOL_ENOMEM;
> +                       }
> +                       *name_alloc = NULL;
> +               }
> +       }
> +
> +       p->filename_trans_count++;
> +       return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
> +}
> +
>  int filename_trans_read(policydb_t *p, struct policy_file *fp)
>  {
>         unsigned int i;
> -       uint32_t buf[4], nel, len;
> -       filename_trans_t *ft;
> -       filename_trans_datum_t *otype;
> +       uint32_t buf[4], nel, len, stype, ttype, tclass, otype;
>         int rc;
>         char *name;
>
> @@ -2610,16 +2674,8 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>         nel = le32_to_cpu(buf[0]);
>
>         for (i = 0; i < nel; i++) {
> -               ft = NULL;
> -               otype = NULL;
>                 name = NULL;
>
> -               ft = calloc(1, sizeof(*ft));
> -               if (!ft)
> -                       goto err;
> -               otype = calloc(1, sizeof(*otype));
> -               if (!otype)
> -                       goto err;
>                 rc = next_entry(buf, fp, sizeof(uint32_t));
>                 if (rc < 0)
>                         goto err;
> @@ -2631,8 +2687,6 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                 if (!name)
>                         goto err;
>
> -               ft->name = name;
> -
>                 rc = next_entry(name, fp, len);
>                 if (rc < 0)
>                         goto err;
> @@ -2641,13 +2695,13 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                 if (rc < 0)
>                         goto err;
>
> -               ft->stype = le32_to_cpu(buf[0]);
> -               ft->ttype = le32_to_cpu(buf[1]);
> -               ft->tclass = le32_to_cpu(buf[2]);
> -               otype->otype = le32_to_cpu(buf[3]);
> +               stype  = le32_to_cpu(buf[0]);
> +               ttype  = le32_to_cpu(buf[1]);
> +               tclass = le32_to_cpu(buf[2]);
> +               otype  = le32_to_cpu(buf[3]);
>
> -               rc = hashtab_insert(p->filename_trans, (hashtab_key_t) ft,
> -                                   otype);
> +               rc = policydb_filetrans_insert(p, stype, ttype, tclass, name,
> +                                              &name, otype, NULL);
>                 if (rc) {
>                         if (rc != SEPOL_EEXIST)
>                                 goto err;
> @@ -2659,21 +2713,17 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                          */
>                         WARN(fp->handle,
>                              "Duplicate name-based type_transition %s %s:%s \"%s\":  %s, ignoring",
> -                            p->p_type_val_to_name[ft->stype - 1],
> -                            p->p_type_val_to_name[ft->ttype - 1],
> -                            p->p_class_val_to_name[ft->tclass - 1],
> -                            ft->name,
> -                            p->p_type_val_to_name[otype->otype - 1]);
> -                       free(ft);
> -                       free(name);
> -                       free(otype);
> +                            p->p_type_val_to_name[stype - 1],
> +                            p->p_type_val_to_name[ttype - 1],
> +                            p->p_class_val_to_name[tclass - 1],
> +                            name,
> +                            p->p_type_val_to_name[otype - 1]);
>                         /* continue, ignoring this one */
>                 }
> +               free(name);
>         }
>         return 0;
>  err:
> -       free(ft);
> -       free(otype);
>         free(name);
>         return -1;
>  }
> diff --git a/libsepol/src/write.c b/libsepol/src/write.c
> index 1fd6a16a..d3aee8d5 100644
> --- a/libsepol/src/write.c
> +++ b/libsepol/src/write.c
> @@ -571,44 +571,50 @@ static int role_allow_write(role_allow_t * r, struct policy_file *fp)
>
>  static int filename_write_helper(hashtab_key_t key, void *data, void *ptr)
>  {
> -       uint32_t buf[4];
> +       uint32_t bit, buf[4];
>         size_t items, len;
> -       struct filename_trans *ft = (struct filename_trans *)key;
> -       struct filename_trans_datum *otype = data;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
> +       filename_trans_datum_t *datum = data;
> +       ebitmap_node_t *node;
>         void *fp = ptr;
>
>         len = strlen(ft->name);
> -       buf[0] = cpu_to_le32(len);
> -       items = put_entry(buf, sizeof(uint32_t), 1, fp);
> -       if (items != 1)
> -               return POLICYDB_ERROR;
> +       do {
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       buf[0] = cpu_to_le32(len);
> +                       items = put_entry(buf, sizeof(uint32_t), 1, fp);
> +                       if (items != 1)
> +                               return POLICYDB_ERROR;
>
> -       items = put_entry(ft->name, sizeof(char), len, fp);
> -       if (items != len)
> -               return POLICYDB_ERROR;
> +                       items = put_entry(ft->name, sizeof(char), len, fp);
> +                       if (items != len)
> +                               return POLICYDB_ERROR;
>
> -       buf[0] = cpu_to_le32(ft->stype);
> -       buf[1] = cpu_to_le32(ft->ttype);
> -       buf[2] = cpu_to_le32(ft->tclass);
> -       buf[3] = cpu_to_le32(otype->otype);
> -       items = put_entry(buf, sizeof(uint32_t), 4, fp);
> -       if (items != 4)
> -               return POLICYDB_ERROR;
> +                       buf[0] = cpu_to_le32(bit + 1);
> +                       buf[1] = cpu_to_le32(ft->ttype);
> +                       buf[2] = cpu_to_le32(ft->tclass);
> +                       buf[3] = cpu_to_le32(datum->otype);
> +                       items = put_entry(buf, sizeof(uint32_t), 4, fp);
> +                       if (items != 4)
> +                               return POLICYDB_ERROR;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
>         return 0;
>  }
>
>  static int filename_trans_write(struct policydb *p, void *fp)
>  {
> -       size_t nel, items;
> +       size_t items;
>         uint32_t buf[1];
>         int rc;
>
>         if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
>                 return 0;
>
> -       nel =  p->filename_trans->nel;
> -       buf[0] = cpu_to_le32(nel);
> +       buf[0] = cpu_to_le32(p->filename_trans_count);
>         items = put_entry(buf, sizeof(uint32_t), 1, fp);
>         if (items != 1)
>                 return POLICYDB_ERROR;
> --
> 2.25.1
>
James Carter April 30, 2020, 5:34 p.m. UTC | #2
On Sat, Mar 28, 2020 at 8:46 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> In preparation to support a new policy format with a more optimal
> representation of filename transition rules, this patch applies an
> equivalent change from kernel commit c3a276111ea2 ("selinux: optimize
> storage of filename transitions").
>
> See the kernel commit's description [1] for the rationale behind this
> representation. This change doesn't bring any measurable difference of
> policy build performance (semodule -B) on Fedora.
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/commit/?id=c3a276111ea2572399281988b3129683e2a6b60b
>
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
>  checkpolicy/policy_define.c                |  52 +++------
>  checkpolicy/test/dispol.c                  |  27 +++--
>  libsepol/cil/src/cil_binary.c              |  29 ++---
>  libsepol/include/sepol/policydb/policydb.h |  15 ++-
>  libsepol/src/expand.c                      |  60 +++-------
>  libsepol/src/kernel_to_cil.c               |  24 +++-
>  libsepol/src/kernel_to_conf.c              |  24 +++-
>  libsepol/src/policydb.c                    | 126 ++++++++++++++-------
>  libsepol/src/write.c                       |  46 ++++----
>  9 files changed, 223 insertions(+), 180 deletions(-)
>
> diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> index c6733fa4..01a90438 100644
> --- a/checkpolicy/policy_define.c
> +++ b/checkpolicy/policy_define.c
> @@ -3303,10 +3303,9 @@ int define_filename_trans(void)
>         ebitmap_t e_stypes, e_ttypes;
>         ebitmap_t e_tclasses;
>         ebitmap_node_t *snode, *tnode, *cnode;
> -       filename_trans_t *ft;
> -       filename_trans_datum_t *ftdatum;
>         filename_trans_rule_t *ftr;
>         type_datum_t *typdatum;
> +       char *dup_name;
>         uint32_t otype;
>         unsigned int c, s, t;
>         int add, rc;
> @@ -3388,40 +3387,21 @@ int define_filename_trans(void)
>         ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) {
>                 ebitmap_for_each_positive_bit(&e_stypes, snode, s) {
>                         ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) {
> -                               ft = calloc(1, sizeof(*ft));
> -                               if (!ft) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -                               ft->stype = s+1;
> -                               ft->ttype = t+1;
> -                               ft->tclass = c+1;
> -                               ft->name = strdup(name);
> -                               if (!ft->name) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -
> -                               ftdatum = hashtab_search(policydbp->filename_trans,
> -                                                        (hashtab_key_t)ft);
> -                               if (ftdatum) {
> -                                       yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> -                                                name,
> -                                                policydbp->p_type_val_to_name[s],
> -                                                policydbp->p_type_val_to_name[t],
> -                                                policydbp->p_class_val_to_name[c]);
> -                                       goto bad;
> -                               }
> -
> -                               ftdatum = calloc(1, sizeof(*ftdatum));
> -                               if (!ftdatum) {
> -                                       yyerror("out of memory");
> -                                       goto bad;
> -                               }
> -                               rc = hashtab_insert(policydbp->filename_trans,
> -                                                   (hashtab_key_t)ft,
> -                                                   ftdatum);
> -                               if (rc) {
> +                               dup_name = NULL;
> +                               rc = policydb_filetrans_insert(
> +                                       policydbp, s+1, t+1, c+1, name,
> +                                       &dup_name, otype, NULL
> +                               );
> +                               free(dup_name);
> +                               if (rc != SEPOL_OK) {
> +                                       if (rc == SEPOL_EEXIST) {
> +                                               yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> +                                                       name,
> +                                                       policydbp->p_type_val_to_name[s],
> +                                                       policydbp->p_type_val_to_name[t],
> +                                                       policydbp->p_class_val_to_name[c]);
> +                                               goto bad;
> +                                       }
>                                         yyerror("out of memory");
>                                         goto bad;
>                                 }
> diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
> index d72d9fb3..7c74e9cf 100644
> --- a/checkpolicy/test/dispol.c
> +++ b/checkpolicy/test/dispol.c
> @@ -329,26 +329,39 @@ static void display_role_trans(policydb_t *p, FILE *fp)
>  struct filenametr_display_args {
>         policydb_t *p;
>         FILE *fp;
> +       filename_trans_key_t *ft;
>  };
>
> -static int filenametr_display(hashtab_key_t key,
> -                             hashtab_datum_t datum,
> -                             void *ptr)
> +static int filenametr_display_sub(hashtab_key_t key,
> +                                 hashtab_datum_t datum,
> +                                 void *ptr)
>  {
> -       struct filename_trans *ft = (struct filename_trans *)key;
> -       struct filename_trans_datum *ftdatum = datum;
>         struct filenametr_display_args *args = ptr;
> +       filename_trans_key_t *ft = args->ft;
>         policydb_t *p = args->p;
>         FILE *fp = args->fp;
> +       uint32_t stype = (uintptr_t)key;
> +       uint32_t otype = (uintptr_t)datum;
>
> -       display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
> +       display_id(p, fp, SYM_TYPES, stype - 1, "");
>         display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
>         display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
> -       display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, "");
> +       display_id(p, fp, SYM_TYPES, otype - 1, "");
>         fprintf(fp, " %s\n", ft->name);
>         return 0;
>  }
>
> +static int filenametr_display(hashtab_key_t key,
> +                             hashtab_datum_t datum,
> +                             void *ptr)
> +{
> +       struct filenametr_display_args *args = ptr;
> +       hashtab_t subhash = datum;
> +
> +       args->ft = (filename_trans_key_t *)key;
> +       return hashtab_map(subhash, filenametr_display_sub, args);
> +}
> +
>

Because of the discussion about setools, I've been playing some with
dispol and I am getting segfaults with your patch when trying to print
out the filename transitions.

Looking closer at dispol, this doesn't make sense. The function
display_filename_trans(), which hasn't changed, passes the function
filenametr_display() to hashtab_map(), but then filenametr_display()
takes the datum passed to it to call hashtab_map() again (this time
with filenametr_display_sub()). The datum is a filename_trans_datum_t
which doesn't have a hashtab in it.

Jim


>  static void display_filename_trans(policydb_t *p, FILE *fp)
>  {
> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> index 62178d99..bedff628 100644
> --- a/libsepol/cil/src/cil_binary.c
> +++ b/libsepol/cil/src/cil_binary.c
> @@ -1131,13 +1131,13 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>         class_datum_t *sepol_obj = NULL;
>         struct cil_list *class_list;
>         type_datum_t *sepol_result = NULL;
> -       filename_trans_t *newkey = NULL;
> -       filename_trans_datum_t *newdatum = NULL, *otype = NULL;
>         ebitmap_t src_bitmap, tgt_bitmap;
>         ebitmap_node_t *node1, *node2;
>         unsigned int i, j;
> +       uint32_t otype;
>         struct cil_list_item *c;
>         char *name = DATUM(typetrans->name)->name;
> +       char *dup_name;
>
>         if (name == CIL_KEY_STAR) {
>                 struct cil_type_rule trans;
> @@ -1176,22 +1176,16 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>                                 rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj);
>                                 if (rc != SEPOL_OK) goto exit;
>
> -                               newkey = cil_calloc(1, sizeof(*newkey));
> -                               newdatum = cil_calloc(1, sizeof(*newdatum));
> -                               newkey->stype = sepol_src->s.value;
> -                               newkey->ttype = sepol_tgt->s.value;
> -                               newkey->tclass = sepol_obj->s.value;
> -                               newkey->name = cil_strdup(name);
> -                               newdatum->otype = sepol_result->s.value;
> -
> -                               rc = hashtab_insert(pdb->filename_trans,
> -                                                   (hashtab_key_t)newkey,
> -                                                   newdatum);
> +                               dup_name = NULL;
> +                               rc = policydb_filetrans_insert(
> +                                       pdb, sepol_src->s.value, sepol_tgt->s.value,
> +                                       sepol_obj->s.value, name, &dup_name,
> +                                       sepol_result->s.value, &otype
> +                               );
> +                               free(dup_name);
>                                 if (rc != SEPOL_OK) {
>                                         if (rc == SEPOL_EEXIST) {
> -                                               otype = hashtab_search(pdb->filename_trans,
> -                                                               (hashtab_key_t)newkey);
> -                                               if (newdatum->otype != otype->otype) {
> +                                               if (sepol_result->s.value!= otype) {
>                                                         cil_log(CIL_ERR, "Conflicting name type transition rules\n");
>                                                 } else {
>                                                         rc = SEPOL_OK;
> @@ -1199,9 +1193,6 @@ int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
>                                         } else {
>                                                 cil_log(CIL_ERR, "Out of memory\n");
>                                         }
> -                                       free(newkey->name);
> -                                       free(newkey);
> -                                       free(newdatum);
>                                         if (rc != SEPOL_OK) {
>                                                 goto exit;
>                                         }
> diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
> index 81b63fef..c3180c61 100644
> --- a/libsepol/include/sepol/policydb/policydb.h
> +++ b/libsepol/include/sepol/policydb/policydb.h
> @@ -162,15 +162,16 @@ typedef struct role_allow {
>  } role_allow_t;
>
>  /* filename_trans rules */
> -typedef struct filename_trans {
> -       uint32_t stype;
> +typedef struct filename_trans_key {
>         uint32_t ttype;
>         uint32_t tclass;
>         char *name;
> -} filename_trans_t;
> +} filename_trans_key_t;
>
>  typedef struct filename_trans_datum {
> -       uint32_t otype;         /* expected of new object */
> +       ebitmap_t stypes;
> +       uint32_t otype;
> +       struct filename_trans_datum *next;
>  } filename_trans_datum_t;
>
>  /* Type attributes */
> @@ -591,6 +592,7 @@ typedef struct policydb {
>
>         /* file transitions with the last path component */
>         hashtab_t filename_trans;
> +       uint32_t filename_trans_count;
>
>         ebitmap_t *type_attr_map;
>
> @@ -650,6 +652,11 @@ extern int policydb_load_isids(policydb_t * p, sidtab_t * s);
>
>  extern int policydb_sort_ocontexts(policydb_t *p);
>
> +extern int policydb_filetrans_insert(policydb_t *p, uint32_t stype,
> +                                    uint32_t ttype, uint32_t tclass,
> +                                    const char *name, char **name_alloc,
> +                                    uint32_t otype, uint32_t *present_otype);
> +
>  /* Deprecated */
>  extern int policydb_context_isvalid(const policydb_t * p,
>                                     const context_struct_t * c);
> diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> index 529e1d35..28f93acb 100644
> --- a/libsepol/src/expand.c
> +++ b/libsepol/src/expand.c
> @@ -1371,16 +1371,15 @@ static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules)
>  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
>  {
>         unsigned int i, j;
> -       filename_trans_t key, *new_trans;
> -       filename_trans_datum_t *otype;
>         filename_trans_rule_t *cur_rule;
>         ebitmap_t stypes, ttypes;
>         ebitmap_node_t *snode, *tnode;
> +       char *name;
>         int rc;
>
>         cur_rule = rules;
>         while (cur_rule) {
> -               uint32_t mapped_otype;
> +               uint32_t mapped_otype, present_otype;
>
>                 ebitmap_init(&stypes);
>                 ebitmap_init(&ttypes);
> @@ -1401,15 +1400,17 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
>
>                 ebitmap_for_each_positive_bit(&stypes, snode, i) {
>                         ebitmap_for_each_positive_bit(&ttypes, tnode, j) {
> -                               key.stype = i + 1;
> -                               key.ttype = j + 1;
> -                               key.tclass = cur_rule->tclass;
> -                               key.name = cur_rule->name;
> -                               otype = hashtab_search(state->out->filename_trans,
> -                                                      (hashtab_key_t) &key);
> -                               if (otype) {
> +                               name = NULL;
> +
> +                               rc = policydb_filetrans_insert(
> +                                       state->out, i + 1, j + 1,
> +                                       cur_rule->tclass, cur_rule->name,
> +                                       &name, mapped_otype, &present_otype
> +                               );
> +                               free(name);
> +                               if (rc == SEPOL_EEXIST) {
>                                         /* duplicate rule, ignore */
> -                                       if (otype->otype == mapped_otype)
> +                                       if (present_otype == mapped_otype)
>                                                 continue;
>
>                                         ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
> @@ -1417,44 +1418,11 @@ static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
>                                             state->out->p_type_val_to_name[j],
>                                             state->out->p_class_val_to_name[cur_rule->tclass - 1],
>                                             cur_rule->name,
> -                                           state->out->p_type_val_to_name[otype->otype - 1],
> +                                           state->out->p_type_val_to_name[present_otype - 1],
>                                             state->out->p_type_val_to_name[mapped_otype - 1]);
>                                         return -1;
> -                               }
> -
> -                               new_trans = calloc(1, sizeof(*new_trans));
> -                               if (!new_trans) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       return -1;
> -                               }
> -
> -                               new_trans->name = strdup(cur_rule->name);
> -                               if (!new_trans->name) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       free(new_trans);
> -                                       return -1;
> -                               }
> -                               new_trans->stype = i + 1;
> -                               new_trans->ttype = j + 1;
> -                               new_trans->tclass = cur_rule->tclass;
> -
> -                               otype = calloc(1, sizeof(*otype));
> -                               if (!otype) {
> -                                       ERR(state->handle, "Out of memory!");
> -                                       free(new_trans->name);
> -                                       free(new_trans);
> -                                       return -1;
> -                               }
> -                               otype->otype = mapped_otype;
> -
> -                               rc = hashtab_insert(state->out->filename_trans,
> -                                                   (hashtab_key_t)new_trans,
> -                                                   otype);
> -                               if (rc) {
> +                               } else if (rc < 0) {
>                                         ERR(state->handle, "Out of memory!");
> -                                       free(otype);
> -                                       free(new_trans->name);
> -                                       free(new_trans);
>                                         return -1;
>                                 }
>                         }
> diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> index ede78a20..718d3481 100644
> --- a/libsepol/src/kernel_to_cil.c
> +++ b/libsepol/src/kernel_to_cil.c
> @@ -1821,21 +1821,35 @@ struct map_filename_trans_args {
>
>  static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
>  {
> -       filename_trans_t *ft = (filename_trans_t *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
>         filename_trans_datum_t *datum = data;
>         struct map_filename_trans_args *map_args = arg;
>         struct policydb *pdb = map_args->pdb;
>         struct strs *strs = map_args->strs;
>         char *src, *tgt, *class, *filename, *new;
> +       struct ebitmap_node *node;
> +       uint32_t bit;
> +       int rc;
>
> -       src = pdb->p_type_val_to_name[ft->stype - 1];
>         tgt = pdb->p_type_val_to_name[ft->ttype - 1];
>         class = pdb->p_class_val_to_name[ft->tclass - 1];
>         filename = ft->name;
> -       new =  pdb->p_type_val_to_name[datum->otype - 1];
> +       do {
> +               new = pdb->p_type_val_to_name[datum->otype - 1];
> +
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       src = pdb->p_type_val_to_name[bit];
> +                       rc = strs_create_and_add(strs,
> +                                                "(typetransition %s %s %s %s %s)",
> +                                                5, src, tgt, class, filename, new);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
> -       return strs_create_and_add(strs, "(typetransition %s %s %s %s %s)", 5,
> -                                  src, tgt, class, filename, new);
> +       return 0;
>  }
>
>  static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb)
> diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
> index 9de64832..9fb3ed22 100644
> --- a/libsepol/src/kernel_to_conf.c
> +++ b/libsepol/src/kernel_to_conf.c
> @@ -1801,21 +1801,35 @@ struct map_filename_trans_args {
>
>  static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
>  {
> -       filename_trans_t *ft = (filename_trans_t *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
>         filename_trans_datum_t *datum = data;
>         struct map_filename_trans_args *map_args = arg;
>         struct policydb *pdb = map_args->pdb;
>         struct strs *strs = map_args->strs;
>         char *src, *tgt, *class, *filename, *new;
> +       struct ebitmap_node *node;
> +       uint32_t bit;
> +       int rc;
>
> -       src = pdb->p_type_val_to_name[ft->stype - 1];
>         tgt = pdb->p_type_val_to_name[ft->ttype - 1];
>         class = pdb->p_class_val_to_name[ft->tclass - 1];
>         filename = ft->name;
> -       new =  pdb->p_type_val_to_name[datum->otype - 1];
> +       do {
> +               new = pdb->p_type_val_to_name[datum->otype - 1];
> +
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       src = pdb->p_type_val_to_name[bit];
> +                       rc = strs_create_and_add(strs,
> +                                                "type_transition %s %s:%s %s \"%s\";",
> +                                                5, src, tgt, class, new, filename);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
> -       return strs_create_and_add(strs, "type_transition %s %s:%s %s \"%s\";", 5,
> -                                  src, tgt, class, new, filename);
> +       return 0;
>  }
>
>  static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb)
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index 5b289a52..6b121d66 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -789,12 +789,12 @@ partial_name_hash(unsigned long c, unsigned long prevhash)
>
>  static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
>  {
> -       const struct filename_trans *ft = (const struct filename_trans *)k;
> +       const filename_trans_key_t *ft = (const filename_trans_key_t *)k;
>         unsigned long hash;
>         unsigned int byte_num;
>         unsigned char focus;
>
> -       hash = ft->stype ^ ft->ttype ^ ft->tclass;
> +       hash = ft->ttype ^ ft->tclass;
>
>         byte_num = 0;
>         while ((focus = ft->name[byte_num++]))
> @@ -805,14 +805,10 @@ static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
>  static int filenametr_cmp(hashtab_t h __attribute__ ((unused)),
>                           const_hashtab_key_t k1, const_hashtab_key_t k2)
>  {
> -       const struct filename_trans *ft1 = (const struct filename_trans *)k1;
> -       const struct filename_trans *ft2 = (const struct filename_trans *)k2;
> +       const filename_trans_key_t *ft1 = (const filename_trans_key_t *)k1;
> +       const filename_trans_key_t *ft2 = (const filename_trans_key_t *)k2;
>         int v;
>
> -       v = ft1->stype - ft2->stype;
> -       if (v)
> -               return v;
> -
>         v = ft1->ttype - ft2->ttype;
>         if (v)
>                 return v;
> @@ -1409,9 +1405,12 @@ common_destroy, class_destroy, role_destroy, type_destroy, user_destroy,
>  static int filenametr_destroy(hashtab_key_t key, hashtab_datum_t datum,
>                               void *p __attribute__ ((unused)))
>  {
> -       struct filename_trans *ft = (struct filename_trans *)key;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
> +       filename_trans_datum_t *fd = datum;
> +
>         free(ft->name);
>         free(key);
> +       ebitmap_destroy(&fd->stypes);
>         free(datum);
>         return 0;
>  }
> @@ -2595,12 +2594,77 @@ int role_allow_read(role_allow_t ** r, struct policy_file *fp)
>         return 0;
>  }
>
> +int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype,
> +                             uint32_t tclass, const char *name,
> +                             char **name_alloc, uint32_t otype,
> +                             uint32_t *present_otype)
> +{
> +       filename_trans_key_t *ft, key;
> +       filename_trans_datum_t *datum, *last;
> +
> +       key.ttype = ttype;
> +       key.tclass = tclass;
> +       key.name = (char *)name;
> +
> +       last = NULL;
> +       datum = hashtab_search(p->filename_trans, (hashtab_key_t)&key);
> +       while (datum) {
> +               if (ebitmap_get_bit(&datum->stypes, stype - 1)) {
> +                       if (present_otype)
> +                               *present_otype = datum->otype;
> +                       return SEPOL_EEXIST;
> +               }
> +               if (datum->otype == otype)
> +                       break;
> +               last = datum;
> +               datum = datum->next;
> +       }
> +       if (!datum) {
> +               if (!*name_alloc) {
> +                       *name_alloc = strdup(name);
> +                       if (!*name_alloc)
> +                               return SEPOL_ENOMEM;
> +               }
> +
> +               datum = malloc(sizeof(*datum));
> +               if (!datum)
> +                       return SEPOL_ENOMEM;
> +
> +               ebitmap_init(&datum->stypes);
> +               datum->otype = otype;
> +               datum->next = NULL;
> +
> +               if (last) {
> +                       last->next = datum;
> +               } else {
> +                       ft = malloc(sizeof(*ft));
> +                       if (!ft) {
> +                               free(datum);
> +                               return SEPOL_ENOMEM;
> +                       }
> +
> +                       ft->ttype = ttype;
> +                       ft->tclass = tclass;
> +                       ft->name = *name_alloc;
> +
> +                       if (hashtab_insert(p->filename_trans, (hashtab_key_t)ft,
> +                                          (hashtab_datum_t)datum)) {
> +                               free(datum);
> +                               free(ft);
> +                               return SEPOL_ENOMEM;
> +                       }
> +                       *name_alloc = NULL;
> +               }
> +       }
> +
> +       p->filename_trans_count++;
> +       return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
> +}
> +
>  int filename_trans_read(policydb_t *p, struct policy_file *fp)
>  {
>         unsigned int i;
> -       uint32_t buf[4], nel, len;
> -       filename_trans_t *ft;
> -       filename_trans_datum_t *otype;
> +       uint32_t buf[4], nel, len, stype, ttype, tclass, otype;
>         int rc;
>         char *name;
>
> @@ -2610,16 +2674,8 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>         nel = le32_to_cpu(buf[0]);
>
>         for (i = 0; i < nel; i++) {
> -               ft = NULL;
> -               otype = NULL;
>                 name = NULL;
>
> -               ft = calloc(1, sizeof(*ft));
> -               if (!ft)
> -                       goto err;
> -               otype = calloc(1, sizeof(*otype));
> -               if (!otype)
> -                       goto err;
>                 rc = next_entry(buf, fp, sizeof(uint32_t));
>                 if (rc < 0)
>                         goto err;
> @@ -2631,8 +2687,6 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                 if (!name)
>                         goto err;
>
> -               ft->name = name;
> -
>                 rc = next_entry(name, fp, len);
>                 if (rc < 0)
>                         goto err;
> @@ -2641,13 +2695,13 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                 if (rc < 0)
>                         goto err;
>
> -               ft->stype = le32_to_cpu(buf[0]);
> -               ft->ttype = le32_to_cpu(buf[1]);
> -               ft->tclass = le32_to_cpu(buf[2]);
> -               otype->otype = le32_to_cpu(buf[3]);
> +               stype  = le32_to_cpu(buf[0]);
> +               ttype  = le32_to_cpu(buf[1]);
> +               tclass = le32_to_cpu(buf[2]);
> +               otype  = le32_to_cpu(buf[3]);
>
> -               rc = hashtab_insert(p->filename_trans, (hashtab_key_t) ft,
> -                                   otype);
> +               rc = policydb_filetrans_insert(p, stype, ttype, tclass, name,
> +                                              &name, otype, NULL);
>                 if (rc) {
>                         if (rc != SEPOL_EEXIST)
>                                 goto err;
> @@ -2659,21 +2713,17 @@ int filename_trans_read(policydb_t *p, struct policy_file *fp)
>                          */
>                         WARN(fp->handle,
>                              "Duplicate name-based type_transition %s %s:%s \"%s\":  %s, ignoring",
> -                            p->p_type_val_to_name[ft->stype - 1],
> -                            p->p_type_val_to_name[ft->ttype - 1],
> -                            p->p_class_val_to_name[ft->tclass - 1],
> -                            ft->name,
> -                            p->p_type_val_to_name[otype->otype - 1]);
> -                       free(ft);
> -                       free(name);
> -                       free(otype);
> +                            p->p_type_val_to_name[stype - 1],
> +                            p->p_type_val_to_name[ttype - 1],
> +                            p->p_class_val_to_name[tclass - 1],
> +                            name,
> +                            p->p_type_val_to_name[otype - 1]);
>                         /* continue, ignoring this one */
>                 }
> +               free(name);
>         }
>         return 0;
>  err:
> -       free(ft);
> -       free(otype);
>         free(name);
>         return -1;
>  }
> diff --git a/libsepol/src/write.c b/libsepol/src/write.c
> index 1fd6a16a..d3aee8d5 100644
> --- a/libsepol/src/write.c
> +++ b/libsepol/src/write.c
> @@ -571,44 +571,50 @@ static int role_allow_write(role_allow_t * r, struct policy_file *fp)
>
>  static int filename_write_helper(hashtab_key_t key, void *data, void *ptr)
>  {
> -       uint32_t buf[4];
> +       uint32_t bit, buf[4];
>         size_t items, len;
> -       struct filename_trans *ft = (struct filename_trans *)key;
> -       struct filename_trans_datum *otype = data;
> +       filename_trans_key_t *ft = (filename_trans_key_t *)key;
> +       filename_trans_datum_t *datum = data;
> +       ebitmap_node_t *node;
>         void *fp = ptr;
>
>         len = strlen(ft->name);
> -       buf[0] = cpu_to_le32(len);
> -       items = put_entry(buf, sizeof(uint32_t), 1, fp);
> -       if (items != 1)
> -               return POLICYDB_ERROR;
> +       do {
> +               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
> +                       buf[0] = cpu_to_le32(len);
> +                       items = put_entry(buf, sizeof(uint32_t), 1, fp);
> +                       if (items != 1)
> +                               return POLICYDB_ERROR;
>
> -       items = put_entry(ft->name, sizeof(char), len, fp);
> -       if (items != len)
> -               return POLICYDB_ERROR;
> +                       items = put_entry(ft->name, sizeof(char), len, fp);
> +                       if (items != len)
> +                               return POLICYDB_ERROR;
>
> -       buf[0] = cpu_to_le32(ft->stype);
> -       buf[1] = cpu_to_le32(ft->ttype);
> -       buf[2] = cpu_to_le32(ft->tclass);
> -       buf[3] = cpu_to_le32(otype->otype);
> -       items = put_entry(buf, sizeof(uint32_t), 4, fp);
> -       if (items != 4)
> -               return POLICYDB_ERROR;
> +                       buf[0] = cpu_to_le32(bit + 1);
> +                       buf[1] = cpu_to_le32(ft->ttype);
> +                       buf[2] = cpu_to_le32(ft->tclass);
> +                       buf[3] = cpu_to_le32(datum->otype);
> +                       items = put_entry(buf, sizeof(uint32_t), 4, fp);
> +                       if (items != 4)
> +                               return POLICYDB_ERROR;
> +               }
> +
> +               datum = datum->next;
> +       } while (datum);
>
>         return 0;
>  }
>
>  static int filename_trans_write(struct policydb *p, void *fp)
>  {
> -       size_t nel, items;
> +       size_t items;
>         uint32_t buf[1];
>         int rc;
>
>         if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
>                 return 0;
>
> -       nel =  p->filename_trans->nel;
> -       buf[0] = cpu_to_le32(nel);
> +       buf[0] = cpu_to_le32(p->filename_trans_count);
>         items = put_entry(buf, sizeof(uint32_t), 1, fp);
>         if (items != 1)
>                 return POLICYDB_ERROR;
> --
> 2.25.1
>
Ondrej Mosnacek April 30, 2020, 6:18 p.m. UTC | #3
On Thu, Apr 30, 2020 at 7:35 PM James Carter <jwcart2@gmail.com> wrote:
> On Sat, Mar 28, 2020 at 8:46 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
> >
> > In preparation to support a new policy format with a more optimal
> > representation of filename transition rules, this patch applies an
> > equivalent change from kernel commit c3a276111ea2 ("selinux: optimize
> > storage of filename transitions").
> >
> > See the kernel commit's description [1] for the rationale behind this
> > representation. This change doesn't bring any measurable difference of
> > policy build performance (semodule -B) on Fedora.
> >
> > [1] https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/commit/?id=c3a276111ea2572399281988b3129683e2a6b60b
> >
> > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> > ---
> >  checkpolicy/policy_define.c                |  52 +++------
> >  checkpolicy/test/dispol.c                  |  27 +++--
> >  libsepol/cil/src/cil_binary.c              |  29 ++---
> >  libsepol/include/sepol/policydb/policydb.h |  15 ++-
> >  libsepol/src/expand.c                      |  60 +++-------
> >  libsepol/src/kernel_to_cil.c               |  24 +++-
> >  libsepol/src/kernel_to_conf.c              |  24 +++-
> >  libsepol/src/policydb.c                    | 126 ++++++++++++++-------
> >  libsepol/src/write.c                       |  46 ++++----
> >  9 files changed, 223 insertions(+), 180 deletions(-)
> >
> > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> > index c6733fa4..01a90438 100644
> > --- a/checkpolicy/policy_define.c
> > +++ b/checkpolicy/policy_define.c
> > @@ -3303,10 +3303,9 @@ int define_filename_trans(void)
> >         ebitmap_t e_stypes, e_ttypes;
> >         ebitmap_t e_tclasses;
> >         ebitmap_node_t *snode, *tnode, *cnode;
> > -       filename_trans_t *ft;
> > -       filename_trans_datum_t *ftdatum;
> >         filename_trans_rule_t *ftr;
> >         type_datum_t *typdatum;
> > +       char *dup_name;
> >         uint32_t otype;
> >         unsigned int c, s, t;
> >         int add, rc;
> > @@ -3388,40 +3387,21 @@ int define_filename_trans(void)
> >         ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) {
> >                 ebitmap_for_each_positive_bit(&e_stypes, snode, s) {
> >                         ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) {
> > -                               ft = calloc(1, sizeof(*ft));
> > -                               if (!ft) {
> > -                                       yyerror("out of memory");
> > -                                       goto bad;
> > -                               }
> > -                               ft->stype = s+1;
> > -                               ft->ttype = t+1;
> > -                               ft->tclass = c+1;
> > -                               ft->name = strdup(name);
> > -                               if (!ft->name) {
> > -                                       yyerror("out of memory");
> > -                                       goto bad;
> > -                               }
> > -
> > -                               ftdatum = hashtab_search(policydbp->filename_trans,
> > -                                                        (hashtab_key_t)ft);
> > -                               if (ftdatum) {
> > -                                       yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> > -                                                name,
> > -                                                policydbp->p_type_val_to_name[s],
> > -                                                policydbp->p_type_val_to_name[t],
> > -                                                policydbp->p_class_val_to_name[c]);
> > -                                       goto bad;
> > -                               }
> > -
> > -                               ftdatum = calloc(1, sizeof(*ftdatum));
> > -                               if (!ftdatum) {
> > -                                       yyerror("out of memory");
> > -                                       goto bad;
> > -                               }
> > -                               rc = hashtab_insert(policydbp->filename_trans,
> > -                                                   (hashtab_key_t)ft,
> > -                                                   ftdatum);
> > -                               if (rc) {
> > +                               dup_name = NULL;
> > +                               rc = policydb_filetrans_insert(
> > +                                       policydbp, s+1, t+1, c+1, name,
> > +                                       &dup_name, otype, NULL
> > +                               );
> > +                               free(dup_name);
> > +                               if (rc != SEPOL_OK) {
> > +                                       if (rc == SEPOL_EEXIST) {
> > +                                               yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
> > +                                                       name,
> > +                                                       policydbp->p_type_val_to_name[s],
> > +                                                       policydbp->p_type_val_to_name[t],
> > +                                                       policydbp->p_class_val_to_name[c]);
> > +                                               goto bad;
> > +                                       }
> >                                         yyerror("out of memory");
> >                                         goto bad;
> >                                 }
> > diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
> > index d72d9fb3..7c74e9cf 100644
> > --- a/checkpolicy/test/dispol.c
> > +++ b/checkpolicy/test/dispol.c
> > @@ -329,26 +329,39 @@ static void display_role_trans(policydb_t *p, FILE *fp)
> >  struct filenametr_display_args {
> >         policydb_t *p;
> >         FILE *fp;
> > +       filename_trans_key_t *ft;
> >  };
> >
> > -static int filenametr_display(hashtab_key_t key,
> > -                             hashtab_datum_t datum,
> > -                             void *ptr)
> > +static int filenametr_display_sub(hashtab_key_t key,
> > +                                 hashtab_datum_t datum,
> > +                                 void *ptr)
> >  {
> > -       struct filename_trans *ft = (struct filename_trans *)key;
> > -       struct filename_trans_datum *ftdatum = datum;
> >         struct filenametr_display_args *args = ptr;
> > +       filename_trans_key_t *ft = args->ft;
> >         policydb_t *p = args->p;
> >         FILE *fp = args->fp;
> > +       uint32_t stype = (uintptr_t)key;
> > +       uint32_t otype = (uintptr_t)datum;
> >
> > -       display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
> > +       display_id(p, fp, SYM_TYPES, stype - 1, "");
> >         display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
> >         display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
> > -       display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, "");
> > +       display_id(p, fp, SYM_TYPES, otype - 1, "");
> >         fprintf(fp, " %s\n", ft->name);
> >         return 0;
> >  }
> >
> > +static int filenametr_display(hashtab_key_t key,
> > +                             hashtab_datum_t datum,
> > +                             void *ptr)
> > +{
> > +       struct filenametr_display_args *args = ptr;
> > +       hashtab_t subhash = datum;
> > +
> > +       args->ft = (filename_trans_key_t *)key;
> > +       return hashtab_map(subhash, filenametr_display_sub, args);
> > +}
> > +
> >
>
> Because of the discussion about setools, I've been playing some with
> dispol and I am getting segfaults with your patch when trying to print
> out the filename transitions.
>
> Looking closer at dispol, this doesn't make sense. The function
> display_filename_trans(), which hasn't changed, passes the function
> filenametr_display() to hashtab_map(), but then filenametr_display()
> takes the datum passed to it to call hashtab_map() again (this time
> with filenametr_display_sub()). The datum is a filename_trans_datum_t
> which doesn't have a hashtab in it.

Ouch, you're right! This must be a leftover of an earlier
implementation of ftrans rules compression I was trying out before...
I must've forgotten to update the dispol code and didn't realize it,
because the damn thing still compiled... I'll have a closer look
tomorrow and get it fixed. Thanks for spotting it, it would've been
much more embarrassing if this was found after merging...
diff mbox series

Patch

diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index c6733fa4..01a90438 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -3303,10 +3303,9 @@  int define_filename_trans(void)
 	ebitmap_t e_stypes, e_ttypes;
 	ebitmap_t e_tclasses;
 	ebitmap_node_t *snode, *tnode, *cnode;
-	filename_trans_t *ft;
-	filename_trans_datum_t *ftdatum;
 	filename_trans_rule_t *ftr;
 	type_datum_t *typdatum;
+	char *dup_name;
 	uint32_t otype;
 	unsigned int c, s, t;
 	int add, rc;
@@ -3388,40 +3387,21 @@  int define_filename_trans(void)
 	ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) {
 		ebitmap_for_each_positive_bit(&e_stypes, snode, s) {
 			ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) {
-				ft = calloc(1, sizeof(*ft));
-				if (!ft) {
-					yyerror("out of memory");
-					goto bad;
-				}
-				ft->stype = s+1;
-				ft->ttype = t+1;
-				ft->tclass = c+1;
-				ft->name = strdup(name);
-				if (!ft->name) {
-					yyerror("out of memory");
-					goto bad;
-				}
-
-				ftdatum = hashtab_search(policydbp->filename_trans,
-							 (hashtab_key_t)ft);
-				if (ftdatum) {
-					yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
-						 name,
-						 policydbp->p_type_val_to_name[s],
-						 policydbp->p_type_val_to_name[t],
-						 policydbp->p_class_val_to_name[c]);
-					goto bad;
-				}
-
-				ftdatum = calloc(1, sizeof(*ftdatum));
-				if (!ftdatum) {
-					yyerror("out of memory");
-					goto bad;
-				}
-				rc = hashtab_insert(policydbp->filename_trans,
-						    (hashtab_key_t)ft,
-						    ftdatum);
-				if (rc) {
+				dup_name = NULL;
+				rc = policydb_filetrans_insert(
+					policydbp, s+1, t+1, c+1, name,
+					&dup_name, otype, NULL
+				);
+				free(dup_name);
+				if (rc != SEPOL_OK) {
+					if (rc == SEPOL_EEXIST) {
+						yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
+							name,
+							policydbp->p_type_val_to_name[s],
+							policydbp->p_type_val_to_name[t],
+							policydbp->p_class_val_to_name[c]);
+						goto bad;
+					}
 					yyerror("out of memory");
 					goto bad;
 				}
diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
index d72d9fb3..7c74e9cf 100644
--- a/checkpolicy/test/dispol.c
+++ b/checkpolicy/test/dispol.c
@@ -329,26 +329,39 @@  static void display_role_trans(policydb_t *p, FILE *fp)
 struct filenametr_display_args {
 	policydb_t *p;
 	FILE *fp;
+	filename_trans_key_t *ft;
 };
 
-static int filenametr_display(hashtab_key_t key,
-			      hashtab_datum_t datum,
-			      void *ptr)
+static int filenametr_display_sub(hashtab_key_t key,
+				  hashtab_datum_t datum,
+				  void *ptr)
 {
-	struct filename_trans *ft = (struct filename_trans *)key;
-	struct filename_trans_datum *ftdatum = datum;
 	struct filenametr_display_args *args = ptr;
+	filename_trans_key_t *ft = args->ft;
 	policydb_t *p = args->p;
 	FILE *fp = args->fp;
+	uint32_t stype = (uintptr_t)key;
+	uint32_t otype = (uintptr_t)datum;
 
-	display_id(p, fp, SYM_TYPES, ft->stype - 1, "");
+	display_id(p, fp, SYM_TYPES, stype - 1, "");
 	display_id(p, fp, SYM_TYPES, ft->ttype - 1, "");
 	display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":");
-	display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, "");
+	display_id(p, fp, SYM_TYPES, otype - 1, "");
 	fprintf(fp, " %s\n", ft->name);
 	return 0;
 }
 
+static int filenametr_display(hashtab_key_t key,
+			      hashtab_datum_t datum,
+			      void *ptr)
+{
+	struct filenametr_display_args *args = ptr;
+	hashtab_t subhash = datum;
+
+	args->ft = (filename_trans_key_t *)key;
+	return hashtab_map(subhash, filenametr_display_sub, args);
+}
+
 
 static void display_filename_trans(policydb_t *p, FILE *fp)
 {
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index 62178d99..bedff628 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -1131,13 +1131,13 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 	class_datum_t *sepol_obj = NULL;
 	struct cil_list *class_list;
 	type_datum_t *sepol_result = NULL;
-	filename_trans_t *newkey = NULL;
-	filename_trans_datum_t *newdatum = NULL, *otype = NULL;
 	ebitmap_t src_bitmap, tgt_bitmap;
 	ebitmap_node_t *node1, *node2;
 	unsigned int i, j;
+	uint32_t otype;
 	struct cil_list_item *c;
 	char *name = DATUM(typetrans->name)->name;
+	char *dup_name;
 
 	if (name == CIL_KEY_STAR) {
 		struct cil_type_rule trans;
@@ -1176,22 +1176,16 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 				rc = __cil_get_sepol_class_datum(pdb, DATUM(c->data), &sepol_obj);
 				if (rc != SEPOL_OK) goto exit;
 
-				newkey = cil_calloc(1, sizeof(*newkey));
-				newdatum = cil_calloc(1, sizeof(*newdatum));
-				newkey->stype = sepol_src->s.value;
-				newkey->ttype = sepol_tgt->s.value;
-				newkey->tclass = sepol_obj->s.value;
-				newkey->name = cil_strdup(name);
-				newdatum->otype = sepol_result->s.value;
-
-				rc = hashtab_insert(pdb->filename_trans,
-						    (hashtab_key_t)newkey,
-						    newdatum);
+				dup_name = NULL;
+				rc = policydb_filetrans_insert(
+					pdb, sepol_src->s.value, sepol_tgt->s.value,
+					sepol_obj->s.value, name, &dup_name,
+					sepol_result->s.value, &otype
+				);
+				free(dup_name);
 				if (rc != SEPOL_OK) {
 					if (rc == SEPOL_EEXIST) {
-						otype = hashtab_search(pdb->filename_trans,
-								(hashtab_key_t)newkey);
-						if (newdatum->otype != otype->otype) {
+						if (sepol_result->s.value!= otype) {
 							cil_log(CIL_ERR, "Conflicting name type transition rules\n");
 						} else {
 							rc = SEPOL_OK;
@@ -1199,9 +1193,6 @@  int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *db, stru
 					} else {
 						cil_log(CIL_ERR, "Out of memory\n");
 					}
-					free(newkey->name);
-					free(newkey);
-					free(newdatum);
 					if (rc != SEPOL_OK) {
 						goto exit;
 					}
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index 81b63fef..c3180c61 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -162,15 +162,16 @@  typedef struct role_allow {
 } role_allow_t;
 
 /* filename_trans rules */
-typedef struct filename_trans {
-	uint32_t stype;
+typedef struct filename_trans_key {
 	uint32_t ttype;
 	uint32_t tclass;
 	char *name;
-} filename_trans_t;
+} filename_trans_key_t;
 
 typedef struct filename_trans_datum {
-	uint32_t otype;		/* expected of new object */
+	ebitmap_t stypes;
+	uint32_t otype;
+	struct filename_trans_datum *next;
 } filename_trans_datum_t;
 
 /* Type attributes */
@@ -591,6 +592,7 @@  typedef struct policydb {
 
 	/* file transitions with the last path component */
 	hashtab_t filename_trans;
+	uint32_t filename_trans_count;
 
 	ebitmap_t *type_attr_map;
 
@@ -650,6 +652,11 @@  extern int policydb_load_isids(policydb_t * p, sidtab_t * s);
 
 extern int policydb_sort_ocontexts(policydb_t *p);
 
+extern int policydb_filetrans_insert(policydb_t *p, uint32_t stype,
+				     uint32_t ttype, uint32_t tclass,
+				     const char *name, char **name_alloc,
+				     uint32_t otype, uint32_t *present_otype);
+
 /* Deprecated */
 extern int policydb_context_isvalid(const policydb_t * p,
 				    const context_struct_t * c);
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index 529e1d35..28f93acb 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -1371,16 +1371,15 @@  static int copy_role_trans(expand_state_t * state, role_trans_rule_t * rules)
 static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *rules)
 {
 	unsigned int i, j;
-	filename_trans_t key, *new_trans;
-	filename_trans_datum_t *otype;
 	filename_trans_rule_t *cur_rule;
 	ebitmap_t stypes, ttypes;
 	ebitmap_node_t *snode, *tnode;
+	char *name;
 	int rc;
 
 	cur_rule = rules;
 	while (cur_rule) {
-		uint32_t mapped_otype;
+		uint32_t mapped_otype, present_otype;
 
 		ebitmap_init(&stypes);
 		ebitmap_init(&ttypes);
@@ -1401,15 +1400,17 @@  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
 
 		ebitmap_for_each_positive_bit(&stypes, snode, i) {
 			ebitmap_for_each_positive_bit(&ttypes, tnode, j) {
-				key.stype = i + 1;
-				key.ttype = j + 1;
-				key.tclass = cur_rule->tclass;
-				key.name = cur_rule->name;
-				otype = hashtab_search(state->out->filename_trans,
-						       (hashtab_key_t) &key);
-				if (otype) {
+				name = NULL;
+
+				rc = policydb_filetrans_insert(
+					state->out, i + 1, j + 1,
+					cur_rule->tclass, cur_rule->name,
+					&name, mapped_otype, &present_otype
+				);
+				free(name);
+				if (rc == SEPOL_EEXIST) {
 					/* duplicate rule, ignore */
-					if (otype->otype == mapped_otype)
+					if (present_otype == mapped_otype)
 						continue;
 
 					ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\":  %s vs %s",
@@ -1417,44 +1418,11 @@  static int expand_filename_trans(expand_state_t *state, filename_trans_rule_t *r
 					    state->out->p_type_val_to_name[j],
 					    state->out->p_class_val_to_name[cur_rule->tclass - 1],
 					    cur_rule->name,
-					    state->out->p_type_val_to_name[otype->otype - 1],
+					    state->out->p_type_val_to_name[present_otype - 1],
 					    state->out->p_type_val_to_name[mapped_otype - 1]);
 					return -1;
-				}
-
-				new_trans = calloc(1, sizeof(*new_trans));
-				if (!new_trans) {
-					ERR(state->handle, "Out of memory!");
-					return -1;
-				}
-
-				new_trans->name = strdup(cur_rule->name);
-				if (!new_trans->name) {
-					ERR(state->handle, "Out of memory!");
-					free(new_trans);
-					return -1;
-				}
-				new_trans->stype = i + 1;
-				new_trans->ttype = j + 1;
-				new_trans->tclass = cur_rule->tclass;
-
-				otype = calloc(1, sizeof(*otype));
-				if (!otype) {
-					ERR(state->handle, "Out of memory!");
-					free(new_trans->name);
-					free(new_trans);
-					return -1;
-				}
-				otype->otype = mapped_otype;
-
-				rc = hashtab_insert(state->out->filename_trans,
-						    (hashtab_key_t)new_trans,
-						    otype);
-				if (rc) {
+				} else if (rc < 0) {
 					ERR(state->handle, "Out of memory!");
-					free(otype);
-					free(new_trans->name);
-					free(new_trans);
 					return -1;
 				}
 			}
diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index ede78a20..718d3481 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1821,21 +1821,35 @@  struct map_filename_trans_args {
 
 static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
 {
-	filename_trans_t *ft = (filename_trans_t *)key;
+	filename_trans_key_t *ft = (filename_trans_key_t *)key;
 	filename_trans_datum_t *datum = data;
 	struct map_filename_trans_args *map_args = arg;
 	struct policydb *pdb = map_args->pdb;
 	struct strs *strs = map_args->strs;
 	char *src, *tgt, *class, *filename, *new;
+	struct ebitmap_node *node;
+	uint32_t bit;
+	int rc;
 
-	src = pdb->p_type_val_to_name[ft->stype - 1];
 	tgt = pdb->p_type_val_to_name[ft->ttype - 1];
 	class = pdb->p_class_val_to_name[ft->tclass - 1];
 	filename = ft->name;
-	new =  pdb->p_type_val_to_name[datum->otype - 1];
+	do {
+		new = pdb->p_type_val_to_name[datum->otype - 1];
+
+		ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
+			src = pdb->p_type_val_to_name[bit];
+			rc = strs_create_and_add(strs,
+						 "(typetransition %s %s %s %s %s)",
+						 5, src, tgt, class, filename, new);
+			if (rc)
+				return rc;
+		}
+
+		datum = datum->next;
+	} while (datum);
 
-	return strs_create_and_add(strs, "(typetransition %s %s %s %s %s)", 5,
-				   src, tgt, class, filename, new);
+	return 0;
 }
 
 static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb)
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 9de64832..9fb3ed22 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -1801,21 +1801,35 @@  struct map_filename_trans_args {
 
 static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg)
 {
-	filename_trans_t *ft = (filename_trans_t *)key;
+	filename_trans_key_t *ft = (filename_trans_key_t *)key;
 	filename_trans_datum_t *datum = data;
 	struct map_filename_trans_args *map_args = arg;
 	struct policydb *pdb = map_args->pdb;
 	struct strs *strs = map_args->strs;
 	char *src, *tgt, *class, *filename, *new;
+	struct ebitmap_node *node;
+	uint32_t bit;
+	int rc;
 
-	src = pdb->p_type_val_to_name[ft->stype - 1];
 	tgt = pdb->p_type_val_to_name[ft->ttype - 1];
 	class = pdb->p_class_val_to_name[ft->tclass - 1];
 	filename = ft->name;
-	new =  pdb->p_type_val_to_name[datum->otype - 1];
+	do {
+		new = pdb->p_type_val_to_name[datum->otype - 1];
+
+		ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
+			src = pdb->p_type_val_to_name[bit];
+			rc = strs_create_and_add(strs,
+						 "type_transition %s %s:%s %s \"%s\";",
+						 5, src, tgt, class, new, filename);
+			if (rc)
+				return rc;
+		}
+
+		datum = datum->next;
+	} while (datum);
 
-	return strs_create_and_add(strs, "type_transition %s %s:%s %s \"%s\";", 5,
-				   src, tgt, class, new, filename);
+	return 0;
 }
 
 static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb)
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 5b289a52..6b121d66 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -789,12 +789,12 @@  partial_name_hash(unsigned long c, unsigned long prevhash)
 
 static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
 {
-	const struct filename_trans *ft = (const struct filename_trans *)k;
+	const filename_trans_key_t *ft = (const filename_trans_key_t *)k;
 	unsigned long hash;
 	unsigned int byte_num;
 	unsigned char focus;
 
-	hash = ft->stype ^ ft->ttype ^ ft->tclass;
+	hash = ft->ttype ^ ft->tclass;
 
 	byte_num = 0;
 	while ((focus = ft->name[byte_num++]))
@@ -805,14 +805,10 @@  static unsigned int filenametr_hash(hashtab_t h, const_hashtab_key_t k)
 static int filenametr_cmp(hashtab_t h __attribute__ ((unused)),
 			  const_hashtab_key_t k1, const_hashtab_key_t k2)
 {
-	const struct filename_trans *ft1 = (const struct filename_trans *)k1;
-	const struct filename_trans *ft2 = (const struct filename_trans *)k2;
+	const filename_trans_key_t *ft1 = (const filename_trans_key_t *)k1;
+	const filename_trans_key_t *ft2 = (const filename_trans_key_t *)k2;
 	int v;
 
-	v = ft1->stype - ft2->stype;
-	if (v)
-		return v;
-
 	v = ft1->ttype - ft2->ttype;
 	if (v)
 		return v;
@@ -1409,9 +1405,12 @@  common_destroy, class_destroy, role_destroy, type_destroy, user_destroy,
 static int filenametr_destroy(hashtab_key_t key, hashtab_datum_t datum,
 			      void *p __attribute__ ((unused)))
 {
-	struct filename_trans *ft = (struct filename_trans *)key;
+	filename_trans_key_t *ft = (filename_trans_key_t *)key;
+	filename_trans_datum_t *fd = datum;
+
 	free(ft->name);
 	free(key);
+	ebitmap_destroy(&fd->stypes);
 	free(datum);
 	return 0;
 }
@@ -2595,12 +2594,77 @@  int role_allow_read(role_allow_t ** r, struct policy_file *fp)
 	return 0;
 }
 
+int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype,
+			      uint32_t tclass, const char *name,
+			      char **name_alloc, uint32_t otype,
+			      uint32_t *present_otype)
+{
+	filename_trans_key_t *ft, key;
+	filename_trans_datum_t *datum, *last;
+
+	key.ttype = ttype;
+	key.tclass = tclass;
+	key.name = (char *)name;
+
+	last = NULL;
+	datum = hashtab_search(p->filename_trans, (hashtab_key_t)&key);
+	while (datum) {
+		if (ebitmap_get_bit(&datum->stypes, stype - 1)) {
+			if (present_otype)
+				*present_otype = datum->otype;
+			return SEPOL_EEXIST;
+		}
+		if (datum->otype == otype)
+			break;
+		last = datum;
+		datum = datum->next;
+	}
+	if (!datum) {
+		if (!*name_alloc) {
+			*name_alloc = strdup(name);
+			if (!*name_alloc)
+				return SEPOL_ENOMEM;
+		}
+
+		datum = malloc(sizeof(*datum));
+		if (!datum)
+			return SEPOL_ENOMEM;
+
+		ebitmap_init(&datum->stypes);
+		datum->otype = otype;
+		datum->next = NULL;
+
+		if (last) {
+			last->next = datum;
+		} else {
+			ft = malloc(sizeof(*ft));
+			if (!ft) {
+				free(datum);
+				return SEPOL_ENOMEM;
+			}
+
+			ft->ttype = ttype;
+			ft->tclass = tclass;
+			ft->name = *name_alloc;
+
+			if (hashtab_insert(p->filename_trans, (hashtab_key_t)ft,
+					   (hashtab_datum_t)datum)) {
+				free(datum);
+				free(ft);
+				return SEPOL_ENOMEM;
+			}
+			*name_alloc = NULL;
+		}
+	}
+
+	p->filename_trans_count++;
+	return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
+}
+
 int filename_trans_read(policydb_t *p, struct policy_file *fp)
 {
 	unsigned int i;
-	uint32_t buf[4], nel, len;
-	filename_trans_t *ft;
-	filename_trans_datum_t *otype;
+	uint32_t buf[4], nel, len, stype, ttype, tclass, otype;
 	int rc;
 	char *name;
 
@@ -2610,16 +2674,8 @@  int filename_trans_read(policydb_t *p, struct policy_file *fp)
 	nel = le32_to_cpu(buf[0]);
 
 	for (i = 0; i < nel; i++) {
-		ft = NULL;
-		otype = NULL;
 		name = NULL;
 
-		ft = calloc(1, sizeof(*ft));
-		if (!ft)
-			goto err;
-		otype = calloc(1, sizeof(*otype));
-		if (!otype)
-			goto err;
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0)
 			goto err;
@@ -2631,8 +2687,6 @@  int filename_trans_read(policydb_t *p, struct policy_file *fp)
 		if (!name)
 			goto err;
 
-		ft->name = name;
-
 		rc = next_entry(name, fp, len);
 		if (rc < 0)
 			goto err;
@@ -2641,13 +2695,13 @@  int filename_trans_read(policydb_t *p, struct policy_file *fp)
 		if (rc < 0)
 			goto err;
 
-		ft->stype = le32_to_cpu(buf[0]);
-		ft->ttype = le32_to_cpu(buf[1]);
-		ft->tclass = le32_to_cpu(buf[2]);
-		otype->otype = le32_to_cpu(buf[3]);
+		stype  = le32_to_cpu(buf[0]);
+		ttype  = le32_to_cpu(buf[1]);
+		tclass = le32_to_cpu(buf[2]);
+		otype  = le32_to_cpu(buf[3]);
 
-		rc = hashtab_insert(p->filename_trans, (hashtab_key_t) ft,
-				    otype);
+		rc = policydb_filetrans_insert(p, stype, ttype, tclass, name,
+					       &name, otype, NULL);
 		if (rc) {
 			if (rc != SEPOL_EEXIST)
 				goto err;
@@ -2659,21 +2713,17 @@  int filename_trans_read(policydb_t *p, struct policy_file *fp)
 			 */
 			WARN(fp->handle,
 			     "Duplicate name-based type_transition %s %s:%s \"%s\":  %s, ignoring",
-			     p->p_type_val_to_name[ft->stype - 1],
-			     p->p_type_val_to_name[ft->ttype - 1],
-			     p->p_class_val_to_name[ft->tclass - 1],
-			     ft->name,
-			     p->p_type_val_to_name[otype->otype - 1]);
-			free(ft);
-			free(name);
-			free(otype);
+			     p->p_type_val_to_name[stype - 1],
+			     p->p_type_val_to_name[ttype - 1],
+			     p->p_class_val_to_name[tclass - 1],
+			     name,
+			     p->p_type_val_to_name[otype - 1]);
 			/* continue, ignoring this one */
 		}
+		free(name);
 	}
 	return 0;
 err:
-	free(ft);
-	free(otype);
 	free(name);
 	return -1;
 }
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 1fd6a16a..d3aee8d5 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -571,44 +571,50 @@  static int role_allow_write(role_allow_t * r, struct policy_file *fp)
 
 static int filename_write_helper(hashtab_key_t key, void *data, void *ptr)
 {
-	uint32_t buf[4];
+	uint32_t bit, buf[4];
 	size_t items, len;
-	struct filename_trans *ft = (struct filename_trans *)key;
-	struct filename_trans_datum *otype = data;
+	filename_trans_key_t *ft = (filename_trans_key_t *)key;
+	filename_trans_datum_t *datum = data;
+	ebitmap_node_t *node;
 	void *fp = ptr;
 
 	len = strlen(ft->name);
-	buf[0] = cpu_to_le32(len);
-	items = put_entry(buf, sizeof(uint32_t), 1, fp);
-	if (items != 1)
-		return POLICYDB_ERROR;
+	do {
+		ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
+			buf[0] = cpu_to_le32(len);
+			items = put_entry(buf, sizeof(uint32_t), 1, fp);
+			if (items != 1)
+				return POLICYDB_ERROR;
 
-	items = put_entry(ft->name, sizeof(char), len, fp);
-	if (items != len)
-		return POLICYDB_ERROR;
+			items = put_entry(ft->name, sizeof(char), len, fp);
+			if (items != len)
+				return POLICYDB_ERROR;
 
-	buf[0] = cpu_to_le32(ft->stype);
-	buf[1] = cpu_to_le32(ft->ttype);
-	buf[2] = cpu_to_le32(ft->tclass);
-	buf[3] = cpu_to_le32(otype->otype);
-	items = put_entry(buf, sizeof(uint32_t), 4, fp);
-	if (items != 4)
-		return POLICYDB_ERROR;
+			buf[0] = cpu_to_le32(bit + 1);
+			buf[1] = cpu_to_le32(ft->ttype);
+			buf[2] = cpu_to_le32(ft->tclass);
+			buf[3] = cpu_to_le32(datum->otype);
+			items = put_entry(buf, sizeof(uint32_t), 4, fp);
+			if (items != 4)
+				return POLICYDB_ERROR;
+		}
+
+		datum = datum->next;
+	} while (datum);
 
 	return 0;
 }
 
 static int filename_trans_write(struct policydb *p, void *fp)
 {
-	size_t nel, items;
+	size_t items;
 	uint32_t buf[1];
 	int rc;
 
 	if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
 		return 0;
 
-	nel =  p->filename_trans->nel;
-	buf[0] = cpu_to_le32(nel);
+	buf[0] = cpu_to_le32(p->filename_trans_count);
 	items = put_entry(buf, sizeof(uint32_t), 1, fp);
 	if (items != 1)
 		return POLICYDB_ERROR;