[PATCH 12/13] perf header: Sanity check HEADER_BPF_PROG_INFO

Arnaldo Carvalho de Melo
2026-04-10T00:41:26Z

Add validation to process_bpf_prog_info() to harden against malformed
perf.data files:

- Upper bound on BPF program count (max 131072)
- Upper bound on per-program data_len (max 256MB)

Cc: Ian Rogers <irogers@google.com>
Assisted-by: Claude Code:claude-opus-4-6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/header.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 03426132e58dc933..628d091658c8c40e 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -3522,6 +3522,19 @@ static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data _
if (do_read_u32(ff, &count))
return -1;

+#define MAX_BPF_PROGS 131072
+ if (count > MAX_BPF_PROGS) {
+ pr_err("Invalid HEADER_BPF_PROG_INFO: count (%u) > %u\n",
+ count, MAX_BPF_PROGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + count * (2 * sizeof(u32) + sizeof(u64))) {
+ pr_err("Invalid HEADER_BPF_PROG_INFO: section too small (%zu) for %u entries\n",
+ ff->size, count);
+ return -1;
+ }
+
down_write(&env->bpf_progs.lock);

for (i = 0; i < count; ++i) {
@@ -3539,6 +3552,13 @@ static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data _
goto out;
}

+#define MAX_BPF_DATA_LEN (256 * 1024 * 1024)
+ if (data_len > MAX_BPF_DATA_LEN) {
+ pr_warning("Invalid HEADER_BPF_PROG_INFO: data_len (%u) too large\n",
+ data_len);
+ goto out;
+ }
+
info_linear = malloc(sizeof(struct perf_bpil) +
data_len);
if (!info_linear)
--
2.53.0