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