Re: [PATCH bpf-next] libbpf: add basic BTF sanity validation

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

 



On Thu, Aug 24, 2023 at 5:11 AM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
>
> On Wed, 2023-08-23 at 16:44 -0700, Andrii Nakryiko wrote:
> > Implement a simple and straightforward BTF sanity check when loading BTF
> > information for BPF ELF file. Right now it's very basic and just
> > validates that all the string offsets and type IDs are within valid
> > range. But even with such simple checks it fixes a bunch of crashes
> > found by OSS fuzzer ([0]-[5]) and will allow fuzzer to make further
> > progress.
> >
> > Some other invariants will be checked in follow up patches (like
> > ensuring there is no infinite type loops), but this seems like a good
> > start already.
> >
> >   [0] https://github.com/libbpf/libbpf/issues/482
> >   [1] https://github.com/libbpf/libbpf/issues/483
> >   [2] https://github.com/libbpf/libbpf/issues/485
> >   [3] https://github.com/libbpf/libbpf/issues/613
> >   [4] https://github.com/libbpf/libbpf/issues/618
> >   [5] https://github.com/libbpf/libbpf/issues/619
> >
> > Closes: https://github.com/libbpf/libbpf/issues/617
> > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > ---
> >  tools/lib/bpf/btf.c             | 146 ++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/libbpf.c          |   7 ++
> >  tools/lib/bpf/libbpf_internal.h |   2 +
> >  3 files changed, 155 insertions(+)
> >
> > diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> > index 8484b563b53d..5f23df94861e 100644
> > --- a/tools/lib/bpf/btf.c
> > +++ b/tools/lib/bpf/btf.c
> > @@ -1155,6 +1155,152 @@ struct btf *btf__parse_split(const char *path, struct btf *base_btf)
> >       return libbpf_ptr(btf_parse(path, base_btf, NULL));
> >  }
> >
> > +static int btf_validate_str(const struct btf *btf, __u32 str_off, const char *what, __u32 type_id)
> > +{
> > +     const char *s;
> > +
> > +     s = btf__str_by_offset(btf, str_off);
> > +     if (!s) {
> > +             pr_warn("btf: type [%u]: invalid %s (string offset %u)\n", type_id, what, str_off);
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int btf_validate_id(const struct btf *btf, __u32 id, __u32 ctx_id)
> > +{
> > +     const struct btf_type *t;
> > +
> > +     t = btf__type_by_id(btf, id);
> > +     if (!t) {
> > +             pr_warn("btf: type [%u]: invalid referenced type ID %u\n", ctx_id, id);
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __u32 id)
> > +{
> > +     __u32 kind = btf_kind(t);
> > +     int err, i, n;
> > +
> > +     err = btf_validate_str(btf, t->name_off, "type name", id);
> > +     if (err)
> > +             return err;
> > +
> > +     switch (kind) {
> > +     case BTF_KIND_UNKN:
> > +     case BTF_KIND_INT:
> > +     case BTF_KIND_FWD:
> > +     case BTF_KIND_FLOAT:
> > +             break;
> > +     case BTF_KIND_PTR:
> > +     case BTF_KIND_TYPEDEF:
> > +     case BTF_KIND_VOLATILE:
> > +     case BTF_KIND_CONST:
> > +     case BTF_KIND_RESTRICT:
> > +     case BTF_KIND_FUNC:
> > +     case BTF_KIND_VAR:
> > +     case BTF_KIND_DECL_TAG:
> > +     case BTF_KIND_TYPE_TAG:
> > +             err = btf_validate_id(btf, t->type, id);
> > +             if (err)
> > +                     return err;
> > +             break;
> > +     case BTF_KIND_ARRAY: {
> > +             const struct btf_array *a = btf_array(t);
> > +
> > +             err = btf_validate_id(btf, a->index_type, id);
>
> `a->index_type` should probably be `a->type` here, otherwise these two
> checks are identical.

copy/paste error, nice catch, will fix

>
> > +             err = err ?: btf_validate_id(btf, a->index_type, id);
> > +             if (err)
> > +                     return err;
> > +             break;
> > +     }
> > +     case BTF_KIND_STRUCT:
> > +     case BTF_KIND_UNION: {
> > +             const struct btf_member *m = btf_members(t);
> > +
> > +             n = btf_vlen(t);
> > +             for (i = 0; i < n; i++, m++) {
> > +                     err = btf_validate_str(btf, m->name_off, "field name", id);
> > +                     err = err ?: btf_validate_id(btf, m->type, id);
> > +                     if (err)
> > +                             return err;
> > +             }
> > +             break;
> > +     }
> > +     case BTF_KIND_ENUM: {
> > +             const struct btf_enum *m = btf_enum(t);
> > +
> > +             n = btf_vlen(t);
> > +             for (i = 0; i < n; i++, m++) {
> > +                     err = btf_validate_str(btf, m->name_off, "enum name", id);
> > +                     if (err)
> > +                             return err;
> > +             }
> > +             break;
> > +     }
> > +     case BTF_KIND_ENUM64: {
> > +             const struct btf_enum64 *m = btf_enum64(t);
> > +
> > +             n = btf_vlen(t);
> > +             for (i = 0; i < n; i++, m++) {
> > +                     err = btf_validate_str(btf, m->name_off, "enum name", id);
> > +                     if (err)
> > +                             return err;
> > +             }
> > +             break;
> > +     }
> > +     case BTF_KIND_FUNC_PROTO: {
> > +             const struct btf_param *m = btf_params(t);
> > +
> > +             n = btf_vlen(t);
> > +             for (i = 0; i < n; i++, m++) {
> > +                     err = btf_validate_str(btf, m->name_off, "param name", id);
>
> Maybe check `m->type` here as well?

of course, I suspected I missed something obvious :)

>
> > +                     if (err)
> > +                             return err;
> > +             }
> > +             break;
> > +     }
> > +     case BTF_KIND_DATASEC: {
> > +             const struct btf_var_secinfo *m = btf_var_secinfos(t);
> > +
> > +             n = btf_vlen(t);
> > +             for (i = 0; i < n; i++, m++) {
> > +                     err = btf_validate_id(btf, m->type, id);
> > +                     if (err)
> > +                             return err;
> > +             }
> > +             break;
> > +     }
> > +     default:
> > +             pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind);
> > +             return -EINVAL;
> > +     }
> > +     return 0;
> > +}
> > +
> > +/* Validate basic sanity of BTF. It's intentionally less thorough than
> > + * kernel's validation and validates only properties of BTF that libbpf relies
> > + * on to be correct (e.g., valid type IDs, valid string offsets, etc)
> > + */
> > +int btf_sanity_check(const struct btf *btf)
> > +{
> > +     const struct btf_type *t;
> > +     __u32 i, n = btf__type_cnt(btf);
> > +     int err;
> > +
> > +     for (i = 1; i < n; i++) {
> > +             t = btf_type_by_id(btf, i);
> > +             err = btf_validate_type(btf, t, i);
> > +             if (err)
> > +                     return err;
> > +     }
> > +     return 0;
> > +}
> > +
> >  static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian);
> >
> >  int btf_load_into_kernel(struct btf *btf, char *log_buf, size_t log_sz, __u32 log_level)
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 4c3967d94b6d..71a3c768d9af 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -2833,6 +2833,13 @@ static int bpf_object__init_btf(struct bpf_object *obj,
> >                       pr_warn("Error loading ELF section %s: %d.\n", BTF_ELF_SEC, err);
> >                       goto out;
> >               }
> > +             err = btf_sanity_check(obj->btf);
> > +             if (err) {
> > +                     pr_warn("elf: .BTF data is corrupted, discarding it...\n");
> > +                     btf__free(obj->btf);
> > +                     obj->btf = NULL;
> > +                     goto out;
> > +             }
> >               /* enforce 8-byte pointers for BPF-targeted BTFs */
> >               btf__set_pointer_size(obj->btf, 8);
> >       }
> > diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
> > index f0f08635adb0..5e715a2d48f2 100644
> > --- a/tools/lib/bpf/libbpf_internal.h
> > +++ b/tools/lib/bpf/libbpf_internal.h
> > @@ -76,6 +76,8 @@
> >  #define BTF_TYPE_TYPE_TAG_ENC(value, type) \
> >       BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_TYPE_TAG, 0, 0), type)
> >
> > +int btf_sanity_check(const struct btf *btf);
> > +
> >  #ifndef likely
> >  #define likely(x) __builtin_expect(!!(x), 1)
> >  #endif
>





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux