Re: [PATCH 2/2] libsepol: Validate scope index class_perm_map array

Stephen Smalley <[email protected]> Fri, 24 Jul 2026 15:20:03 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ7_DfRsGBrVMOL_rqE8tRZ_EDXMzK=oRq3sujDNr-DcQg@mail.gmail.com>
On Fri, Jul 24, 2026 at 2:29 PM James Carter <[email protected]> wrote:
>
> In addtion to the array of ebitmaps that shows which declarations
> are in scope for a given block, the scope index also contains an
> array of ebitmaps, class_perms_map, that shows which permissions
> are in scope for each class and it contains the length of that
> array.
>
> If the class_perms_map is NULL, then verify that class_perms_len
> is 0. If the class_perms_map is not NULL, then verify that the
> class_perms_len is not 0 and that the array is long enough to
> contain all of the classes that are in scope for the block and
> that for each class in the array either the permission ebitmap
> is empty or it contains at least one valid permission. [Older
> compilers could set all the bits if "*" was used.]
>
> Signed-off-by: James Carter <[email protected]>

Was missing the v3 in the subject line but regardless:
Acked-by: Stephen Smalley <[email protected]>

> ---
> v3: - Stop differentiating between required and declared blocks and
>       instead differentiate between NULL and non-NULL class_perms_map
>     - Remove the added bool argument "required"
>
>  libsepol/src/policydb_validate.c | 45 +++++++++++++++++++++++++++-----
>  1 file changed, 38 insertions(+), 7 deletions(-)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 1d8e0870..fc66a4c7 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -1713,10 +1713,8 @@ bad:
>
>  static int validate_scope_index(sepol_handle_t *handle,
>                                 const scope_index_t *scope_index,
> -                               validate_t flavors[])
> +                               const policydb_t *p, validate_t flavors[])
>  {
> -       uint32_t i;
> -
>         if (!ebitmap_is_empty(&scope_index->scope[SYM_COMMONS]))
>                 goto bad;
>         if (validate_ebitmap(&scope_index->p_classes_scope,
> @@ -1735,9 +1733,42 @@ static int validate_scope_index(sepol_handle_t *handle,
>         if (validate_ebitmap(&scope_index->p_cat_scope, &flavors[SYM_CATS]))
>                 goto bad;
>
> -       for (i = 0; i < scope_index->class_perms_len; i++)
> -               if (validate_value(i + 1, &flavors[SYM_CLASSES]))
> +       if (scope_index->class_perms_map != NULL) {
> +               uint32_t i;
> +               if (scope_index->class_perms_len == 0)
> +                       goto bad;
> +               if (ebitmap_highest_set_bit(&scope_index->p_classes_scope) >=
> +                   scope_index->class_perms_len)
>                         goto bad;
> +               for (i = 0; i < scope_index->class_perms_len; i++) {
> +                       const ebitmap_t *map;
> +                       class_datum_t *class;
> +                       ebitmap_node_t *node;
> +                       unsigned int bit = 0;
> +                       if (validate_value(i + 1, &flavors[SYM_CLASSES]))
> +                               goto bad;
> +                       map = &scope_index->class_perms_map[i];
> +                       class = p->class_val_to_struct[i];
> +                       /* Either there are no perms */
> +                       if (ebitmap_is_empty(map))
> +                               continue;
> +                       /* Or at least one valid perm */
> +                       ebitmap_for_each_positive_bit(map, node, bit) {
> +                               if (bit < class->permissions.nprim)
> +                                       break;
> +                       }
> +                       if (bit >= class->permissions.nprim)
> +                               goto bad;
> +               }
> +       } else {
> +               /* This is the normal branch for declared blocks.
> +                * Base module required blocks should never be in this branch.
> +                * Module required blocks can be if neither its nor the module's
> +                * require block has a class in it.
> +                */
> +               if (scope_index->class_perms_len != 0)
> +                       goto bad;
> +       }
>
>         return 0;
>
> @@ -1811,10 +1842,10 @@ static int validate_avrule_blocks(sepol_handle_t *handle,
>                         if (validate_range_trans_rules(
>                                     handle, decl->range_tr_rules, flavors))
>                                 goto bad;
> -                       if (validate_scope_index(handle, &decl->required,
> +                       if (validate_scope_index(handle, &decl->required, p,
>                                                  flavors))
>                                 goto bad;
> -                       if (validate_scope_index(handle, &decl->declared,
> +                       if (validate_scope_index(handle, &decl->declared, p,
>                                                  flavors))
>                                 goto bad;
>                         if (validate_filename_trans_rules(
> --
> 2.55.0
>