Message ID | 20230616171728.530116-5-alan.maguire@oracle.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | bpf: support BTF kind layout info, CRCs | expand |
On Fri, Jun 16, 2023 at 06:17:22PM +0100, Alan Maguire wrote: SNIP > static int btf_sec_info_cmp(const void *a, const void *b) > @@ -5193,32 +5246,37 @@ static int btf_check_sec_info(struct btf_verifier_env *env, > u32 btf_data_size) > { > struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)]; > - u32 total, expected_total, i; > + u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset); > + u32 total, expected_total, gap, i; > const struct btf_header *hdr; > const struct btf *btf; > > btf = env->btf; > hdr = &btf->hdr; > > + if (hdr->hdr_len < sizeof(struct btf_header)) > + nr_secs--; > + > /* Populate the secs from hdr */ > - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) > + for (i = 0; i < nr_secs; i++) > secs[i] = *(struct btf_sec_info *)((void *)hdr + > btf_sec_info_offset[i]); > > - sort(secs, ARRAY_SIZE(btf_sec_info_offset), > + sort(secs, nr_secs, > sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL); > > /* Check for gaps and overlap among sections */ > total = 0; > expected_total = btf_data_size - hdr->hdr_len; > - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) { > + for (i = 0; i < nr_secs; i++) { > if (expected_total < secs[i].off) { > btf_verifier_log(env, "Invalid section offset"); > return -EINVAL; > } > - if (total < secs[i].off) { > - /* gap */ > - btf_verifier_log(env, "Unsupported section found"); > + gap = secs[i].off - total; > + if (gap >= 4) { > + /* gap larger than alignment gap */ > + btf_verifier_log(env, "Unsupported section gap found"); > return -EINVAL; this sems to break several btf header tests with: do_test_raw:PASS:check 0 nsec do_test_raw:FAIL:check expected err_str:Unsupported section found magic: 0xeb9f version: 1 flags: 0x0 hdr_len: 40 type_off: 4 type_len: 16 str_off: 16 str_len: 5 btf_total_size: 61 Unsupported section gap found #23/48 btf/btf_header test. Gap between hdr and type:FAIL jirka > } > if (total > secs[i].off) { > @@ -5230,7 +5288,7 @@ static int btf_check_sec_info(struct btf_verifier_env *env, > "Total section length too long"); > return -EINVAL; > } > - total += secs[i].len; > + total += secs[i].len + gap; > } > > /* There is data other than hdr and known sections */ > @@ -5293,7 +5351,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env) > return -ENOTSUPP; > } > > - if (hdr->flags) { > + if (hdr->flags & ~(BTF_FLAG_CRC_SET | BTF_FLAG_BASE_CRC_SET)) { > btf_verifier_log(env, "Unsupported flags"); > return -ENOTSUPP; > } > @@ -5530,6 +5588,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat > if (err) > goto errout; > > + err = btf_parse_kind_layout_sec(env); > + if (err) > + goto errout; > + > err = btf_parse_type_sec(env); > if (err) > goto errout; > -- > 2.39.3 >
On 18/06/2023 14:08, Jiri Olsa wrote: > On Fri, Jun 16, 2023 at 06:17:22PM +0100, Alan Maguire wrote: > > SNIP > >> static int btf_sec_info_cmp(const void *a, const void *b) >> @@ -5193,32 +5246,37 @@ static int btf_check_sec_info(struct btf_verifier_env *env, >> u32 btf_data_size) >> { >> struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)]; >> - u32 total, expected_total, i; >> + u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset); >> + u32 total, expected_total, gap, i; >> const struct btf_header *hdr; >> const struct btf *btf; >> >> btf = env->btf; >> hdr = &btf->hdr; >> >> + if (hdr->hdr_len < sizeof(struct btf_header)) >> + nr_secs--; >> + >> /* Populate the secs from hdr */ >> - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) >> + for (i = 0; i < nr_secs; i++) >> secs[i] = *(struct btf_sec_info *)((void *)hdr + >> btf_sec_info_offset[i]); >> >> - sort(secs, ARRAY_SIZE(btf_sec_info_offset), >> + sort(secs, nr_secs, >> sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL); >> >> /* Check for gaps and overlap among sections */ >> total = 0; >> expected_total = btf_data_size - hdr->hdr_len; >> - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) { >> + for (i = 0; i < nr_secs; i++) { >> if (expected_total < secs[i].off) { >> btf_verifier_log(env, "Invalid section offset"); >> return -EINVAL; >> } >> - if (total < secs[i].off) { >> - /* gap */ >> - btf_verifier_log(env, "Unsupported section found"); >> + gap = secs[i].off - total; >> + if (gap >= 4) { >> + /* gap larger than alignment gap */ >> + btf_verifier_log(env, "Unsupported section gap found"); >> return -EINVAL; > > this sems to break several btf header tests with: > > do_test_raw:PASS:check 0 nsec > do_test_raw:FAIL:check expected err_str:Unsupported section found > > magic: 0xeb9f > version: 1 > flags: 0x0 > hdr_len: 40 > type_off: 4 > type_len: 16 > str_off: 16 > str_len: 5 > btf_total_size: 61 > Unsupported section gap found > #23/48 btf/btf_header test. Gap between hdr and type:FAIL > > thanks for spotting this Jiri! I've reworked the logic and the messages for v3 (in progress), and these pass now. Alan > jirka > >> } >> if (total > secs[i].off) { >> @@ -5230,7 +5288,7 @@ static int btf_check_sec_info(struct btf_verifier_env *env, >> "Total section length too long"); >> return -EINVAL; >> } >> - total += secs[i].len; >> + total += secs[i].len + gap; >> } >> >> /* There is data other than hdr and known sections */ >> @@ -5293,7 +5351,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env) >> return -ENOTSUPP; >> } >> >> - if (hdr->flags) { >> + if (hdr->flags & ~(BTF_FLAG_CRC_SET | BTF_FLAG_BASE_CRC_SET)) { >> btf_verifier_log(env, "Unsupported flags"); >> return -ENOTSUPP; >> } >> @@ -5530,6 +5588,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat >> if (err) >> goto errout; >> >> + err = btf_parse_kind_layout_sec(env); >> + if (err) >> + goto errout; >> + >> err = btf_parse_type_sec(env); >> if (err) >> goto errout; >> -- >> 2.39.3 >>
On Fri, Jun 16, 2023 at 10:18 AM Alan Maguire <alan.maguire@oracle.com> wrote: > > Use kind layout to parse BTF with unknown kinds that have a > kind layout representation. > > Validate kind layout if present, and use it to parse BTF with > unrecognized kinds. Reject BTF that contains a type > of a kind that is not optional. > > Signed-off-by: Alan Maguire <alan.maguire@oracle.com> > --- > kernel/bpf/btf.c | 102 +++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 82 insertions(+), 20 deletions(-) > > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c > index bd2cac057928..ffe3926ea051 100644 > --- a/kernel/bpf/btf.c > +++ b/kernel/bpf/btf.c > @@ -257,6 +257,7 @@ struct btf { > struct btf_kfunc_set_tab *kfunc_set_tab; > struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab; > struct btf_struct_metas *struct_meta_tab; > + struct btf_kind_layout *kind_layout; > > /* split BTF support */ > struct btf *base_btf; > @@ -4965,22 +4966,41 @@ static s32 btf_check_meta(struct btf_verifier_env *env, > return -EINVAL; > } > > - if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX || > - BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) { > - btf_verifier_log(env, "[%u] Invalid kind:%u", > - env->log_type_id, BTF_INFO_KIND(t->info)); > - return -EINVAL; > - } > - > if (!btf_name_offset_valid(env->btf, t->name_off)) { > btf_verifier_log(env, "[%u] Invalid name_offset:%u", > env->log_type_id, t->name_off); > return -EINVAL; > } > > - var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); > - if (var_meta_size < 0) > - return var_meta_size; > + if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) { > + btf_verifier_log(env, "[%u] Invalid kind:%u", > + env->log_type_id, BTF_INFO_KIND(t->info)); > + return -EINVAL; > + } > + > + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX && env->btf->kind_layout && > + (BTF_INFO_KIND(t->info) * sizeof(struct btf_kind_layout)) < > + env->btf->hdr.kind_layout_len) { > + struct btf_kind_layout *k = &env->btf->kind_layout[BTF_INFO_KIND(t->info)]; > + > + if (!(k->flags & BTF_KIND_LAYOUT_OPTIONAL)) { same question as on previous patch, should kernel trust and handle OPTIONAL flag? I'd say let's drop it for now, doesn't seem worth the trouble > + btf_verifier_log(env, "[%u] unknown but required kind %u", > + env->log_type_id, > + BTF_INFO_KIND(t->info)); > + return -EINVAL; > + } > + var_meta_size = sizeof(struct btf_type); > + var_meta_size += k->info_sz + (btf_type_vlen(t) * k->elem_sz); > + } else { > + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX) { > + btf_verifier_log(env, "[%u] Invalid kind:%u", > + env->log_type_id, BTF_INFO_KIND(t->info)); > + return -EINVAL; > + } > + var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); > + if (var_meta_size < 0) > + return var_meta_size; > + } > > meta_left -= var_meta_size; > > @@ -5155,7 +5175,8 @@ static int btf_parse_str_sec(struct btf_verifier_env *env) > start = btf->nohdr_data + hdr->str_off; > end = start + hdr->str_len; > > - if (end != btf->data + btf->data_size) { > + if (hdr->hdr_len < sizeof(struct btf_header) && > + end != btf->data + btf->data_size) { > btf_verifier_log(env, "String section is not at the end"); > return -EINVAL; > } > @@ -5176,9 +5197,41 @@ static int btf_parse_str_sec(struct btf_verifier_env *env) > return 0; > } > > +static int btf_parse_kind_layout_sec(struct btf_verifier_env *env) > +{ > + const struct btf_header *hdr = &env->btf->hdr; > + struct btf *btf = env->btf; > + void *start, *end; > + > + if (hdr->hdr_len < sizeof(struct btf_header) || > + hdr->kind_layout_len == 0) let's make sure that kind_layout_off is zero in this case as well > + return 0; > + > + /* Kind layout section must align to 4 bytes */ > + if (hdr->kind_layout_off & (sizeof(u32) - 1)) { > + btf_verifier_log(env, "Unaligned kind_layout_off"); > + return -EINVAL; > + } > + start = btf->nohdr_data + hdr->kind_layout_off; > + end = start + hdr->kind_layout_len; > + > + if (hdr->kind_layout_len < sizeof(struct btf_kind_layout)) { same as on libbpf side, more generally kind_layout_len should be a multiple of sizeof(struct btf_kind_layout) > + btf_verifier_log(env, "Kind layout section is too small"); > + return -EINVAL; > + } > + if (end != btf->data + btf->data_size) { > + btf_verifier_log(env, "Kind layout section is not at the end"); > + return -EINVAL; > + } > + btf->kind_layout = start; > + > + return 0; > +} > + [...]
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index bd2cac057928..ffe3926ea051 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -257,6 +257,7 @@ struct btf { struct btf_kfunc_set_tab *kfunc_set_tab; struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab; struct btf_struct_metas *struct_meta_tab; + struct btf_kind_layout *kind_layout; /* split BTF support */ struct btf *base_btf; @@ -4965,22 +4966,41 @@ static s32 btf_check_meta(struct btf_verifier_env *env, return -EINVAL; } - if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX || - BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) { - btf_verifier_log(env, "[%u] Invalid kind:%u", - env->log_type_id, BTF_INFO_KIND(t->info)); - return -EINVAL; - } - if (!btf_name_offset_valid(env->btf, t->name_off)) { btf_verifier_log(env, "[%u] Invalid name_offset:%u", env->log_type_id, t->name_off); return -EINVAL; } - var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); - if (var_meta_size < 0) - return var_meta_size; + if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) { + btf_verifier_log(env, "[%u] Invalid kind:%u", + env->log_type_id, BTF_INFO_KIND(t->info)); + return -EINVAL; + } + + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX && env->btf->kind_layout && + (BTF_INFO_KIND(t->info) * sizeof(struct btf_kind_layout)) < + env->btf->hdr.kind_layout_len) { + struct btf_kind_layout *k = &env->btf->kind_layout[BTF_INFO_KIND(t->info)]; + + if (!(k->flags & BTF_KIND_LAYOUT_OPTIONAL)) { + btf_verifier_log(env, "[%u] unknown but required kind %u", + env->log_type_id, + BTF_INFO_KIND(t->info)); + return -EINVAL; + } + var_meta_size = sizeof(struct btf_type); + var_meta_size += k->info_sz + (btf_type_vlen(t) * k->elem_sz); + } else { + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX) { + btf_verifier_log(env, "[%u] Invalid kind:%u", + env->log_type_id, BTF_INFO_KIND(t->info)); + return -EINVAL; + } + var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left); + if (var_meta_size < 0) + return var_meta_size; + } meta_left -= var_meta_size; @@ -5155,7 +5175,8 @@ static int btf_parse_str_sec(struct btf_verifier_env *env) start = btf->nohdr_data + hdr->str_off; end = start + hdr->str_len; - if (end != btf->data + btf->data_size) { + if (hdr->hdr_len < sizeof(struct btf_header) && + end != btf->data + btf->data_size) { btf_verifier_log(env, "String section is not at the end"); return -EINVAL; } @@ -5176,9 +5197,41 @@ static int btf_parse_str_sec(struct btf_verifier_env *env) return 0; } +static int btf_parse_kind_layout_sec(struct btf_verifier_env *env) +{ + const struct btf_header *hdr = &env->btf->hdr; + struct btf *btf = env->btf; + void *start, *end; + + if (hdr->hdr_len < sizeof(struct btf_header) || + hdr->kind_layout_len == 0) + return 0; + + /* Kind layout section must align to 4 bytes */ + if (hdr->kind_layout_off & (sizeof(u32) - 1)) { + btf_verifier_log(env, "Unaligned kind_layout_off"); + return -EINVAL; + } + start = btf->nohdr_data + hdr->kind_layout_off; + end = start + hdr->kind_layout_len; + + if (hdr->kind_layout_len < sizeof(struct btf_kind_layout)) { + btf_verifier_log(env, "Kind layout section is too small"); + return -EINVAL; + } + if (end != btf->data + btf->data_size) { + btf_verifier_log(env, "Kind layout section is not at the end"); + return -EINVAL; + } + btf->kind_layout = start; + + return 0; +} + static const size_t btf_sec_info_offset[] = { offsetof(struct btf_header, type_off), offsetof(struct btf_header, str_off), + offsetof(struct btf_header, kind_layout_off), }; static int btf_sec_info_cmp(const void *a, const void *b) @@ -5193,32 +5246,37 @@ static int btf_check_sec_info(struct btf_verifier_env *env, u32 btf_data_size) { struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)]; - u32 total, expected_total, i; + u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset); + u32 total, expected_total, gap, i; const struct btf_header *hdr; const struct btf *btf; btf = env->btf; hdr = &btf->hdr; + if (hdr->hdr_len < sizeof(struct btf_header)) + nr_secs--; + /* Populate the secs from hdr */ - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) + for (i = 0; i < nr_secs; i++) secs[i] = *(struct btf_sec_info *)((void *)hdr + btf_sec_info_offset[i]); - sort(secs, ARRAY_SIZE(btf_sec_info_offset), + sort(secs, nr_secs, sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL); /* Check for gaps and overlap among sections */ total = 0; expected_total = btf_data_size - hdr->hdr_len; - for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) { + for (i = 0; i < nr_secs; i++) { if (expected_total < secs[i].off) { btf_verifier_log(env, "Invalid section offset"); return -EINVAL; } - if (total < secs[i].off) { - /* gap */ - btf_verifier_log(env, "Unsupported section found"); + gap = secs[i].off - total; + if (gap >= 4) { + /* gap larger than alignment gap */ + btf_verifier_log(env, "Unsupported section gap found"); return -EINVAL; } if (total > secs[i].off) { @@ -5230,7 +5288,7 @@ static int btf_check_sec_info(struct btf_verifier_env *env, "Total section length too long"); return -EINVAL; } - total += secs[i].len; + total += secs[i].len + gap; } /* There is data other than hdr and known sections */ @@ -5293,7 +5351,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env) return -ENOTSUPP; } - if (hdr->flags) { + if (hdr->flags & ~(BTF_FLAG_CRC_SET | BTF_FLAG_BASE_CRC_SET)) { btf_verifier_log(env, "Unsupported flags"); return -ENOTSUPP; } @@ -5530,6 +5588,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat if (err) goto errout; + err = btf_parse_kind_layout_sec(env); + if (err) + goto errout; + err = btf_parse_type_sec(env); if (err) goto errout;
Use kind layout to parse BTF with unknown kinds that have a kind layout representation. Validate kind layout if present, and use it to parse BTF with unrecognized kinds. Reject BTF that contains a type of a kind that is not optional. Signed-off-by: Alan Maguire <alan.maguire@oracle.com> --- kernel/bpf/btf.c | 102 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 20 deletions(-)