Linux Perf Users
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ian Rogers" <irogers@google.com>
Cc: bpf@vger.kernel.org, linux-perf-users@vger.kernel.org
Subject: Re: [RFC PATCH v1 5/8] perf tools: Internal support for BUILD_ID_OFFSET
Date: Fri, 07 Aug 2026 08:41:42 +0000	[thread overview]
Message-ID: <20260807084143.0FE6B1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260807071818.718751-6-irogers@google.com>

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] Synthesizing an event with PERF_SAMPLE_CALLCHAIN_BUILD_ID_OFFSET but without PERF_SAMPLE_CALLCHAIN causes a NULL pointer dereference.
- [High] Copying `callchain_bids` during event synthesis uses the potentially unrelated length `sample->callchain->nr`, leading to a heap buffer over-read.
- [High] Array access on `sample->callchain_bids` uses the unverified index `j` derived from `chain->nr`, leading to a heap buffer over-read.
- [High] `add_callchain_ip` ignores the `bid` and `offset` arguments when `cpumode` is set, breaking callchain resolution for `PERF_SAMPLE_CALLCHAIN_BUILD_ID_OFFSET`.
- [High] Missing `OVERFLOW_CHECK` when parsing `PERF_SAMPLE_BUILD_ID_OFFSET` allows out-of-bounds reads on malformed events.
- [Medium] `__evsel__parse_sample` fails to unswap the byte layout of `struct perf_build_id` when `swapped` is true, corrupting cross-endian perf.data analysis.
- [Medium] The test suite in `sample-parsing.c` is not updated for the new flags, causing immediate failure.
- [Medium] `thread__find_map` fails to resolve build IDs because `sample->ip` takes precedence over `sample->bid.bid`, misinterpreting the offset as a virtual address.
- [Medium] `leaf_frame_caller` incorrectly inherits the build ID and offset of its callee in `thread__resolve_callchain_sample`.
--

--- Patch [5]: [RFC PATCH v1 5/8] perf tools: Internal support for BUILD_ID_OFFSET ---
commit fbd06af754237e67a7a1661b582808d4407ddb3e
Author: Ian Rogers <irogers@google.com>
Subject: perf tools: Internal support for BUILD_ID_OFFSET

This patch implements user-space parsing for the new sample types in evsel.
It updates perf_event_attr_fprintf to display the new bits in 'perf report -D'
and updates synthetic event parameter names.

[Severity: Medium]
There appears to be an omission outside of the files modified by this patch:
Has test__sample_parsing() in tools/perf/tests/sample-parsing.c been
updated for the new flags? If PERF_SAMPLE_MAX was updated, the hardcoded
safety check:

    if (PERF_SAMPLE_MAX > PERF_SAMPLE_WEIGHT_STRUCT << 1)

might cause the test suite to unconditionally abort.

> diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
> --- a/tools/perf/util/event.c
> +++ b/tools/perf/util/event.c
> @@ -740,15 +734,33 @@ struct map *thread__find_map(struct thread *thread, struct perf_sample *sample,
>  
>  		return NULL;
>  	}
> -	al->map = maps__find(maps, al->addr);
> -	if (al->map != NULL) {
> -		/*
> -		 * Kernel maps might be changed when loading symbols so loading
> -		 * must be done prior to using kernel maps.
> -		 */
> -		if (load_map)
> -			map__load(al->map);
> -		al->addr = map__map_ip(al->map, al->addr);
> +
> +	if (sample->ip) {
> +		al->addr = sample->ip;
> +		al->map = maps__find(maps, al->addr);

[Severity: Medium]
When resolving callchains, sample->ip is typically populated with the file
offset. Because sample->ip takes precedence in this if/else chain, will the
offset be incorrectly evaluated as a virtual address via maps__find(),
causing the resolution to skip maps__find_by_build_id() entirely?

> +		if (al->map != NULL) {

[ ... ]

> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -3778,6 +3778,30 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
>  		array = (void *)array + sz;
>  	}
>  
> +	if (type & PERF_SAMPLE_BUILD_ID_OFFSET) {
> +		data->bid.bid = (struct perf_build_id *) array;

[Severity: Medium]
During cross-platform analysis of perf.data files from a different
endianness, the event array is 64-bit byte-swapped prior to parsing. Since
struct perf_build_id contains mixed-size fields (8-bit, 16-bit, 32-bit,
and a byte array), does this code need to unswap the layout similar to how
PERF_SAMPLE_AUX uses mem_bswap_64() to prevent data corruption?

> +		array += sizeof(struct perf_build_id) / sizeof(u64);
> +		data->bid.offset = *array;
> +		array++;

[Severity: High]
Should there be an OVERFLOW_CHECK_u64(array) before dereferencing array
for the offset? Since PERF_SAMPLE_BUILD_ID_OFFSET is not part of
PERF_SAMPLE_MASK, its size isn't included in evsel->sample_size,
which could allow an out-of-bounds read on truncated events.

> +	} else {
> +		data->bid.bid = NULL;
> +		data->bid.offset = 0;
> +	}

[ ... ]

> diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> --- a/tools/perf/util/machine.c
> +++ b/tools/perf/util/machine.c
> @@ -2216,7 +2216,9 @@ static int add_callchain_ip(struct thread *thread,
>  			    struct branch_flags *flags,
>  			    struct iterations *iter,
>  			    u64 branch_from,
> -			    bool symbols)
> +			    bool symbols,
> +			      struct perf_build_id *bid,
> +			      u64 offset)
>  {
>  	struct map_symbol ms = {};
>  	struct addr_location al;
> @@ -2230,7 +2232,9 @@ static int add_callchain_ip(struct thread *thread,
>  	al.srcline = NULL;
>  	if (!cpumode) {
>  		thread__find_cpumode_addr_location(thread,
> -					   &(struct perf_sample){.ip = ip},
> +					   &(struct perf_sample){.ip = ip,
> +								 .bid = { .bid = bid,
> +									  .offset = offset }},
>  					   symbols, &al);
>  	} else {

[Severity: High]
In thread__resolve_callchain_sample(), cpumode is initialized locally and
its address (&cpumode) is passed to add_callchain_ip(), meaning cpumode is
never NULL. Doesn't this force execution into the else branch here?

The else branch constructs a temporary struct perf_sample that omits the
new bid and offset parameters, which silently drops the data before
map/symbol resolution.

[ ... ]

> @@ -2979,13 +3003,19 @@ static int thread__resolve_callchain_sample(struct thread *thread,
>  			continue;
>  #endif
>  		ip = chain->ips[j];
> +		if (sample->callchain_bids) {
> +			bid = (struct perf_build_id *)&((u64 *)sample->callchain_bids)[j * 4];
> +			offset = ((u64 *)sample->callchain_bids)[j * 4 + 3];
> +		}

[Severity: High]
Since the loop index j is bounded by chain->nr rather than
sample->callchain_bids_nr, could a malformed event where chain->nr exceeds
the actual length of callchain_bids cause a heap buffer over-read here?

>  		if (ip < PERF_CONTEXT_MAX)
>                         ++nr_entries;

[ ... ]

> @@ -3011,21 +3041,28 @@ static int thread__resolve_callchain_sample(struct thread *thread,
>  
>  				err = add_callchain_ip(thread, cursor, parent,
>  						root_al, &cpumode, leaf_frame_caller,
> -						false, NULL, NULL, 0, symbols);
> -				if (err)
> -					return (err < 0) ? err : 0;
> +						false, NULL, NULL, 0, symbols, bid, offset);

[Severity: Medium]
When synthesizing a missing caller frame (leaf_frame_caller), this passes
the bid and offset variables belonging to the current frame (j, the callee)
into add_callchain_ip(). Does this incorrectly associate the synthesized
caller's IP with the callee's build ID?

> +				if (err) {
> +					err = (err < 0) ? err : 0;
> +					goto out;
> +				}

[ ... ]

> diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
> --- a/tools/perf/util/synthetic-events.c
> +++ b/tools/perf/util/synthetic-events.c
> @@ -1701,6 +1701,13 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
>  		result += sample->aux_sample.size;
>  	}
>  
> +	if (type & PERF_SAMPLE_BUILD_ID_OFFSET)
> +		result += sizeof(struct perf_build_id) + sizeof(u64);
> +
> +	if (type & PERF_SAMPLE_CALLCHAIN_BUILD_ID_OFFSET)
> +		result += sizeof(u64) + sample->callchain->nr *
> +			  (sizeof(struct perf_build_id) + sizeof(u64));
> +
>  	return result;
>  }

[Severity: High]
If an event contains PERF_SAMPLE_CALLCHAIN_BUILD_ID_OFFSET but not
PERF_SAMPLE_CALLCHAIN, sample->callchain will be NULL. Does this sequence
unconditionally dereference sample->callchain->nr and cause a crash?

[ ... ]

> @@ -1933,6 +1940,19 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo
>  		array = (void *)array + sz;
>  	}
>  
> +	if (type & PERF_SAMPLE_BUILD_ID_OFFSET) {
> +		memcpy(array, sample->bid.bid, sizeof(struct perf_build_id));
> +		array += sizeof(struct perf_build_id) / sizeof(u64);
> +		*array++ = sample->bid.offset;
> +	}
> +
> +	if (type & PERF_SAMPLE_CALLCHAIN_BUILD_ID_OFFSET) {
> +		*array++ = sample->callchain->nr;
> +		sz = sample->callchain->nr * (sizeof(struct perf_build_id) + sizeof(u64));
> +		memcpy(array, sample->callchain_bids, sz);

[Severity: High]
Should this use sample->callchain_bids_nr instead of sample->callchain->nr
for the copy size calculation? If a malformed event provides a chain->nr
that is larger than the true bounds of callchain_bids, could this
copy out-of-bounds heap memory into the synthesized event stream?

> +		array += sz / sizeof(u64);
> +	}
> +
>  	return 0;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260807071818.718751-1-irogers@google.com?part=5

  reply	other threads:[~2026-08-07  8:41 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-07  7:18 [RFC PATCH v1 0/8] perf/core, perf/tools: Add PERF_SAMPLE_BUILD_ID_OFFSET support Ian Rogers
2026-08-07  7:18 ` [RFC PATCH v1 1/8] perf event: Factor build_id out into its own top-level struct Ian Rogers
2026-08-07  7:33   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 2/8] perf/core: Add BUILD_ID_OFFSET to UAPI Ian Rogers
2026-08-07  7:42   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 3/8] perf/core: Implement BUILD_ID_OFFSET sample type Ian Rogers
2026-08-07  7:54   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 4/8] perf: Refactor thread map and symbol APIs to take perf_sample Ian Rogers
2026-08-07  8:20   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 5/8] perf tools: Internal support for BUILD_ID_OFFSET Ian Rogers
2026-08-07  8:41   ` sashiko-bot [this message]
2026-08-07  7:18 ` [RFC PATCH v1 6/8] perf inject: Extend perf inject to support bid_offset conversion Ian Rogers
2026-08-07  9:18   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 7/8] perf record: Add --buildid-offset option Ian Rogers
2026-08-07  9:47   ` sashiko-bot
2026-08-07  7:18 ` [RFC PATCH v1 8/8] perf tests: Add build_id_offset test coverage Ian Rogers
2026-08-07  9:58   ` sashiko-bot
2026-08-07 11:18 ` [RFC PATCH v1 0/8] perf/core, perf/tools: Add PERF_SAMPLE_BUILD_ID_OFFSET support Peter Zijlstra

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260807084143.0FE6B1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=irogers@google.com \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox