libbpf: Use mmap to parse vmlinux BTF from sysfs

Teach libbpf to use mmap when parsing vmlinux BTF from /sys. We don't
apply this to fall-back paths on the regular file system because there
is no way to ensure that modifications underlying the MAP_PRIVATE
mapping are not visible to the process.

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Tested-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250520-vmlinux-mmap-v5-3-e8c941acc414@isovalent.com
This commit is contained in:
Lorenz Bauer 2025-05-20 14:01:19 +01:00 committed by Andrii Nakryiko
parent 828226b69f
commit 3c0421c93c

View file

@ -12,6 +12,7 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/btf.h> #include <linux/btf.h>
@ -120,6 +121,9 @@ struct btf {
/* whether base_btf should be freed in btf_free for this instance */ /* whether base_btf should be freed in btf_free for this instance */
bool owns_base; bool owns_base;
/* whether raw_data is a (read-only) mmap */
bool raw_data_is_mmap;
/* BTF object FD, if loaded into kernel */ /* BTF object FD, if loaded into kernel */
int fd; int fd;
@ -951,6 +955,17 @@ static bool btf_is_modifiable(const struct btf *btf)
return (void *)btf->hdr != btf->raw_data; return (void *)btf->hdr != btf->raw_data;
} }
static void btf_free_raw_data(struct btf *btf)
{
if (btf->raw_data_is_mmap) {
munmap(btf->raw_data, btf->raw_size);
btf->raw_data_is_mmap = false;
} else {
free(btf->raw_data);
}
btf->raw_data = NULL;
}
void btf__free(struct btf *btf) void btf__free(struct btf *btf)
{ {
if (IS_ERR_OR_NULL(btf)) if (IS_ERR_OR_NULL(btf))
@ -970,7 +985,7 @@ void btf__free(struct btf *btf)
free(btf->types_data); free(btf->types_data);
strset__free(btf->strs_set); strset__free(btf->strs_set);
} }
free(btf->raw_data); btf_free_raw_data(btf);
free(btf->raw_data_swapped); free(btf->raw_data_swapped);
free(btf->type_offs); free(btf->type_offs);
if (btf->owns_base) if (btf->owns_base)
@ -1030,7 +1045,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf)
return libbpf_ptr(btf_new_empty(base_btf)); return libbpf_ptr(btf_new_empty(base_btf));
} }
static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, bool is_mmap)
{ {
struct btf *btf; struct btf *btf;
int err; int err;
@ -1050,12 +1065,18 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
btf->start_str_off = base_btf->hdr->str_len; btf->start_str_off = base_btf->hdr->str_len;
} }
btf->raw_data = malloc(size); if (is_mmap) {
if (!btf->raw_data) { btf->raw_data = (void *)data;
err = -ENOMEM; btf->raw_data_is_mmap = true;
goto done; } else {
btf->raw_data = malloc(size);
if (!btf->raw_data) {
err = -ENOMEM;
goto done;
}
memcpy(btf->raw_data, data, size);
} }
memcpy(btf->raw_data, data, size);
btf->raw_size = size; btf->raw_size = size;
btf->hdr = btf->raw_data; btf->hdr = btf->raw_data;
@ -1083,12 +1104,12 @@ done:
struct btf *btf__new(const void *data, __u32 size) struct btf *btf__new(const void *data, __u32 size)
{ {
return libbpf_ptr(btf_new(data, size, NULL)); return libbpf_ptr(btf_new(data, size, NULL, false));
} }
struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf) struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf)
{ {
return libbpf_ptr(btf_new(data, size, base_btf)); return libbpf_ptr(btf_new(data, size, base_btf, false));
} }
struct btf_elf_secs { struct btf_elf_secs {
@ -1209,7 +1230,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
if (secs.btf_base_data) { if (secs.btf_base_data) {
dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size, dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size,
NULL); NULL, false);
if (IS_ERR(dist_base_btf)) { if (IS_ERR(dist_base_btf)) {
err = PTR_ERR(dist_base_btf); err = PTR_ERR(dist_base_btf);
dist_base_btf = NULL; dist_base_btf = NULL;
@ -1218,7 +1239,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
} }
btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size, btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size,
dist_base_btf ?: base_btf); dist_base_btf ?: base_btf, false);
if (IS_ERR(btf)) { if (IS_ERR(btf)) {
err = PTR_ERR(btf); err = PTR_ERR(btf);
goto done; goto done;
@ -1335,7 +1356,7 @@ static struct btf *btf_parse_raw(const char *path, struct btf *base_btf)
} }
/* finally parse BTF data */ /* finally parse BTF data */
btf = btf_new(data, sz, base_btf); btf = btf_new(data, sz, base_btf, false);
err_out: err_out:
free(data); free(data);
@ -1354,6 +1375,37 @@ struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf)
return libbpf_ptr(btf_parse_raw(path, base_btf)); return libbpf_ptr(btf_parse_raw(path, base_btf));
} }
static struct btf *btf_parse_raw_mmap(const char *path, struct btf *base_btf)
{
struct stat st;
void *data;
struct btf *btf;
int fd, err;
fd = open(path, O_RDONLY);
if (fd < 0)
return libbpf_err_ptr(-errno);
if (fstat(fd, &st) < 0) {
err = -errno;
close(fd);
return libbpf_err_ptr(err);
}
data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
err = -errno;
close(fd);
if (data == MAP_FAILED)
return libbpf_err_ptr(err);
btf = btf_new(data, st.st_size, base_btf, true);
if (IS_ERR(btf))
munmap(data, st.st_size);
return btf;
}
static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext) static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext)
{ {
struct btf *btf; struct btf *btf;
@ -1618,7 +1670,7 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf)
goto exit_free; goto exit_free;
} }
btf = btf_new(ptr, btf_info.btf_size, base_btf); btf = btf_new(ptr, btf_info.btf_size, base_btf, false);
exit_free: exit_free:
free(ptr); free(ptr);
@ -1658,10 +1710,8 @@ struct btf *btf__load_from_kernel_by_id(__u32 id)
static void btf_invalidate_raw_data(struct btf *btf) static void btf_invalidate_raw_data(struct btf *btf)
{ {
if (btf->raw_data) { if (btf->raw_data)
free(btf->raw_data); btf_free_raw_data(btf);
btf->raw_data = NULL;
}
if (btf->raw_data_swapped) { if (btf->raw_data_swapped) {
free(btf->raw_data_swapped); free(btf->raw_data_swapped);
btf->raw_data_swapped = NULL; btf->raw_data_swapped = NULL;
@ -5331,7 +5381,10 @@ struct btf *btf__load_vmlinux_btf(void)
pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n", pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n",
sysfs_btf_path); sysfs_btf_path);
} else { } else {
btf = btf__parse(sysfs_btf_path, NULL); btf = btf_parse_raw_mmap(sysfs_btf_path, NULL);
if (IS_ERR(btf))
btf = btf__parse(sysfs_btf_path, NULL);
if (!btf) { if (!btf) {
err = -errno; err = -errno;
pr_warn("failed to read kernel BTF from '%s': %s\n", pr_warn("failed to read kernel BTF from '%s': %s\n",