libbpf: Improve BTF sanitization handling
Change sanitization process to preserve original BTF, which might be used by libbpf itself for Kconfig externs, CO-RE relocs, etc, even if kernel is old and doesn't support BTF. To achieve that, if libbpf detects the need for BTF sanitization, it would clone original BTF, sanitize it in-place, attempt to load it into kernel, and if successful, will preserve loaded BTF FD in original `struct btf`, while freeing sanitized local copy. If kernel doesn't support any BTF, original btf and btf_ext will still be preserved to be used later for CO-RE relocation and other BTF-dependent libbpf features, which don't dependon kernel BTF support. Patch takes care to not specify BTF and BTF.ext features when loading BPF programs and/or maps, if it was detected that kernel doesn't support BTF features. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20200708015318.3827358-4-andriin@fb.com
This commit is contained in:
parent
81372e1218
commit
0f0e55d824
@ -2338,18 +2338,23 @@ static bool section_have_execinstr(struct bpf_object *obj, int idx)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bpf_object__sanitize_btf(struct bpf_object *obj)
|
static bool btf_needs_sanitization(struct bpf_object *obj)
|
||||||
|
{
|
||||||
|
bool has_func_global = obj->caps.btf_func_global;
|
||||||
|
bool has_datasec = obj->caps.btf_datasec;
|
||||||
|
bool has_func = obj->caps.btf_func;
|
||||||
|
|
||||||
|
return !has_func || !has_datasec || !has_func_global;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
|
||||||
{
|
{
|
||||||
bool has_func_global = obj->caps.btf_func_global;
|
bool has_func_global = obj->caps.btf_func_global;
|
||||||
bool has_datasec = obj->caps.btf_datasec;
|
bool has_datasec = obj->caps.btf_datasec;
|
||||||
bool has_func = obj->caps.btf_func;
|
bool has_func = obj->caps.btf_func;
|
||||||
struct btf *btf = obj->btf;
|
|
||||||
struct btf_type *t;
|
struct btf_type *t;
|
||||||
int i, j, vlen;
|
int i, j, vlen;
|
||||||
|
|
||||||
if (!obj->btf || (has_func && has_datasec && has_func_global))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 1; i <= btf__get_nr_types(btf); i++) {
|
for (i = 1; i <= btf__get_nr_types(btf); i++) {
|
||||||
t = (struct btf_type *)btf__type_by_id(btf, i);
|
t = (struct btf_type *)btf__type_by_id(btf, i);
|
||||||
|
|
||||||
@ -2402,17 +2407,6 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 bool libbpf_needs_btf(const struct bpf_object *obj)
|
static bool libbpf_needs_btf(const struct bpf_object *obj)
|
||||||
{
|
{
|
||||||
return obj->efile.btf_maps_shndx >= 0 ||
|
return obj->efile.btf_maps_shndx >= 0 ||
|
||||||
@ -2530,30 +2524,50 @@ static int bpf_object__load_vmlinux_btf(struct bpf_object *obj)
|
|||||||
|
|
||||||
static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
|
static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
|
||||||
{
|
{
|
||||||
|
struct btf *kern_btf = obj->btf;
|
||||||
|
bool btf_mandatory, sanitize;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (!obj->btf)
|
if (!obj->btf)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bpf_object__sanitize_btf(obj);
|
sanitize = btf_needs_sanitization(obj);
|
||||||
bpf_object__sanitize_btf_ext(obj);
|
if (sanitize) {
|
||||||
|
const void *orig_data;
|
||||||
|
void *san_data;
|
||||||
|
__u32 sz;
|
||||||
|
|
||||||
err = btf__load(obj->btf);
|
/* clone BTF to sanitize a copy and leave the original intact */
|
||||||
if (err) {
|
orig_data = btf__get_raw_data(obj->btf, &sz);
|
||||||
pr_warn("Error loading %s into kernel: %d.\n",
|
san_data = malloc(sz);
|
||||||
BTF_ELF_SEC, err);
|
if (!san_data)
|
||||||
btf__free(obj->btf);
|
return -ENOMEM;
|
||||||
obj->btf = NULL;
|
memcpy(san_data, orig_data, sz);
|
||||||
/* btf_ext can't exist without btf, so free it as well */
|
kern_btf = btf__new(san_data, sz);
|
||||||
if (obj->btf_ext) {
|
if (IS_ERR(kern_btf))
|
||||||
btf_ext__free(obj->btf_ext);
|
return PTR_ERR(kern_btf);
|
||||||
obj->btf_ext = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kernel_needs_btf(obj))
|
bpf_object__sanitize_btf(obj, kern_btf);
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
err = btf__load(kern_btf);
|
||||||
|
if (sanitize) {
|
||||||
|
if (!err) {
|
||||||
|
/* move fd to libbpf's BTF */
|
||||||
|
btf__set_fd(obj->btf, btf__fd(kern_btf));
|
||||||
|
btf__set_fd(kern_btf, -1);
|
||||||
|
}
|
||||||
|
btf__free(kern_btf);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
btf_mandatory = kernel_needs_btf(obj);
|
||||||
|
pr_warn("Error loading .BTF into kernel: %d. %s\n", err,
|
||||||
|
btf_mandatory ? "BTF is mandatory, can't proceed."
|
||||||
|
: "BTF is optional, ignoring.");
|
||||||
|
if (!btf_mandatory)
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_object__elf_collect(struct bpf_object *obj)
|
static int bpf_object__elf_collect(struct bpf_object *obj)
|
||||||
@ -3777,7 +3791,7 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map)
|
|||||||
create_attr.btf_fd = 0;
|
create_attr.btf_fd = 0;
|
||||||
create_attr.btf_key_type_id = 0;
|
create_attr.btf_key_type_id = 0;
|
||||||
create_attr.btf_value_type_id = 0;
|
create_attr.btf_value_type_id = 0;
|
||||||
if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
|
if (obj->btf && btf__fd(obj->btf) >= 0 && !bpf_map_find_btf_info(obj, map)) {
|
||||||
create_attr.btf_fd = btf__fd(obj->btf);
|
create_attr.btf_fd = btf__fd(obj->btf);
|
||||||
create_attr.btf_key_type_id = map->btf_key_type_id;
|
create_attr.btf_key_type_id = map->btf_key_type_id;
|
||||||
create_attr.btf_value_type_id = map->btf_value_type_id;
|
create_attr.btf_value_type_id = map->btf_value_type_id;
|
||||||
@ -5361,18 +5375,17 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
|
|||||||
load_attr.kern_version = kern_version;
|
load_attr.kern_version = kern_version;
|
||||||
load_attr.prog_ifindex = prog->prog_ifindex;
|
load_attr.prog_ifindex = prog->prog_ifindex;
|
||||||
}
|
}
|
||||||
/* if .BTF.ext was loaded, kernel supports associated BTF for prog */
|
/* specify func_info/line_info only if kernel supports them */
|
||||||
if (prog->obj->btf_ext)
|
btf_fd = bpf_object__btf_fd(prog->obj);
|
||||||
btf_fd = bpf_object__btf_fd(prog->obj);
|
if (btf_fd >= 0 && prog->obj->caps.btf_func) {
|
||||||
else
|
load_attr.prog_btf_fd = btf_fd;
|
||||||
btf_fd = -1;
|
load_attr.func_info = prog->func_info;
|
||||||
load_attr.prog_btf_fd = btf_fd >= 0 ? btf_fd : 0;
|
load_attr.func_info_rec_size = prog->func_info_rec_size;
|
||||||
load_attr.func_info = prog->func_info;
|
load_attr.func_info_cnt = prog->func_info_cnt;
|
||||||
load_attr.func_info_rec_size = prog->func_info_rec_size;
|
load_attr.line_info = prog->line_info;
|
||||||
load_attr.func_info_cnt = prog->func_info_cnt;
|
load_attr.line_info_rec_size = prog->line_info_rec_size;
|
||||||
load_attr.line_info = prog->line_info;
|
load_attr.line_info_cnt = prog->line_info_cnt;
|
||||||
load_attr.line_info_rec_size = prog->line_info_rec_size;
|
}
|
||||||
load_attr.line_info_cnt = prog->line_info_cnt;
|
|
||||||
load_attr.log_level = prog->log_level;
|
load_attr.log_level = prog->log_level;
|
||||||
load_attr.prog_flags = prog->prog_flags;
|
load_attr.prog_flags = prog->prog_flags;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user