Re: [PATCH v3] checkpolicy, libsepol: Fix potential double free of mls_level_t

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

 



On Wed, Feb 28, 2024 at 3:43 PM James Carter <jwcart2@xxxxxxxxx> wrote:
>
> In checkpolicy, a sensitivity that has one or more aliases will
> temporarily share the mls_level_t structure with its aliases until
> a level statement is processed for the sensitivity (or one of the
> aliases) and the aliases are updated to have their own mls_level_t
> structure. If the policydb is destroyed while they are sharing the
> mls_level_t structure, then a double free of the shared mls_level_t
> will occur. This does not currently occur only because checkpolicy
> does very little clean-up before exiting.
>
> The "defined" field of the level_datum_t is set after a level
> statement is processed for a sensitivity and its aliases. This means
> that we know an alias has its own mls_level_t if the "defined" field
> is set. The double free can be avoided by not destroying the
> mls_leve_t structure for an alias unless the "defined" field is set.
>
> Since the "defined" field is only set to false while the mls_level_t
> structure is being shared, it would be clearer to rename the field
> as "notdefined". It would only be set during the time the sensitivity
> and its aliases are sharing the mls_level_t structure. Outside of
> checkpolicy, the "notdefined" field will always be set to 0.
>
> Also, do more validation of the level_datum_t when validating the
> policydb.
>
> Signed-off-by: James Carter <jwcart2@xxxxxxxxx>

This patch has been merged.
Jim

> ---
> v3: Add a comment about the "notdefined" field in the definition of level_datum_t.
>     Do more validation of the level_datum_t when validating the policydb.
> v2: Change the field name from "defined" to "notdefined" and change
>     the logic to match.
>
>  checkpolicy/checkpolicy.c                  |  7 ++---
>  checkpolicy/policy_define.c                | 10 ++++---
>  libsepol/cil/src/cil_binary.c              |  3 --
>  libsepol/include/sepol/policydb/policydb.h |  2 +-
>  libsepol/src/policydb.c                    |  6 ++--
>  libsepol/src/policydb_validate.c           | 35 ++++++++++++++++++----
>  6 files changed, 44 insertions(+), 19 deletions(-)
>
> diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
> index fcec6e51..d7cafaa4 100644
> --- a/checkpolicy/checkpolicy.c
> +++ b/checkpolicy/checkpolicy.c
> @@ -370,10 +370,9 @@ static int check_level(hashtab_key_t key, hashtab_datum_t datum, void *arg __att
>  {
>         level_datum_t *levdatum = (level_datum_t *) datum;
>
> -       if (!levdatum->isalias && !levdatum->defined) {
> -               fprintf(stderr,
> -                       "Error:  sensitivity %s was not used in a level definition!\n",
> -                       key);
> +       if (!levdatum->isalias && levdatum->notdefined) {
> +               fprintf(stderr, "Error:  sensitivity %s was not used in a level definition!\n",
> +                               key);
>                 return -1;
>         }
>         return 0;
> diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> index 260e609d..ac215086 100644
> --- a/checkpolicy/policy_define.c
> +++ b/checkpolicy/policy_define.c
> @@ -743,6 +743,7 @@ int define_sens(void)
>         level_datum_init(datum);
>         datum->isalias = FALSE;
>         datum->level = level;
> +       datum->notdefined = TRUE;
>
>         ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value);
>         switch (ret) {
> @@ -780,6 +781,7 @@ int define_sens(void)
>                 level_datum_init(aliasdatum);
>                 aliasdatum->isalias = TRUE;
>                 aliasdatum->level = level;
> +               aliasdatum->notdefined = TRUE;
>
>                 ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value);
>                 switch (ret) {
> @@ -1006,9 +1008,10 @@ static int clone_level(hashtab_key_t key __attribute__ ((unused)), hashtab_datum
>         mls_level_t *level = (mls_level_t *) arg, *newlevel;
>
>         if (levdatum->level == level) {
> -               levdatum->defined = 1;
> -               if (!levdatum->isalias)
> +               if (!levdatum->isalias) {
> +                       levdatum->notdefined = FALSE;
>                         return 0;
> +               }
>                 newlevel = (mls_level_t *) malloc(sizeof(mls_level_t));
>                 if (!newlevel)
>                         return -1;
> @@ -1017,6 +1020,7 @@ static int clone_level(hashtab_key_t key __attribute__ ((unused)), hashtab_datum
>                         return -1;
>                 }
>                 levdatum->level = newlevel;
> +               levdatum->notdefined = FALSE;
>         }
>         return 0;
>  }
> @@ -1057,8 +1061,6 @@ int define_level(void)
>         }
>         free(id);
>
> -       levdatum->defined = 1;
> -
>         while ((id = queue_remove(id_queue))) {
>                 cat_datum_t *cdatum;
>                 int range_start, range_end, i;
> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> index a8e3616a..95bd18ba 100644
> --- a/libsepol/cil/src/cil_binary.c
> +++ b/libsepol/cil/src/cil_binary.c
> @@ -907,7 +907,6 @@ static int cil_sensalias_to_policydb(policydb_t *pdb, struct cil_alias *cil_alia
>                 goto exit;
>         }
>         sepol_alias->level = mls_level;
> -       sepol_alias->defined = 1;
>         sepol_alias->isalias = 1;
>
>         return SEPOL_OK;
> @@ -3163,8 +3162,6 @@ int cil_sepol_level_define(policydb_t *pdb, struct cil_sens *cil_sens)
>                 }
>         }
>
> -       sepol_level->defined = 1;
> -
>         return SEPOL_OK;
>
>  exit:
> diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
> index 6682069e..56d2cb01 100644
> --- a/libsepol/include/sepol/policydb/policydb.h
> +++ b/libsepol/include/sepol/policydb/policydb.h
> @@ -217,7 +217,7 @@ typedef struct user_datum {
>  typedef struct level_datum {
>         mls_level_t *level;     /* sensitivity and associated categories */
>         unsigned char isalias;  /* is this sensitivity an alias for another? */
> -       unsigned char defined;
> +       unsigned char notdefined; /* Only set to non-zero in checkpolicy */
>  } level_datum_t;
>
>  /* Category attributes */
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index f10a8a95..0c23a7a2 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -1390,8 +1390,10 @@ static int sens_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p
>         if (key)
>                 free(key);
>         levdatum = (level_datum_t *) datum;
> -       mls_level_destroy(levdatum->level);
> -       free(levdatum->level);
> +       if (!levdatum->isalias || !levdatum->notdefined) {
> +               mls_level_destroy(levdatum->level);
> +               free(levdatum->level);
> +       }
>         level_datum_destroy(levdatum);
>         free(levdatum);
>         return 0;
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index d86f885e..1d8de44e 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -618,14 +618,39 @@ static int validate_mls_level(const mls_level_t *level, const validate_t *sens,
>         return -1;
>  }
>
> -static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
> +static int validate_level_datum(sepol_handle_t *handle, const level_datum_t *level, validate_t flavors[], const policydb_t *p)
>  {
> -       level_datum_t *level = d;
> -       validate_t *flavors = args;
> +       if (level->notdefined != 0)
> +               goto bad;
> +
> +       if (validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
> +               goto bad;
> +
> +       if (level->isalias) {
> +               const mls_level_t *l1 = level->level;
> +               const mls_level_t *l2;
> +               const level_datum_t *actual = (level_datum_t *) hashtab_search(p->p_levels.table, p->p_sens_val_to_name[l1->sens - 1]);
> +               if (!actual)
> +                       goto bad;
> +               l2 = actual->level;
> +               if (!ebitmap_cmp(&l1->cat, &l2->cat))
> +                       goto bad;
> +       }
>
> -       return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
> +       return 0;
> +
> +       bad:
> +       ERR(handle, "Invalid level datum");
> +       return -1;
>  }
>
> +static int validate_level_datum_wrapper(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
> +{
> +       map_arg_t *margs = args;
> +
> +       return validate_level_datum(margs->handle, d, margs->flavors, margs->policy);
> +}
> +
>  static int validate_mls_range(const mls_range_t *range, const validate_t *sens, const validate_t *cats)
>  {
>         if (validate_mls_level(&range->level[0], sens, cats))
> @@ -774,7 +799,7 @@ static int validate_datum_array_entries(sepol_handle_t *handle, const policydb_t
>         if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
>                 goto bad;
>
> -       if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
> +       if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum_wrapper, &margs))
>                 goto bad;
>
>         if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
> --
> 2.43.2
>





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

  Powered by Linux