Commit 376ce5a9 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by Namhyung Kim
Browse files

perf header: Sanity check HEADER_NRCPUS and HEADER_CPU_DOMAIN_INFO



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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
parent 06452a41
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -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;