* [PATCH] perf evsel: Validate branch stack before byte swapping
@ 2026-09-03 10:08 ` Mark Amirkan
0 siblings, 0 replies; 3+ messages in thread
From: Mark Amirkan via B4 Relay @ 2026-09-03 10:08 UTC (permalink / raw)
To: namhyung, acme
Cc: jolsa, peterz, alexander.shishkin, adrian.hunter, linux-kernel,
mark.rutland, james.clark, mingo, linux-perf-users, irogers
From: Mark Amirkan <markdamirkan@gmail.com>
When perf reads an opposite-endian branch stack, __evsel__parse_sample()
swaps each entry before checking whether all entries fit in the event. A
truncated sample can therefore make the swap loop read and write past the
event boundary.
A truncated perf.data file makes perf report crash with SIGSEGV. ASan
reports an out-of-bounds read. A regression test puts an entry just past
the declared end and shows that its flags are changed before the parser
returns -EFAULT.
Move the bounds check before the byte-swap loop. Valid samples are handled
as before.
Fixes: 63c12ae2f246 ("perf evsel: Add bitfield_swap() to handle branch_stack endian issue")
Cc: stable@vger.kernel.org
Assisted-by: Symbolic
Signed-off-by: Mark Amirkan <markdamirkan@gmail.com>
---
tools/perf/tests/sample-parsing.c | 48 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/evsel.c | 3 ++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 32dbc484487a..583951534937 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
+#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -417,6 +418,49 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
return ret;
}
+static int test_truncated_branch_stack(void)
+{
+ struct perf_event_attr attr = {
+ .sample_type = PERF_SAMPLE_BRANCH_STACK,
+ };
+ struct {
+ struct perf_event_header header;
+ u64 nr;
+ struct branch_entry entry;
+ } input = {
+ .header = {
+ .type = PERF_RECORD_SAMPLE,
+ .size = sizeof(input.header) + sizeof(input.nr),
+ },
+ .nr = 1,
+ };
+ struct perf_sample sample;
+ struct evsel *evsel;
+ u64 flags = 1;
+ int err;
+
+ input.entry.flags.value = flags;
+ evsel = evsel__new(&attr);
+ if (!evsel)
+ return -1;
+
+ evsel->sample_size = __evsel__sample_size(attr.sample_type);
+ err = __evsel__parse_sample(evsel, (union perf_event *)&input,
+ &sample, /*needs_swap=*/true);
+ perf_sample__exit(&sample);
+ evsel__put(evsel);
+
+ if (err != -EFAULT) {
+ pr_debug("truncated branch stack returned %d, expected -EFAULT\n", err);
+ return -1;
+ }
+ if (input.entry.flags.value != flags) {
+ pr_debug("truncated branch stack modified data past the event\n");
+ return -1;
+ }
+ return 0;
+}
+
/**
* test__sample_parsing - test sample parsing.
*
@@ -433,6 +477,10 @@ static int test__sample_parsing(struct test_suite *test __maybe_unused, int subt
size_t i;
int err;
+ err = test_truncated_branch_stack();
+ if (err)
+ return err;
+
/*
* Fail the test if it has not been updated when new sample format bits
* were added. Please actually update the test rather than just change
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d4cb455f4a7d..cc0bc0857754 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -3639,6 +3639,8 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
e = (struct branch_entry *)&data->branch_stack->hw_idx;
}
+ OVERFLOW_CHECK(array, sz, max_size);
+
if (swapped) {
/*
* struct branch_flag does not have endian
@@ -3654,7 +3656,6 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
e->flags.value = evsel__bitfield_swap_branch_flags(e->flags.value);
}
- OVERFLOW_CHECK(array, sz, max_size);
array = (void *)array + sz;
if (evsel__has_branch_counters(evsel)) {
---
base-commit: aadea57f532882d8bab444646863c7ef8a778ff1
change-id: 20260903-sympwn-linux-002-final-v2-e30a8df210c1
Best regards,
--
Mark Amirkan <markdamirkan@gmail.com>
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH] perf evsel: Validate branch stack before byte swapping
@ 2026-09-03 10:08 ` Mark Amirkan
0 siblings, 0 replies; 3+ messages in thread
From: Mark Amirkan @ 2026-09-03 10:08 UTC (permalink / raw)
To: namhyung, acme
Cc: jolsa, peterz, alexander.shishkin, adrian.hunter, linux-kernel,
mark.rutland, james.clark, mingo, linux-perf-users, irogers
When perf reads an opposite-endian branch stack, __evsel__parse_sample()
swaps each entry before checking whether all entries fit in the event. A
truncated sample can therefore make the swap loop read and write past the
event boundary.
A truncated perf.data file makes perf report crash with SIGSEGV. ASan
reports an out-of-bounds read. A regression test puts an entry just past
the declared end and shows that its flags are changed before the parser
returns -EFAULT.
Move the bounds check before the byte-swap loop. Valid samples are handled
as before.
Fixes: 63c12ae2f246 ("perf evsel: Add bitfield_swap() to handle branch_stack endian issue")
Cc: stable@vger.kernel.org
Assisted-by: Symbolic
Signed-off-by: Mark Amirkan <markdamirkan@gmail.com>
---
tools/perf/tests/sample-parsing.c | 48 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/evsel.c | 3 ++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 32dbc484487a..583951534937 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
+#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -417,6 +418,49 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
return ret;
}
+static int test_truncated_branch_stack(void)
+{
+ struct perf_event_attr attr = {
+ .sample_type = PERF_SAMPLE_BRANCH_STACK,
+ };
+ struct {
+ struct perf_event_header header;
+ u64 nr;
+ struct branch_entry entry;
+ } input = {
+ .header = {
+ .type = PERF_RECORD_SAMPLE,
+ .size = sizeof(input.header) + sizeof(input.nr),
+ },
+ .nr = 1,
+ };
+ struct perf_sample sample;
+ struct evsel *evsel;
+ u64 flags = 1;
+ int err;
+
+ input.entry.flags.value = flags;
+ evsel = evsel__new(&attr);
+ if (!evsel)
+ return -1;
+
+ evsel->sample_size = __evsel__sample_size(attr.sample_type);
+ err = __evsel__parse_sample(evsel, (union perf_event *)&input,
+ &sample, /*needs_swap=*/true);
+ perf_sample__exit(&sample);
+ evsel__put(evsel);
+
+ if (err != -EFAULT) {
+ pr_debug("truncated branch stack returned %d, expected -EFAULT\n", err);
+ return -1;
+ }
+ if (input.entry.flags.value != flags) {
+ pr_debug("truncated branch stack modified data past the event\n");
+ return -1;
+ }
+ return 0;
+}
+
/**
* test__sample_parsing - test sample parsing.
*
@@ -433,6 +477,10 @@ static int test__sample_parsing(struct test_suite *test __maybe_unused, int subt
size_t i;
int err;
+ err = test_truncated_branch_stack();
+ if (err)
+ return err;
+
/*
* Fail the test if it has not been updated when new sample format bits
* were added. Please actually update the test rather than just change
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d4cb455f4a7d..cc0bc0857754 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -3639,6 +3639,8 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
e = (struct branch_entry *)&data->branch_stack->hw_idx;
}
+ OVERFLOW_CHECK(array, sz, max_size);
+
if (swapped) {
/*
* struct branch_flag does not have endian
@@ -3654,7 +3656,6 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
e->flags.value = evsel__bitfield_swap_branch_flags(e->flags.value);
}
- OVERFLOW_CHECK(array, sz, max_size);
array = (void *)array + sz;
if (evsel__has_branch_counters(evsel)) {
---
base-commit: aadea57f532882d8bab444646863c7ef8a778ff1
change-id: 20260903-sympwn-linux-002-final-v2-e30a8df210c1
Best regards,
--
Mark Amirkan <markdamirkan@gmail.com>
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] perf evsel: Validate branch stack before byte swapping
2026-09-03 10:08 ` Mark Amirkan
(?)
@ 2026-09-03 10:21 ` sashiko-bot
-1 siblings, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-09-03 10:21 UTC (permalink / raw)
To: Mark Amirkan; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Out-of-bounds memory read and write in PERF_SAMPLE_RAW parsing due to mem_bswap_64 executing before OVERFLOW_CHECK.
--
commit ecce0770a0ee5a1e1d8c053f5ba923c05679efc1
Author: Mark Amirkan <markdamirkan@gmail.com>
perf evsel: Validate branch stack before byte swapping
This commit fixes an out-of-bounds read/write in __evsel__parse_sample()
when handling opposite-endian branch stacks. It moves the overflow check
before the byte-swap loop to prevent truncated samples from causing memory
corruption.
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index d4cb455f4a7d4..cc0bc08577540 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
[ ... ]
> @@ -3639,6 +3639,8 @@ int __evsel__parse_sample(struct evsel *evsel, union perf_event *event,
> e = (struct branch_entry *)&data->branch_stack->hw_idx;
> }
>
> + OVERFLOW_CHECK(array, sz, max_size);
> +
[Severity: Critical]
This isn't a bug introduced by this patch, but while you are fixing the
byte-swapping bounds check for PERF_SAMPLE_BRANCH_STACK, does the exact
same vulnerability exist just above this code for PERF_SAMPLE_RAW?
In tools/perf/util/evsel.c:__evsel__parse_sample() when handling
PERF_SAMPLE_RAW:
if (swapped)
mem_bswap_64((void *) array, data->raw_size);
array = (void *)array + sizeof(u32);
OVERFLOW_CHECK(array, data->raw_size, max_size);
Could an attacker provide a maliciously crafted cross-endian perf.data file
with a spoofed raw_size that exceeds the actual event size? Because
mem_bswap_64 performs in-place byte swapping before the OVERFLOW_CHECK
validates the untrusted raw_size, it appears it would blindly read,
byte-swap, and write memory past the event payload boundary.
> if (swapped) {
> /*
> * struct branch_flag does not have endian
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260903-sympwn-linux-002-final-v2-v1-1-7c6e4166b814@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-03 10:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 10:08 [PATCH] perf evsel: Validate branch stack before byte swapping Mark Amirkan via B4 Relay
2026-09-03 10:08 ` Mark Amirkan
2026-09-03 10:21 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.