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.

Acked-by: Andrii Nakryiko <and...@kernel.org>
Tested-by: Alan Maguire <alan.magu...@oracle.com>
Signed-off-by: Lorenz Bauer <l...@isovalent.com>
---
 tools/lib/bpf/btf.c | 89 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 71 insertions(+), 18 deletions(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 
f18d7e6a453cd9e5c384487659df04f7efafdf5a..3b98ac40bbd66c4fa688f967f9370b5c92610ba0
 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -12,6 +12,7 @@
 #include <sys/utsname.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/btf.h>
@@ -120,6 +121,9 @@ struct btf {
        /* whether base_btf should be freed in btf_free for this instance */
        bool owns_base;
 
+       /* whether raw_data is a (read-only) mmap */
+       bool raw_data_is_mmap;
+
        /* BTF object FD, if loaded into kernel */
        int fd;
 
@@ -951,6 +955,17 @@ static bool btf_is_modifiable(const struct btf *btf)
        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)
 {
        if (IS_ERR_OR_NULL(btf))
@@ -970,7 +985,7 @@ void btf__free(struct btf *btf)
                free(btf->types_data);
                strset__free(btf->strs_set);
        }
-       free(btf->raw_data);
+       btf_free_raw_data(btf);
        free(btf->raw_data_swapped);
        free(btf->type_offs);
        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));
 }
 
-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;
        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->raw_data = malloc(size);
-       if (!btf->raw_data) {
-               err = -ENOMEM;
-               goto done;
+       if (is_mmap) {
+               btf->raw_data = (void *)data;
+               btf->raw_data_is_mmap = true;
+       } 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->hdr = btf->raw_data;
@@ -1083,12 +1104,12 @@ static struct btf *btf_new(const void *data, __u32 
size, struct btf *base_btf)
 
 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)
 {
-       return libbpf_ptr(btf_new(data, size, base_btf));
+       return libbpf_ptr(btf_new(data, size, base_btf, false));
 }
 
 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) {
                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)) {
                        err = PTR_ERR(dist_base_btf);
                        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,
-                     dist_base_btf ?: base_btf);
+                     dist_base_btf ?: base_btf, false);
        if (IS_ERR(btf)) {
                err = PTR_ERR(btf);
                goto done;
@@ -1335,7 +1356,7 @@ static struct btf *btf_parse_raw(const char *path, struct 
btf *base_btf)
        }
 
        /* finally parse BTF data */
-       btf = btf_new(data, sz, base_btf);
+       btf = btf_new(data, sz, base_btf, false);
 
 err_out:
        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));
 }
 
+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)
 {
        struct btf *btf;
@@ -1618,7 +1670,7 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf 
*base_btf)
                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:
        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)
 {
-       if (btf->raw_data) {
-               free(btf->raw_data);
-               btf->raw_data = NULL;
-       }
+       if (btf->raw_data)
+               btf_free_raw_data(btf);
        if (btf->raw_data_swapped) {
                free(btf->raw_data_swapped);
                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",
                        sysfs_btf_path);
        } 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) {
                        err = -errno;
                        pr_warn("failed to read kernel BTF from '%s': %s\n",

-- 
2.49.0


Reply via email to