]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf bpf: Validate array presence before casting BPF prog info pointers
authorArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 13 Jun 2026 18:25:11 +0000 (15:25 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 17 Jun 2026 12:21:03 +0000 (09:21 -0300)
Several functions cast bpf_prog_info fields (jited_ksyms,
jited_func_lens, jited_prog_insns) from u64 to pointers and
dereference them.  These fields are only valid pointers if
bpil_offs_to_addr() converted their file offsets to addresses, which
only happens when the corresponding PERF_BPIL_* bits are set in
info_linear->arrays.

A crafted perf.data can leave these bits unset while setting non-zero
counts and offset values, causing the functions to dereference raw file
offsets as pointers.

Add array bitmask validation to all perf.data processing paths:

  - __bpf_event__print_bpf_prog_info(): check JITED_KSYMS and
    JITED_FUNC_LENS (changed to take struct perf_bpil *)
  - machine__process_bpf_event_load(): check JITED_KSYMS
  - bpf_read(): check JITED_INSNS before memcpy from jited_prog_insns
  - dso__disassemble_filename(): check JITED_INSNS before returning
    jited_prog_insns pointer

Fixes: f8dfeae009effc0b ("perf bpf: Show more BPF program info in print_bpf_prog_info()")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Song Liu <songliubraving@fb.com>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/bpf-event.c
tools/perf/util/bpf-event.h
tools/perf/util/dso.c
tools/perf/util/header.c

index 57d53ba848359e122690a06793089400a8cc141c..fa3ebc8ea7f09cddac6361f5054eb1ccfc2ba295 100644 (file)
@@ -59,6 +59,10 @@ static int machine__process_bpf_event_load(struct machine *machine,
                return 0;
        info_linear = info_node->info_linear;
 
+       /* jited_ksyms is only valid if bpil_offs_to_addr() converted it */
+       if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_KSYMS)))
+               return 0;
+
        for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) {
                u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms);
                u64 addr = addrs[i];
@@ -959,12 +963,15 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env)
        return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env);
 }
 
-void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
+void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear,
                                      struct perf_env *env,
                                      FILE *fp)
 {
-       __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens);
-       __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
+       struct bpf_prog_info *info = &info_linear->info;
+       __u64 required_arrays = (1UL << PERF_BPIL_JITED_KSYMS) |
+                               (1UL << PERF_BPIL_JITED_FUNC_LENS);
+       __u32 *prog_lens;
+       __u64 *prog_addrs;
        char name[KSYM_NAME_LEN];
        struct btf *btf = NULL;
        u32 sub_prog_cnt, i;
@@ -974,6 +981,13 @@ void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
            sub_prog_cnt != info->nr_jited_func_lens)
                return;
 
+       /* Ensure the arrays were present and converted by bpil_offs_to_addr() */
+       if ((info_linear->arrays & required_arrays) != required_arrays)
+               return;
+
+       prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens);
+       prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
+
        if (info->btf_id) {
                struct btf_node *node;
 
index 60d2c6637af5d6eb00ca22df8ad6659d082054ec..da4eeb4a1a73208c2310618cf3b6ca29b0f58c73 100644 (file)
@@ -40,7 +40,7 @@ struct btf_node {
 int machine__process_bpf(struct machine *machine, union perf_event *event,
                         struct perf_sample *sample);
 int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env);
-void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
+void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear,
                                      struct perf_env *env,
                                      FILE *fp);
 void bpf_metadata_free(struct bpf_metadata *metadata);
@@ -58,7 +58,7 @@ static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused,
        return 0;
 }
 
-static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused,
+static inline void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear __maybe_unused,
                                                    struct perf_env *env __maybe_unused,
                                                    FILE *fp __maybe_unused)
 {
index 1a2fc6d18da74d6c5b70ed4f7835ff694d2f6b28..79f1a30f3683d6b32321d4dbca964b9b468e376d 100644 (file)
@@ -880,6 +880,12 @@ static ssize_t bpf_read(struct dso *dso, u64 offset, char *data)
                return -1;
        }
 
+       /* jited_prog_insns is only valid if bpil_offs_to_addr() converted it */
+       if (!(node->info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) {
+               dso__data(dso)->status = DSO_DATA_STATUS_ERROR;
+               return -1;
+       }
+
        len = node->info_linear->info.jited_prog_len;
        buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns;
 
@@ -1995,6 +2001,10 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
                        return NULL;
                }
                info_linear = info_node->info_linear;
+               if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) {
+                       errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
+                       return NULL;
+               }
                assert(len <= info_linear->info.jited_prog_len);
                *out_buf_len = len;
                return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns);
index d7f41db7322cbcb46b1f7551adb56dedf3c6afaf..091d8f7f6bd2c9d5ea98006d3fc37f490b5827c6 100644 (file)
@@ -2107,8 +2107,7 @@ static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp)
                node = rb_entry(next, struct bpf_prog_info_node, rb_node);
                next = rb_next(&node->rb_node);
 
-               __bpf_event__print_bpf_prog_info(&node->info_linear->info,
-                                                env, fp);
+               __bpf_event__print_bpf_prog_info(node->info_linear, env, fp);
        }
 
        up_read(&env->bpf_progs.lock);