[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CACAyw9_9Q4CPzPm-ikMyMMmWR56u+5c7RpW-7o0YBG5JoheF2A@mail.gmail.com>
Date: Fri, 10 May 2019 10:45:53 +0100
From: Lorenz Bauer <lmb@...udflare.com>
To: Andrii Nakryiko <andriin@...com>
Cc: andrii.nakryiko@...il.com, netdev@...r.kernel.org,
bpf@...r.kernel.org, Daniel Borkmann <daniel@...earbox.net>,
Alexei Starovoitov <ast@...com>, yhs@...com
Subject: Re: [PATCH bpf] libbpf: detect supported kernel BTF features and
sanitize BTF
On Fri, 10 May 2019 at 05:37, Andrii Nakryiko <andriin@...com> wrote:
>
> Depending on used versions of libbpf, Clang, and kernel, it's possible to
> have valid BPF object files with valid BTF information, that still won't
> load successfully due to Clang emitting newer BTF features (e.g.,
> BTF_KIND_FUNC, .BTF.ext's line_info/func_info, BTF_KIND_DATASEC, etc), that
> are not yet supported by older kernel.
For sys_bpf, we ignore a zero tail in struct bpf_attr, which gives us
backwards / forwards compatibility
as long as the user doesn't use the new fields.
Do we need a similar mechanism for BTF? Is it possible to discard
unknown types at load time?
>
> This patch adds detection of BTF features and sanitizes BPF object's BTF
> by substituting various supported BTF kinds, which have compatible layout:
> - BTF_KIND_FUNC -> BTF_KIND_TYPEDEF
> - BTF_KIND_FUNC_PROTO -> BTF_KIND_ENUM
> - BTF_KIND_VAR -> BTF_KIND_INT
> - BTF_KIND_DATASEC -> BTF_KIND_STRUCT
>
> Replacement is done in such a way as to preserve as much information as
> possible (names, sizes, etc) where possible without violating kernel's
> validation rules.
>
> Reported-by: Alexei Starovoitov <ast@...com>
> Signed-off-by: Andrii Nakryiko <andriin@...com>
> ---
> tools/lib/bpf/libbpf.c | 185 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 184 insertions(+), 1 deletion(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 11a65db4b93f..0813c4ad5d11 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -128,6 +128,10 @@ struct bpf_capabilities {
> __u32 name:1;
> /* v5.2: kernel support for global data sections. */
> __u32 global_data:1;
> + /* BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO support */
> + __u32 btf_func:1;
> + /* BTF_KIND_VAR and BTF_KIND_DATASEC support */
> + __u32 btf_datasec:1;
> };
>
> /*
> @@ -1021,6 +1025,81 @@ static bool section_have_execinstr(struct bpf_object *obj, int idx)
> return false;
> }
>
> +static void bpf_object__sanitize_btf(struct bpf_object *obj)
> +{
> +#define BTF_INFO_ENC(kind, kind_flag, vlen) \
> + ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
> +#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
> + ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
> +
> + bool has_datasec = obj->caps.btf_datasec;
> + bool has_func = obj->caps.btf_func;
> + struct btf *btf = obj->btf;
> + struct btf_type *t;
> + int i, j, vlen;
> + __u16 kind;
> +
> + if (!obj->btf || (has_func && has_datasec))
> + return;
> +
> + for (i = 1; i <= btf__get_nr_types(btf); i++) {
> + t = (struct btf_type *)btf__type_by_id(btf, i);
> + kind = BTF_INFO_KIND(t->info);
> +
> + if (!has_datasec && kind == BTF_KIND_VAR) {
> + /* replace VAR with INT */
> + t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
> + t->size = sizeof(int);
> + *(int *)(t+1) = BTF_INT_ENC(0, 0, 32);
> + } else if (!has_datasec && kind == BTF_KIND_DATASEC) {
> + /* replace DATASEC with STRUCT */
> + struct btf_var_secinfo *v = (void *)(t + 1);
> + struct btf_member *m = (void *)(t + 1);
> + struct btf_type *vt;
> + char *name;
> +
> + name = (char *)btf__name_by_offset(btf, t->name_off);
> + while (*name) {
> + if (*name == '.')
> + *name = '_';
> + name++;
> + }
> +
> + vlen = BTF_INFO_VLEN(t->info);
> + t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen);
> + for (j = 0; j < vlen; j++, v++, m++) {
> + /* order of field assignments is important */
> + m->offset = v->offset * 8;
> + m->type = v->type;
> + /* preserve variable name as member name */
> + vt = (void *)btf__type_by_id(btf, v->type);
> + m->name_off = vt->name_off;
> + }
> + } else if (!has_func && kind == BTF_KIND_FUNC_PROTO) {
> + /* replace FUNC_PROTO with ENUM */
> + vlen = BTF_INFO_VLEN(t->info);
> + t->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen);
> + t->size = sizeof(__u32); /* kernel enforced */
> + } else if (!has_func && kind == BTF_KIND_FUNC) {
> + /* replace FUNC with TYPEDEF */
> + t->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0);
> + }
> + }
> +#undef BTF_INFO_ENC
> +#undef BTF_INT_ENC
> +}
> +
> +static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
> +{
> + if (!obj->btf_ext)
> + return;
> +
> + if (!obj->caps.btf_func) {
> + btf_ext__free(obj->btf_ext);
> + obj->btf_ext = NULL;
> + }
> +}
> +
> static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> {
> Elf *elf = obj->efile.elf;
> @@ -1164,8 +1243,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> obj->btf = NULL;
> } else {
> err = btf__finalize_data(obj, obj->btf);
> - if (!err)
> + if (!err) {
> + bpf_object__sanitize_btf(obj);
> err = btf__load(obj->btf);
> + }
> if (err) {
> pr_warning("Error finalizing and loading %s into kernel: %d. Ignored and continue.\n",
> BTF_ELF_SEC, err);
> @@ -1187,6 +1268,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> BTF_EXT_ELF_SEC,
> PTR_ERR(obj->btf_ext));
> obj->btf_ext = NULL;
> + } else {
> + bpf_object__sanitize_btf_ext(obj);
> }
> }
> }
> @@ -1556,12 +1639,112 @@ bpf_object__probe_global_data(struct bpf_object *obj)
> return 0;
> }
>
> +static int try_load_btf(const char *raw_types, size_t types_len,
> + const char *str_sec, size_t str_len)
> +{
> + char buf[1024];
> + struct btf_header hdr = {
> + .magic = BTF_MAGIC,
> + .version = BTF_VERSION,
> + .hdr_len = sizeof(struct btf_header),
> + .type_len = types_len,
> + .str_off = types_len,
> + .str_len = str_len,
> + };
> + int btf_fd, btf_len;
> + __u8 *raw_btf;
> +
> + btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len;
> + raw_btf = malloc(btf_len);
> + if (!raw_btf)
> + return -ENOMEM;
> +
> + memcpy(raw_btf, &hdr, sizeof(hdr));
> + memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len);
> + memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
> +
> + btf_fd = bpf_load_btf(raw_btf, btf_len, buf, 1024, 0);
> + if (btf_fd < 0) {
> + free(raw_btf);
> + return 0;
> + }
> +
> + close(btf_fd);
> + free(raw_btf);
> + return 1;
> +}
> +
> +#define BTF_INFO_ENC(kind, kind_flag, vlen) \
> + ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
> +#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
> +#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
> + ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
> +#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
> + BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
> + BTF_INT_ENC(encoding, bits_offset, bits)
> +#define BTF_PARAM_ENC(name, type) (name), (type)
> +#define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
> +static int bpf_object__probe_btf_func(struct bpf_object *obj)
> +{
> + const char strs[] = "\0int\0x\0a";
> + /* void x(int a) {} */
> + __u32 types[] = {
> + /* int */
> + BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
> + /* FUNC_PROTO */ /* [2] */
> + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
> + BTF_PARAM_ENC(7, 1),
> + /* FUNC x */ /* [3] */
> + BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), 2),
> + };
> + int res;
> +
> + res = try_load_btf((char *)types, sizeof(types), strs, sizeof(strs));
> + if (res < 0)
> + return res;
> + if (res > 0)
> + obj->caps.btf_func = 1;
> + return 0;
> +}
> +
> +static int bpf_object__probe_btf_datasec(struct bpf_object *obj)
> +{
> + const char strs[] = "\0x\0.data";
> + /* static int a; */
> + __u32 types[] = {
> + /* int */
> + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
> + /* VAR x */ /* [2] */
> + BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
> + BTF_VAR_STATIC,
> + /* DATASEC val */ /* [3] */
> + BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
> + BTF_VAR_SECINFO_ENC(2, 0, 4),
> + };
> + int res;
> +
> + res = try_load_btf((char *)&types, sizeof(types), strs, sizeof(strs));
> + if (res < 0)
> + return res;
> + if (res > 0)
> + obj->caps.btf_datasec = 1;
> + return 0;
> +}
> +#undef BTF_INFO_ENC
> +#undef BTF_TYPE_ENC
> +#undef BTF_INT_ENC
> +#undef BTF_TYPE_INT_ENC
> +#undef BTF_PARAM_ENC
> +#undef BTF_VAR_SECINFO_ENC
> +
> static int
> bpf_object__probe_caps(struct bpf_object *obj)
> {
> int (*probe_fn[])(struct bpf_object *obj) = {
> bpf_object__probe_name,
> bpf_object__probe_global_data,
> + bpf_object__probe_btf_func,
> + bpf_object__probe_btf_datasec,
> };
> int i, ret;
>
> --
> 2.17.1
>
--
Lorenz Bauer | Systems Engineer
25 Lavington St., London SE1 0NZ
www.cloudflare.com
Powered by blists - more mailing lists