]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf header: Sanity check HEADER_NRCPUS and HEADER_CPU_DOMAIN_INFO
authorArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 10 Apr 2026 22:08:55 +0000 (19:08 -0300)
committerNamhyung Kim <namhyung@kernel.org>
Tue, 14 Apr 2026 06:21:52 +0000 (23:21 -0700)
While working on some cleanups sashiko questioned about pre-existing
issues, namely lacking sanity checks for perf.data headers, add some
with the help of Claude.

Cc: Ian Rogers <irogers@google.com>
Cc: Swapnil Sapkal <swapnil.sapkal@amd.com>
Assisted-by: Claude Code:claude-opus-4-6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/util/header.c

index 22c44b6f0b098f95f06ac68b8aeff6306eabe98f..4cb748763c8a0741a5d64cf6d5ac8c04f125c2b9 100644 (file)
@@ -63,6 +63,8 @@
 #include <event-parse.h>
 #endif
 
+#define MAX_SCHED_DOMAINS      64
+
 /*
  * magic2 = "PERFILE2"
  * must be a numerical value to let the endianness
@@ -2722,6 +2724,13 @@ static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused)
        ret = do_read_u32(ff, &nr_cpus_online);
        if (ret)
                return ret;
+
+       if (nr_cpus_online > nr_cpus_avail) {
+               pr_err("Invalid HEADER_NRCPUS: nr_cpus_online (%u) > nr_cpus_avail (%u)\n",
+                      nr_cpus_online, nr_cpus_avail);
+               return -1;
+       }
+
        env->nr_cpus_avail = (int)nr_cpus_avail;
        env->nr_cpus_online = (int)nr_cpus_online;
        return 0;
@@ -3698,6 +3707,17 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused
        nra = env->nr_cpus_avail;
        nr = env->nr_cpus_online;
 
+       if (nra == 0 || nr == 0) {
+               pr_err("Invalid HEADER_CPU_DOMAIN_INFO: missing HEADER_NRCPUS\n");
+               return -1;
+       }
+
+       if (ff->size < 2 * sizeof(u32) + nr * 2 * sizeof(u32)) {
+               pr_err("Invalid HEADER_CPU_DOMAIN_INFO: section too small (%zu) for %u CPUs\n",
+                      (size_t)ff->size, nr);
+               return -1;
+       }
+
        cd_map = calloc(nra, sizeof(*cd_map));
        if (!cd_map)
                return -1;
@@ -3714,6 +3734,18 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused
        if (ret)
                return ret;
 
+       /*
+        * Sanity check: real systems have at most ~10 sched domain levels
+        * (SMT, CLS, MC, PKG + NUMA hops). Reject obviously bogus values
+        * from malformed perf.data files before they cause excessive
+        * allocation in the per-CPU loop.
+        */
+       if (max_sched_domains > MAX_SCHED_DOMAINS) {
+               pr_err("Invalid HEADER_CPU_DOMAIN_INFO: max_sched_domains %u > %u\n",
+                      max_sched_domains, MAX_SCHED_DOMAINS);
+               return -1;
+       }
+
        env->max_sched_domains = max_sched_domains;
 
        for (i = 0; i < nr; i++) {
@@ -3725,6 +3757,11 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused
                        return -1;
                }
 
+               if (cd_map[cpu]) {
+                       pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate cpu %u\n", cpu);
+                       return -1;
+               }
+
                cd_map[cpu] = zalloc(sizeof(*cd_map[cpu]));
                if (!cd_map[cpu])
                        return -1;
@@ -3760,7 +3797,13 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused
                        if (!d_info)
                                return -1;
 
-                       assert(cd_map[cpu]->domains[domain] == NULL);
+                       if (cd_map[cpu]->domains[domain]) {
+                               pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate domain %u for cpu %u\n",
+                                      domain, cpu);
+                               free(d_info);
+                               return -1;
+                       }
+
                        cd_map[cpu]->domains[domain] = d_info;
                        d_info->domain = domain;