diff mbox series

[v2] libsepol: handle type gaps

Message ID 20220106204226.146879-1-cgzones@googlemail.com (mailing list archive)
State Superseded
Headers show
Series [v2] libsepol: handle type gaps | expand

Commit Message

Christian Göttsche Jan. 6, 2022, 8:42 p.m. UTC
For policy versions between 20 and 23 the type_val_to_struct array might
contain gaps. Skip those gaps to avoid NULL pointer dereferences:

    ==1250==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x00000058560b bp 0x7ffdca60c110 sp 0x7ffdca60bfc0 T0)
    ==1250==The signal is caused by a READ memory access.
    ==1250==Hint: address points to the zero page.
        #0 0x58560b in build_type_map selinux/libsepol/src/optimize.c:107:33
        #1 0x58560b in policydb_optimize selinux/libsepol/src/optimize.c:441:13
        #2 0x55e63e in LLVMFuzzerTestOneInput selinux/libsepol/fuzz/binpolicy-fuzzer.c:42:10
        #3 0x455283 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp:0
        #4 0x440ec2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
        #5 0x44671c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp:0
        #6 0x46f522 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
        #7 0x7f9c160d00b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/libc-start.c:308:16
        #8 0x41f67d in _start

Found by oss-fuzz (#42697)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
@Jim:
    I hope I have interpreted and implemented your feedback correctly, but I
    am not sure I have seen through the whole gaps logic yet.
---
 libsepol/src/kernel_to_cil.c  | 40 +++++++++++++++++++++++++++++++++++
 libsepol/src/kernel_to_conf.c | 32 ++++++++++++++++++++++++++++
 libsepol/src/optimize.c       | 18 +++++++++++++++-
 3 files changed, 89 insertions(+), 1 deletion(-)

Comments

James Carter Jan. 12, 2022, 3:55 p.m. UTC | #1
On Fri, Jan 7, 2022 at 1:57 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> For policy versions between 20 and 23 the type_val_to_struct array might
> contain gaps. Skip those gaps to avoid NULL pointer dereferences:
>
>     ==1250==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x00000058560b bp 0x7ffdca60c110 sp 0x7ffdca60bfc0 T0)
>     ==1250==The signal is caused by a READ memory access.
>     ==1250==Hint: address points to the zero page.
>         #0 0x58560b in build_type_map selinux/libsepol/src/optimize.c:107:33
>         #1 0x58560b in policydb_optimize selinux/libsepol/src/optimize.c:441:13
>         #2 0x55e63e in LLVMFuzzerTestOneInput selinux/libsepol/fuzz/binpolicy-fuzzer.c:42:10
>         #3 0x455283 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp:0
>         #4 0x440ec2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
>         #5 0x44671c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp:0
>         #6 0x46f522 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
>         #7 0x7f9c160d00b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/libc-start.c:308:16
>         #8 0x41f67d in _start
>
> Found by oss-fuzz (#42697)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> @Jim:
>     I hope I have interpreted and implemented your feedback correctly, but I
>     am not sure I have seen through the whole gaps logic yet.
> ---
>  libsepol/src/kernel_to_cil.c  | 40 +++++++++++++++++++++++++++++++++++
>  libsepol/src/kernel_to_conf.c | 32 ++++++++++++++++++++++++++++
>  libsepol/src/optimize.c       | 18 +++++++++++++++-
>  3 files changed, 89 insertions(+), 1 deletion(-)
>
> diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> index 18294a9a..1353e77f 100644
> --- a/libsepol/src/kernel_to_cil.c
> +++ b/libsepol/src/kernel_to_cil.c
> @@ -1227,6 +1227,14 @@ static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

There is already a check in sepol_kernel_policydb_to_cil() and an
error is returned if the policy version is between 20 and 23.
While no policy that is written by libsepol will have gaps in this
array, the validate_policydb() only checks that gaps are not referred
to by any rules, so there does need to be a check for a NULL here.

I would make the line below to be "if (type && type->flavor ..."

>                 if (type->flavor == TYPE_ATTRIB) {
>                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
>                         if (rc != 0) {
> @@ -1357,6 +1365,14 @@ static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

Same here.

>                 if (type->flavor == TYPE_TYPE && type->primary) {
>                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
>                         if (rc != 0) {
> @@ -1486,6 +1502,14 @@ static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

And here.

>                 if (type->flavor == TYPE_TYPE) {
>                         if (type->bounds > 0) {
>                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> @@ -1540,6 +1564,14 @@ static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 attr = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!attr) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

And here.

>                 if (attr->flavor != TYPE_ATTRIB) continue;
>                 name = pdb->p_type_val_to_name[i];
>                 typemap = &pdb->attr_type_map[i];
> @@ -2273,6 +2305,14 @@ static int write_role_decl_rules_to_cil(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type_datum = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type_datum) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

And here.

>                 if (type_datum->flavor == TYPE_TYPE && type_datum->primary) {
>                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
>                         if (rc != 0) {
> diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
> index a92ba9fd..235b4556 100644
> --- a/libsepol/src/kernel_to_conf.c
> +++ b/libsepol/src/kernel_to_conf.c
> @@ -1210,6 +1210,14 @@ static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }

Same thing here. sepol_kernel_policydb_to_conf() already does a check
and returns an error if the policy version is between 20 and 23. All
the same comments above apply here.

>                 if (type->flavor == TYPE_ATTRIB) {
>                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
>                         if (rc != 0) {
> @@ -1340,6 +1348,14 @@ static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }
>                 if (type->flavor == TYPE_TYPE && type->primary) {
>                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
>                         if (rc != 0) {
> @@ -1460,6 +1476,14 @@ static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }
>                 if (type->flavor == TYPE_TYPE) {
>                         if (type->bounds > 0) {
>                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> @@ -1583,6 +1607,14 @@ static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb)
>
>         for (i=0; i < pdb->p_types.nprim; i++) {
>                 type = pdb->type_val_to_struct[i];
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!type) {
> +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> +                               rc = -1;
> +                               goto exit;
> +                       }
> +                       continue;
> +               }
>                 if (type->flavor != TYPE_TYPE || !type->primary) continue;
>                 if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue;
>
> diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
> index 8a048702..f8c28313 100644
> --- a/libsepol/src/optimize.c
> +++ b/libsepol/src/optimize.c
> @@ -104,6 +104,13 @@ static struct type_vec *build_type_map(const policydb_t *p)
>                 if (type_vec_init(&map[i]))
>                         goto err;
>
> +               /* Gap in types for policy versions between 20 and 23 */
> +               if (!p->type_val_to_struct[i]) {
> +                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> +                               goto err;
> +                       continue;
> +               }
> +

I want the same sort of check for the policy version done in
policydb_optimize(), not throughout the code.
Can still check for NULL.

>                 if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
>                         ebitmap_for_each_positive_bit(&p->type_attr_map[i],
>                                                       n, k) {
> @@ -114,11 +121,20 @@ static struct type_vec *build_type_map(const policydb_t *p)
>                         ebitmap_t *types_i = &p->attr_type_map[i];
>
>                         for (k = 0; k < p->p_types.nprim; k++) {
> -                               ebitmap_t *types_k = &p->attr_type_map[k];
> +                               ebitmap_t *types_k;
> +
> +                               /* Gap in types for policy versions between 20 and 23 */
> +                               if (!p->type_val_to_struct[k]) {
> +                                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> +                                               goto err;
> +                                       continue;
> +                               }

Same here.

Thanks,
Jim

>
>                                 if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
>                                         continue;
>
> +                               types_k = &p->attr_type_map[k];
> +
>                                 if (ebitmap_contains(types_k, types_i)) {
>                                         if (type_vec_append(&map[i], k))
>                                                 goto err;
> --
> 2.34.1
>
Christian Göttsche Jan. 19, 2022, 3:43 p.m. UTC | #2
On Wed, 12 Jan 2022 at 16:55, James Carter <jwcart2@gmail.com> wrote:
>
> On Fri, Jan 7, 2022 at 1:57 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > For policy versions between 20 and 23 the type_val_to_struct array might
> > contain gaps. Skip those gaps to avoid NULL pointer dereferences:
> >
> >     ==1250==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x00000058560b bp 0x7ffdca60c110 sp 0x7ffdca60bfc0 T0)
> >     ==1250==The signal is caused by a READ memory access.
> >     ==1250==Hint: address points to the zero page.
> >         #0 0x58560b in build_type_map selinux/libsepol/src/optimize.c:107:33
> >         #1 0x58560b in policydb_optimize selinux/libsepol/src/optimize.c:441:13
> >         #2 0x55e63e in LLVMFuzzerTestOneInput selinux/libsepol/fuzz/binpolicy-fuzzer.c:42:10
> >         #3 0x455283 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp:0
> >         #4 0x440ec2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
> >         #5 0x44671c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp:0
> >         #6 0x46f522 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
> >         #7 0x7f9c160d00b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/libc-start.c:308:16
> >         #8 0x41f67d in _start
> >
> > Found by oss-fuzz (#42697)
> >
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> > @Jim:
> >     I hope I have interpreted and implemented your feedback correctly, but I
> >     am not sure I have seen through the whole gaps logic yet.
> > ---
> >  libsepol/src/kernel_to_cil.c  | 40 +++++++++++++++++++++++++++++++++++
> >  libsepol/src/kernel_to_conf.c | 32 ++++++++++++++++++++++++++++
> >  libsepol/src/optimize.c       | 18 +++++++++++++++-
> >  3 files changed, 89 insertions(+), 1 deletion(-)
> >
> > diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> > index 18294a9a..1353e77f 100644
> > --- a/libsepol/src/kernel_to_cil.c
> > +++ b/libsepol/src/kernel_to_cil.c
> > @@ -1227,6 +1227,14 @@ static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> There is already a check in sepol_kernel_policydb_to_cil() and an
> error is returned if the policy version is between 20 and 23.
> While no policy that is written by libsepol will have gaps in this
> array, the validate_policydb() only checks that gaps are not referred
> to by any rules, so there does need to be a check for a NULL here.
>
> I would make the line below to be "if (type && type->flavor ..."
>
> >                 if (type->flavor == TYPE_ATTRIB) {
> >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> >                         if (rc != 0) {
> > @@ -1357,6 +1365,14 @@ static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> Same here.
>
> >                 if (type->flavor == TYPE_TYPE && type->primary) {
> >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> >                         if (rc != 0) {
> > @@ -1486,6 +1502,14 @@ static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> And here.
>
> >                 if (type->flavor == TYPE_TYPE) {
> >                         if (type->bounds > 0) {
> >                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > @@ -1540,6 +1564,14 @@ static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 attr = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!attr) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> And here.
>
> >                 if (attr->flavor != TYPE_ATTRIB) continue;
> >                 name = pdb->p_type_val_to_name[i];
> >                 typemap = &pdb->attr_type_map[i];
> > @@ -2273,6 +2305,14 @@ static int write_role_decl_rules_to_cil(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type_datum = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type_datum) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> And here.
>
> >                 if (type_datum->flavor == TYPE_TYPE && type_datum->primary) {
> >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> >                         if (rc != 0) {
> > diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
> > index a92ba9fd..235b4556 100644
> > --- a/libsepol/src/kernel_to_conf.c
> > +++ b/libsepol/src/kernel_to_conf.c
> > @@ -1210,6 +1210,14 @@ static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
>
> Same thing here. sepol_kernel_policydb_to_conf() already does a check
> and returns an error if the policy version is between 20 and 23. All
> the same comments above apply here.
>
> >                 if (type->flavor == TYPE_ATTRIB) {
> >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> >                         if (rc != 0) {
> > @@ -1340,6 +1348,14 @@ static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
> >                 if (type->flavor == TYPE_TYPE && type->primary) {
> >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> >                         if (rc != 0) {
> > @@ -1460,6 +1476,14 @@ static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
> >                 if (type->flavor == TYPE_TYPE) {
> >                         if (type->bounds > 0) {
> >                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > @@ -1583,6 +1607,14 @@ static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb)
> >
> >         for (i=0; i < pdb->p_types.nprim; i++) {
> >                 type = pdb->type_val_to_struct[i];
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!type) {
> > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > +                               rc = -1;
> > +                               goto exit;
> > +                       }
> > +                       continue;
> > +               }
> >                 if (type->flavor != TYPE_TYPE || !type->primary) continue;
> >                 if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue;
> >
> > diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
> > index 8a048702..f8c28313 100644
> > --- a/libsepol/src/optimize.c
> > +++ b/libsepol/src/optimize.c
> > @@ -104,6 +104,13 @@ static struct type_vec *build_type_map(const policydb_t *p)
> >                 if (type_vec_init(&map[i]))
> >                         goto err;
> >
> > +               /* Gap in types for policy versions between 20 and 23 */
> > +               if (!p->type_val_to_struct[i]) {
> > +                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> > +                               goto err;
> > +                       continue;
> > +               }
> > +
>
> I want the same sort of check for the policy version done in
> policydb_optimize(), not throughout the code.

Something like

    diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
    index 8a048702..059c61f2 100644
    --- a/libsepol/src/optimize.c
    +++ b/libsepol/src/optimize.c
    @@ -438,6 +443,15 @@ int policydb_optimize(policydb_t *p)
           if (p->policy_type != POLICY_KERN)
                   return -1;

    +       if (p->policyvers >= POLICYDB_VERSION_AVTAB &&
p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
    +               /*
    +                * For policy versions between 20 and 23,
attributes exist in the policy,
    +                * but only in the type_attr_map. This means that
there are gaps in both
    +                * the type_val_to_struct and p_type_val_to_name
arrays and policy rules
    +                * can refer to those gaps.
    +                */
    +               return FIXME;
    +
           type_map = build_type_map(p);
           if (!type_map)
                   return -1;

?
Should it return 0 and silently not optimize or fail with -1?
In case of a failure should there be a message, e.g.

    ERR(NULL, "Optimizing policy versions between 20 and 23 is not supported");

?

> Can still check for NULL.
>
> >                 if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
> >                         ebitmap_for_each_positive_bit(&p->type_attr_map[i],
> >                                                       n, k) {
> > @@ -114,11 +121,20 @@ static struct type_vec *build_type_map(const policydb_t *p)
> >                         ebitmap_t *types_i = &p->attr_type_map[i];
> >
> >                         for (k = 0; k < p->p_types.nprim; k++) {
> > -                               ebitmap_t *types_k = &p->attr_type_map[k];
> > +                               ebitmap_t *types_k;
> > +
> > +                               /* Gap in types for policy versions between 20 and 23 */
> > +                               if (!p->type_val_to_struct[k]) {
> > +                                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> > +                                               goto err;
> > +                                       continue;
> > +                               }
>
> Same here.
>
> Thanks,
> Jim
>
> >
> >                                 if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
> >                                         continue;
> >
> > +                               types_k = &p->attr_type_map[k];
> > +
> >                                 if (ebitmap_contains(types_k, types_i)) {
> >                                         if (type_vec_append(&map[i], k))
> >                                                 goto err;
> > --
> > 2.34.1
> >
James Carter Jan. 19, 2022, 6:11 p.m. UTC | #3
On Wed, Jan 19, 2022 at 10:43 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> On Wed, 12 Jan 2022 at 16:55, James Carter <jwcart2@gmail.com> wrote:
> >
> > On Fri, Jan 7, 2022 at 1:57 PM Christian Göttsche
> > <cgzones@googlemail.com> wrote:
> > >
> > > For policy versions between 20 and 23 the type_val_to_struct array might
> > > contain gaps. Skip those gaps to avoid NULL pointer dereferences:
> > >
> > >     ==1250==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x00000058560b bp 0x7ffdca60c110 sp 0x7ffdca60bfc0 T0)
> > >     ==1250==The signal is caused by a READ memory access.
> > >     ==1250==Hint: address points to the zero page.
> > >         #0 0x58560b in build_type_map selinux/libsepol/src/optimize.c:107:33
> > >         #1 0x58560b in policydb_optimize selinux/libsepol/src/optimize.c:441:13
> > >         #2 0x55e63e in LLVMFuzzerTestOneInput selinux/libsepol/fuzz/binpolicy-fuzzer.c:42:10
> > >         #3 0x455283 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp:0
> > >         #4 0x440ec2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
> > >         #5 0x44671c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp:0
> > >         #6 0x46f522 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
> > >         #7 0x7f9c160d00b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/libc-start.c:308:16
> > >         #8 0x41f67d in _start
> > >
> > > Found by oss-fuzz (#42697)
> > >
> > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > > ---
> > > @Jim:
> > >     I hope I have interpreted and implemented your feedback correctly, but I
> > >     am not sure I have seen through the whole gaps logic yet.
> > > ---
> > >  libsepol/src/kernel_to_cil.c  | 40 +++++++++++++++++++++++++++++++++++
> > >  libsepol/src/kernel_to_conf.c | 32 ++++++++++++++++++++++++++++
> > >  libsepol/src/optimize.c       | 18 +++++++++++++++-
> > >  3 files changed, 89 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
> > > index 18294a9a..1353e77f 100644
> > > --- a/libsepol/src/kernel_to_cil.c
> > > +++ b/libsepol/src/kernel_to_cil.c
> > > @@ -1227,6 +1227,14 @@ static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > There is already a check in sepol_kernel_policydb_to_cil() and an
> > error is returned if the policy version is between 20 and 23.
> > While no policy that is written by libsepol will have gaps in this
> > array, the validate_policydb() only checks that gaps are not referred
> > to by any rules, so there does need to be a check for a NULL here.
> >
> > I would make the line below to be "if (type && type->flavor ..."
> >
> > >                 if (type->flavor == TYPE_ATTRIB) {
> > >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > >                         if (rc != 0) {
> > > @@ -1357,6 +1365,14 @@ static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > Same here.
> >
> > >                 if (type->flavor == TYPE_TYPE && type->primary) {
> > >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > >                         if (rc != 0) {
> > > @@ -1486,6 +1502,14 @@ static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > And here.
> >
> > >                 if (type->flavor == TYPE_TYPE) {
> > >                         if (type->bounds > 0) {
> > >                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > > @@ -1540,6 +1564,14 @@ static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 attr = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!attr) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > And here.
> >
> > >                 if (attr->flavor != TYPE_ATTRIB) continue;
> > >                 name = pdb->p_type_val_to_name[i];
> > >                 typemap = &pdb->attr_type_map[i];
> > > @@ -2273,6 +2305,14 @@ static int write_role_decl_rules_to_cil(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type_datum = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type_datum) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > And here.
> >
> > >                 if (type_datum->flavor == TYPE_TYPE && type_datum->primary) {
> > >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > >                         if (rc != 0) {
> > > diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
> > > index a92ba9fd..235b4556 100644
> > > --- a/libsepol/src/kernel_to_conf.c
> > > +++ b/libsepol/src/kernel_to_conf.c
> > > @@ -1210,6 +1210,14 @@ static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> >
> > Same thing here. sepol_kernel_policydb_to_conf() already does a check
> > and returns an error if the policy version is between 20 and 23. All
> > the same comments above apply here.
> >
> > >                 if (type->flavor == TYPE_ATTRIB) {
> > >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > >                         if (rc != 0) {
> > > @@ -1340,6 +1348,14 @@ static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> > >                 if (type->flavor == TYPE_TYPE && type->primary) {
> > >                         rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > >                         if (rc != 0) {
> > > @@ -1460,6 +1476,14 @@ static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> > >                 if (type->flavor == TYPE_TYPE) {
> > >                         if (type->bounds > 0) {
> > >                                 rc = strs_add(strs, pdb->p_type_val_to_name[i]);
> > > @@ -1583,6 +1607,14 @@ static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb)
> > >
> > >         for (i=0; i < pdb->p_types.nprim; i++) {
> > >                 type = pdb->type_val_to_struct[i];
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!type) {
> > > +                       if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
> > > +                               rc = -1;
> > > +                               goto exit;
> > > +                       }
> > > +                       continue;
> > > +               }
> > >                 if (type->flavor != TYPE_TYPE || !type->primary) continue;
> > >                 if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue;
> > >
> > > diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
> > > index 8a048702..f8c28313 100644
> > > --- a/libsepol/src/optimize.c
> > > +++ b/libsepol/src/optimize.c
> > > @@ -104,6 +104,13 @@ static struct type_vec *build_type_map(const policydb_t *p)
> > >                 if (type_vec_init(&map[i]))
> > >                         goto err;
> > >
> > > +               /* Gap in types for policy versions between 20 and 23 */
> > > +               if (!p->type_val_to_struct[i]) {
> > > +                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> > > +                               goto err;
> > > +                       continue;
> > > +               }
> > > +
> >
> > I want the same sort of check for the policy version done in
> > policydb_optimize(), not throughout the code.
>
> Something like
>
>     diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
>     index 8a048702..059c61f2 100644
>     --- a/libsepol/src/optimize.c
>     +++ b/libsepol/src/optimize.c
>     @@ -438,6 +443,15 @@ int policydb_optimize(policydb_t *p)
>            if (p->policy_type != POLICY_KERN)
>                    return -1;
>
>     +       if (p->policyvers >= POLICYDB_VERSION_AVTAB &&
> p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
>     +               /*
>     +                * For policy versions between 20 and 23,
> attributes exist in the policy,
>     +                * but only in the type_attr_map. This means that
> there are gaps in both
>     +                * the type_val_to_struct and p_type_val_to_name
> arrays and policy rules
>     +                * can refer to those gaps.
>     +                */
>     +               return FIXME;
>     +
>            type_map = build_type_map(p);
>            if (!type_map)
>                    return -1;
>

Yes.

> ?
> Should it return 0 and silently not optimize or fail with -1?
> In case of a failure should there be a message, e.g.
>
>     ERR(NULL, "Optimizing policy versions between 20 and 23 is not supported");
>
> ?
>

Fail with -1 and the error message you have there.

Thanks,
Jim


> > Can still check for NULL.
> >
> > >                 if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
> > >                         ebitmap_for_each_positive_bit(&p->type_attr_map[i],
> > >                                                       n, k) {
> > > @@ -114,11 +121,20 @@ static struct type_vec *build_type_map(const policydb_t *p)
> > >                         ebitmap_t *types_i = &p->attr_type_map[i];
> > >
> > >                         for (k = 0; k < p->p_types.nprim; k++) {
> > > -                               ebitmap_t *types_k = &p->attr_type_map[k];
> > > +                               ebitmap_t *types_k;
> > > +
> > > +                               /* Gap in types for policy versions between 20 and 23 */
> > > +                               if (!p->type_val_to_struct[k]) {
> > > +                                       if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
> > > +                                               goto err;
> > > +                                       continue;
> > > +                               }
> >
> > Same here.
> >
> > Thanks,
> > Jim
> >
> > >
> > >                                 if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
> > >                                         continue;
> > >
> > > +                               types_k = &p->attr_type_map[k];
> > > +
> > >                                 if (ebitmap_contains(types_k, types_i)) {
> > >                                         if (type_vec_append(&map[i], k))
> > >                                                 goto err;
> > > --
> > > 2.34.1
> > >
diff mbox series

Patch

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index 18294a9a..1353e77f 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1227,6 +1227,14 @@  static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_ATTRIB) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
@@ -1357,6 +1365,14 @@  static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_TYPE && type->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
@@ -1486,6 +1502,14 @@  static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_TYPE) {
 			if (type->bounds > 0) {
 				rc = strs_add(strs, pdb->p_type_val_to_name[i]);
@@ -1540,6 +1564,14 @@  static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		attr = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!attr) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (attr->flavor != TYPE_ATTRIB) continue;
 		name = pdb->p_type_val_to_name[i];
 		typemap = &pdb->attr_type_map[i];
@@ -2273,6 +2305,14 @@  static int write_role_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type_datum = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type_datum) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type_datum->flavor == TYPE_TYPE && type_datum->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index a92ba9fd..235b4556 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -1210,6 +1210,14 @@  static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_ATTRIB) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
@@ -1340,6 +1348,14 @@  static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_TYPE && type->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
@@ -1460,6 +1476,14 @@  static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor == TYPE_TYPE) {
 			if (type->bounds > 0) {
 				rc = strs_add(strs, pdb->p_type_val_to_name[i]);
@@ -1583,6 +1607,14 @@  static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb)
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!type) {
+			if (pdb->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+				rc = -1;
+				goto exit;
+			}
+			continue;
+		}
 		if (type->flavor != TYPE_TYPE || !type->primary) continue;
 		if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue;
 
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index 8a048702..f8c28313 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -104,6 +104,13 @@  static struct type_vec *build_type_map(const policydb_t *p)
 		if (type_vec_init(&map[i]))
 			goto err;
 
+		/* Gap in types for policy versions between 20 and 23 */
+		if (!p->type_val_to_struct[i]) {
+			if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
+				goto err;
+			continue;
+		}
+
 		if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
 			ebitmap_for_each_positive_bit(&p->type_attr_map[i],
 						      n, k) {
@@ -114,11 +121,20 @@  static struct type_vec *build_type_map(const policydb_t *p)
 			ebitmap_t *types_i = &p->attr_type_map[i];
 
 			for (k = 0; k < p->p_types.nprim; k++) {
-				ebitmap_t *types_k = &p->attr_type_map[k];
+				ebitmap_t *types_k;
+
+				/* Gap in types for policy versions between 20 and 23 */
+				if (!p->type_val_to_struct[k]) {
+					if (p->policyvers <= POLICYDB_VERSION_PERMISSIVE)
+						goto err;
+					continue;
+				}
 
 				if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
 					continue;
 
+				types_k = &p->attr_type_map[k];
+
 				if (ebitmap_contains(types_k, types_i)) {
 					if (type_vec_append(&map[i], k))
 						goto err;