* [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes
@ 2026-06-02 23:56 Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 1/8] perf sample: Add file_offset field to struct perf_sample Arnaldo Carvalho de Melo
` (7 more replies)
0 siblings, 8 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:56 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo
When perf report, perf sched, or perf timechart skip a malformed or
unprocessable event, the warning message doesn't say where in the
perf.data file the problem occurred. This makes it hard to
cross-reference with 'perf report -D' output or to locate the
corrupted region with a hex editor.
This series adds a file_offset field to struct perf_sample, set in the
event delivery path (including the deferred callchain path), and
retrofits all skip/stop/error messages to include:
- The file offset where the event was found
- The event type name via perf_event__name() with the numeric
type value in parentheses
For example, instead of:
problem processing 10 event, skipping it.
a user now sees:
WARNING: at offset 0x1a3f0: MMAP2 (10) event size 24 too small (min 64), skipping
The peek_event() path, which validates events during initial file
scanning, also gains file offsets in its three warning messages
(misaligned size, unsupported type, undersized event).
Two pre-existing bugs found by sashiko-bot are fixed:
- builtin-timechart.c cat_backtrace(): use-after-free and
double-free when an invalid callchain context triggers zfree()
before fclose() on an open_memstream buffer. The open_memstream
contract requires fclose() before the buffer can be freed — see
open_memstream(3).
- builtin-sched.c: three BUG_ON(cpu >= MAX_CPUS || cpu < 0)
that abort perf sched when PERF_SAMPLE_CPU is absent from the
sample type and the CPU sentinel (u32)-1 is cast to signed -1.
perf.data is untrusted input — a corrupted or truncated file
should produce a warning, not an abort.
Arnaldo Carvalho de Melo (8):
perf sample: Add file_offset field to struct perf_sample
perf session: Include file offset in event skip/stop messages
perf sched: Include file offset in event skip messages
perf timechart: Include file offset in CPU bounds check messages
perf tools: Include file offset and event type name in skip messages
perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain
perf sched: Replace BUG_ON on invalid CPU with graceful skip
perf test: Add file offset diagnostic test for corrupted perf.data
15 files changed, 261 insertions(+), 101 deletions(-)
Developed with AI assistance (Claude/sashiko), tagged in commits.
Best regards,
- Arnaldo
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/8] perf sample: Add file_offset field to struct perf_sample
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 2/8] perf session: Include file offset in event skip/stop messages Arnaldo Carvalho de Melo
` (6 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add a file_offset field to struct perf_sample so that event processing
callbacks can report the byte offset of the problematic event in
perf.data, letting users cross-reference with 'perf report -D' output.
Set sample.file_offset in perf_session__deliver_event(), which is the
common entry point for both file mode (mmap'd offset) and pipe mode
(running byte counter from __perf_session__process_pipe_events).
The assignment is placed after evsel__parse_sample(), which zeroes
the struct via memset.
Preserve file_offset through the deferred callchain delivery path by
storing it in struct deferred_event and restoring it after
evlist__parse_sample() in both evlist__deliver_deferred_callchain()
and session__flush_deferred_samples().
Subsequent patches will use this field in skip/stop warning messages.
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/sample.h | 2 ++
tools/perf/util/session.c | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
index e556c9b656ea9cd6..c4eae8b2fd06035a 100644
--- a/tools/perf/util/sample.h
+++ b/tools/perf/util/sample.h
@@ -158,6 +158,8 @@ struct perf_sample {
u64 code_page_size;
/** @cgroup: The sample event PERF_SAMPLE_CGROUP value. */
u64 cgroup;
+ /** @file_offset: Byte offset of this event in the perf.data file. */
+ u64 file_offset;
/** @flags: Extra flag data from auxiliary events like intel-pt. */
u32 flags;
/** @machine_pid: The guest machine pid derived from the sample id. */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index e2e821b77766dbfc..7996787d742e32c6 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1824,6 +1824,7 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool
struct deferred_event {
struct list_head list;
union perf_event *event;
+ u64 file_offset;
};
/*
@@ -1858,6 +1859,7 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist,
perf_sample__exit(&orig_sample);
break;
}
+ orig_sample.file_offset = de->file_offset;
if (sample->tid != orig_sample.tid) {
perf_sample__exit(&orig_sample);
@@ -1906,6 +1908,7 @@ static int session__flush_deferred_samples(struct perf_session *session,
perf_sample__exit(&sample);
break;
}
+ sample.file_offset = de->file_offset;
sample.evsel = evlist__id2evsel(evlist, sample.id);
ret = evlist__deliver_sample(evlist, tool, de->event,
@@ -1984,6 +1987,7 @@ static int machines__deliver_event(struct machines *machines,
return -ENOMEM;
}
memcpy(de->event, event, sz);
+ de->file_offset = sample->file_offset;
list_add_tail(&de->list, &evlist->deferred_samples);
return 0;
}
@@ -2126,6 +2130,7 @@ static int perf_session__deliver_event(struct perf_session *session,
pr_err("Can't parse sample, err = %d\n", ret);
goto out;
}
+ sample.file_offset = file_offset;
/*
* evsel__parse_sample() doesn't populate machine_pid/vcpu,
* which are needed by machines__find_for_cpumode() to
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/8] perf session: Include file offset in event skip/stop messages
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 1/8] perf sample: Add file_offset field to struct perf_sample Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 3/8] perf sched: Include file offset in event skip messages Arnaldo Carvalho de Melo
` (5 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add 'at offset %#<hex>' to all warning and error messages in session.c
that fire when events are skipped or processing stops due to validation
failures. This lets users cross-reference with 'perf report -D' output
to inspect the surrounding records and understand the corruption context.
Covers messages in perf_session__process_event() (alignment, min size,
swap failure), perf_session__deliver_event() (no evsel, parse failure,
CPU clamping), machines__deliver_event() (NAMESPACES, TEXT_POKE,
null-terminated string checks for MMAP/MMAP2/COMM/CGROUP/KSYMBOL), and
perf_session__process_user_event() (THREAD_MAP, CPU_MAP, STAT_CONFIG,
BPF_METADATA, HEADER_BUILD_ID).
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/session.c | 112 ++++++++++++++++++++------------------
1 file changed, 60 insertions(+), 52 deletions(-)
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7996787d742e32c6..e4efb75509278a4e 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1931,13 +1931,14 @@ static int session__flush_deferred_samples(struct perf_session *session,
* read-only (MAP_SHARED + PROT_READ) so we cannot write a
* null byte in place; skip the event instead.
*/
-static bool perf_event__check_nul(const char *str, const void *end, const char *event_name)
+static bool perf_event__check_nul(const char *str, const void *end,
+ const char *event_name, u64 file_offset)
{
size_t max_len = (const char *)end - str;
if (max_len == 0 || strnlen(str, max_len) == max_len) {
- pr_warning("WARNING: PERF_RECORD_%s: string not null-terminated, skipping event\n",
- event_name);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_%s: string not null-terminated, skipping event\n",
+ file_offset, event_name);
return false;
}
@@ -1995,7 +1996,7 @@ static int machines__deliver_event(struct machines *machines,
case PERF_RECORD_MMAP:
if (!perf_event__check_nul(event->mmap.filename,
(void *)event + event->header.size,
- "MMAP"))
+ "MMAP", file_offset))
return 0;
return tool->mmap(tool, event, sample, machine);
case PERF_RECORD_MMAP2:
@@ -2003,13 +2004,13 @@ static int machines__deliver_event(struct machines *machines,
++evlist->stats.nr_proc_map_timeout;
if (!perf_event__check_nul(event->mmap2.filename,
(void *)event + event->header.size,
- "MMAP2"))
+ "MMAP2", file_offset))
return 0;
return tool->mmap2(tool, event, sample, machine);
case PERF_RECORD_COMM:
if (!perf_event__check_nul(event->comm.comm,
(void *)event + event->header.size,
- "COMM"))
+ "COMM", file_offset))
return 0;
return tool->comm(tool, event, sample, machine);
case PERF_RECORD_NAMESPACES: {
@@ -2027,8 +2028,8 @@ static int machines__deliver_event(struct machines *machines,
* cross-endian path.
*/
if (event->namespaces.nr_namespaces > max_nr) {
- pr_warning("WARNING: PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n",
- (u64)event->namespaces.nr_namespaces, max_nr);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n",
+ file_offset, (u64)event->namespaces.nr_namespaces, max_nr);
return 0;
}
return tool->namespaces(tool, event, sample, machine);
@@ -2036,7 +2037,7 @@ static int machines__deliver_event(struct machines *machines,
case PERF_RECORD_CGROUP:
if (!perf_event__check_nul(event->cgroup.path,
(void *)event + event->header.size,
- "CGROUP"))
+ "CGROUP", file_offset))
return 0;
return tool->cgroup(tool, event, sample, machine);
case PERF_RECORD_FORK:
@@ -2078,7 +2079,7 @@ static int machines__deliver_event(struct machines *machines,
case PERF_RECORD_KSYMBOL:
if (!perf_event__check_nul(event->ksymbol.name,
(void *)event + event->header.size,
- "KSYMBOL"))
+ "KSYMBOL", file_offset))
return 0;
return tool->ksymbol(tool, event, sample, machine);
case PERF_RECORD_BPF_EVENT:
@@ -2090,7 +2091,8 @@ static int machines__deliver_event(struct machines *machines,
event->text_poke.new_len;
if (event->header.size < text_poke_len) {
- pr_warning("WARNING: PERF_RECORD_TEXT_POKE: old_len+new_len exceeds event, skipping\n");
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_TEXT_POKE: old_len+new_len exceeds event, skipping\n",
+ file_offset);
return 0;
}
return tool->text_poke(tool, event, sample, machine);
@@ -2120,14 +2122,17 @@ static int perf_session__deliver_event(struct perf_session *session,
perf_sample__init(&sample, /*all=*/false);
evsel = evlist__event2evsel(session->evlist, event);
if (!evsel) {
- pr_err("No evsel found for event type %u\n",
+ pr_err("ERROR: at offset %#" PRIx64 ": no evsel found for %s (%u) event\n",
+ file_offset, perf_event__name(event->header.type),
event->header.type);
ret = -EFAULT;
goto out;
}
ret = evsel__parse_sample(evsel, event, &sample);
if (ret) {
- pr_err("Can't parse sample, err = %d\n", ret);
+ pr_err("ERROR: at offset %#" PRIx64 ": can't parse %s (%u) sample, err = %d\n",
+ file_offset, perf_event__name(event->header.type),
+ event->header.type, ret);
goto out;
}
sample.file_offset = file_offset;
@@ -2204,8 +2209,8 @@ static int perf_session__deliver_event(struct perf_session *session,
* Downstream array users (timechart, kwork) have
* their own per-callback bounds checks.
*/
- pr_warning_once("WARNING: sample CPU %u >= nr_cpus_avail %u, clamping to 0\n",
- sample.cpu, nr_cpus_avail);
+ pr_warning_once("WARNING: at offset %#" PRIx64 ": sample CPU %u >= nr_cpus_avail %u, clamping to 0\n",
+ file_offset, sample.cpu, nr_cpus_avail);
sample.cpu = 0;
}
}
@@ -2278,7 +2283,7 @@ static s64 perf_session__process_user_event(struct perf_session *session,
case PERF_RECORD_HEADER_BUILD_ID:
if (!perf_event__check_nul(event->build_id.filename,
(void *)event + event_size,
- "HEADER_BUILD_ID")) {
+ "HEADER_BUILD_ID", file_offset)) {
err = 0;
break;
}
@@ -2311,8 +2316,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
u64 max_nr;
if (event_size < sizeof(event->thread_map)) {
- pr_err("PERF_RECORD_THREAD_MAP: header.size (%u) too small\n",
- event_size);
+ pr_err("ERROR: at offset %#" PRIx64 ": PERF_RECORD_THREAD_MAP: header.size (%u) too small\n",
+ file_offset, event_size);
err = -EINVAL;
break;
}
@@ -2320,8 +2325,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
max_nr = (event_size - sizeof(event->thread_map)) /
sizeof(event->thread_map.entries[0]);
if (event->thread_map.nr > max_nr) {
- pr_err("PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n",
- (u64)event->thread_map.nr, max_nr);
+ pr_err("ERROR: at offset %#" PRIx64 ": PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n",
+ file_offset, (u64)event->thread_map.nr, max_nr);
err = -EINVAL;
break;
}
@@ -2345,8 +2350,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
sizeof(data->cpus_data.cpu[0]);
if (data->cpus_data.nr > max_nr) {
- pr_warning("WARNING: PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), skipping\n",
- data->cpus_data.nr, max_nr);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), skipping\n",
+ file_offset, data->cpus_data.nr, max_nr);
err = 0;
goto out;
}
@@ -2359,8 +2364,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
sizeof(data->mask32_data.mask[0]);
if (data->mask32_data.nr > max_nr) {
- pr_warning("WARNING: PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), skipping\n",
- data->mask32_data.nr, max_nr);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), skipping\n",
+ file_offset, data->mask32_data.nr, max_nr);
err = 0;
goto out;
}
@@ -2375,14 +2380,14 @@ static s64 perf_session__process_user_event(struct perf_session *session,
mask64_data.mask)) /
sizeof(data->mask64_data.mask[0]);
if (data->mask64_data.nr > max_nr) {
- pr_warning("WARNING: PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), skipping\n",
- data->mask64_data.nr, max_nr);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), skipping\n",
+ file_offset, data->mask64_data.nr, max_nr);
err = 0;
goto out;
}
} else {
- pr_warning("WARNING: PERF_RECORD_CPU_MAP: unsupported long_size %u, skipping\n",
- data->mask32_data.long_size);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP: unsupported long_size %u, skipping\n",
+ file_offset, data->mask32_data.long_size);
err = 0;
goto out;
}
@@ -2404,8 +2409,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
* cannot clamp nr in place. Skip the event instead.
*/
if (event->stat_config.nr > max_nr) {
- pr_warning("WARNING: PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n",
- (u64)event->stat_config.nr, max_nr);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n",
+ file_offset, (u64)event->stat_config.nr, max_nr);
err = 0;
goto out;
}
@@ -2446,8 +2451,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
u64 nr_entries, max_entries;
if (event_size < sizeof(event->bpf_metadata)) {
- pr_warning("WARNING: PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n",
- event_size);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n",
+ file_offset, event_size);
err = 0;
break;
}
@@ -2458,7 +2463,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
*/
if (strnlen(event->bpf_metadata.prog_name,
BPF_PROG_NAME_LEN) == BPF_PROG_NAME_LEN) {
- pr_warning("WARNING: PERF_RECORD_BPF_METADATA: prog_name not null-terminated, skipping\n");
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: prog_name not null-terminated, skipping\n",
+ file_offset);
err = 0;
break;
}
@@ -2467,8 +2473,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
max_entries = (event_size - sizeof(event->bpf_metadata)) /
sizeof(event->bpf_metadata.entries[0]);
if (nr_entries > max_entries) {
- pr_warning("WARNING: PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n",
- nr_entries, max_entries);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n",
+ file_offset, nr_entries, max_entries);
err = 0;
break;
}
@@ -2478,7 +2484,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
BPF_METADATA_KEY_LEN) == BPF_METADATA_KEY_LEN ||
strnlen(event->bpf_metadata.entries[i].value,
BPF_METADATA_VALUE_LEN) == BPF_METADATA_VALUE_LEN) {
- pr_warning("WARNING: PERF_RECORD_BPF_METADATA: entry %" PRIu64 " key/value not null-terminated, skipping\n", i);
+ pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: entry %" PRIu64 " key/value not null-terminated, skipping\n",
+ file_offset, i);
err = 0;
goto out;
}
@@ -2752,22 +2759,22 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset,
event->header.type != PERF_RECORD_HEADER_TRACING_DATA &&
event->header.type != PERF_RECORD_COMPRESSED &&
event->header.type != PERF_RECORD_HEADER_FEATURE) {
- pr_warning("WARNING: peek_event: event type %u size %u not aligned to %zu\n",
- event->header.type,
- event->header.size, sizeof(u64));
+ pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u not aligned to %zu\n",
+ (u64)file_offset, perf_event__name(event->header.type),
+ event->header.type, event->header.size, sizeof(u64));
return -1;
}
if (event->header.type >= PERF_RECORD_HEADER_MAX) {
- pr_warning("WARNING: peek_event: unsupported event type %u, skipping\n",
- event->header.type);
+ pr_warning("WARNING: at offset %#" PRIx64 ": unsupported event type %u, skipping\n",
+ (u64)file_offset, event->header.type);
return 0;
}
if (perf_event__too_small(event, &min_sz)) {
- pr_warning("WARNING: peek_event: %s event size %u too small (min %u)\n",
- perf_event__name(event->header.type),
- event->header.size, min_sz);
+ pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u too small (min %u)\n",
+ (u64)file_offset, perf_event__name(event->header.type),
+ event->header.type, event->header.size, min_sz);
return -1;
}
@@ -2883,9 +2890,9 @@ static s64 perf_session__process_event(struct perf_session *session,
event->header.type != PERF_RECORD_HEADER_TRACING_DATA &&
event->header.type != PERF_RECORD_COMPRESSED &&
event->header.type != PERF_RECORD_HEADER_FEATURE) {
- pr_err("ERROR: %s event size %u is not 8-byte aligned, aborting\n",
- perf_event__name(event->header.type),
- event->header.size);
+ pr_err("ERROR: at offset %#" PRIx64 ": %s (%u) event size %u is not 8-byte aligned, aborting\n",
+ file_offset, perf_event__name(event->header.type),
+ event->header.type, event->header.size);
return -EINVAL;
}
@@ -2905,16 +2912,17 @@ static s64 perf_session__process_event(struct perf_session *session,
* can be safely stepped over without misaligning the stream.
*/
if (perf_event__too_small(event, &min_sz)) {
- pr_warning("WARNING: %s event size %u too small (min %u), skipping\n",
- perf_event__name(event->header.type),
- event->header.size, min_sz);
+ pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u too small (min %u), skipping\n",
+ file_offset, perf_event__name(event->header.type),
+ event->header.type, event->header.size, min_sz);
return 0;
}
if (session->header.needs_swap &&
event_swap(event, evlist__sample_id_all(evlist))) {
- pr_warning("WARNING: swap failed for %s event, skipping\n",
- perf_event__name(event->header.type));
+ pr_warning("WARNING: at offset %#" PRIx64 ": swap failed for %s (%u) event, skipping\n",
+ file_offset, perf_event__name(event->header.type),
+ event->header.type);
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/8] perf sched: Include file offset in event skip messages
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 1/8] perf sample: Add file_offset field to struct perf_sample Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 2/8] perf session: Include file offset in event skip/stop messages Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages Arnaldo Carvalho de Melo
` (4 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add the perf.data file offset to the CPU out-of-bounds and
machine__resolve failure messages emitted when samples are skipped in
process_sched_switch_event(), process_sched_runtime_event(), and
timehist_sched_change_event(). Also switch event type from raw integer
to perf_event__name() string for readability.
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-sched.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 812a1b0d56d6e5f3..9ec8e049e19b0038 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1792,8 +1792,10 @@ static int process_sched_switch_event(const struct perf_tool *tool,
u32 prev_pid = perf_sample__intval(sample, "prev_pid"),
next_pid = perf_sample__intval(sample, "next_pid");
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (this_cpu < 0 || this_cpu >= MAX_CPUS) {
- pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", this_cpu);
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
+ sample->file_offset, this_cpu);
return 0;
}
@@ -1819,8 +1821,10 @@ static int process_sched_runtime_event(const struct perf_tool *tool,
{
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
- pr_warning("Out-of-bound sample CPU %u. Skipping sample\n", sample->cpu);
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %u, skipping sample\n",
+ sample->file_offset, sample->cpu);
return 0;
}
@@ -2786,15 +2790,18 @@ static int timehist_sched_change_event(const struct perf_tool *tool,
int rc = 0;
const char state = perf_sample__taskstate(sample, "prev_state");
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
- pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", sample->cpu);
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
+ sample->file_offset, sample->cpu);
return 0;
}
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_err("problem processing %d event. skipping it\n",
- event->header.type);
+ pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
rc = -1;
goto out;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
` (2 preceding siblings ...)
2026-06-02 23:57 ` [PATCH 3/8] perf sched: Include file offset in event skip messages Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-03 0:36 ` sashiko-bot
2026-06-02 23:57 ` [PATCH 5/8] perf tools: Include file offset and event type name in skip messages Arnaldo Carvalho de Melo
` (3 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add the perf.data file offset to the out-of-bounds CPU debug messages
in process_sample_cpu_idle(), process_sample_cpu_frequency(),
process_sample_sched_wakeup(), and process_sample_sched_switch().
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-timechart.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 630756bebe3242dc..071987241a528ba4 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -605,8 +605,10 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
u32 state = perf_sample__intval(sample, "state");
u32 cpu_id = perf_sample__intval(sample, "cpu_id");
+ /* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu_id %u\n", cpu_id);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %u\n",
+ sample->file_offset, cpu_id);
return -1;
}
if (state == (u32)PWR_EVENT_EXIT)
@@ -624,8 +626,10 @@ process_sample_cpu_frequency(struct timechart *tchart,
u32 state = perf_sample__intval(sample, "state");
u32 cpu_id = perf_sample__intval(sample, "cpu_id");
+ /* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu_id %u\n", cpu_id);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %u\n",
+ sample->file_offset, cpu_id);
return -1;
}
p_state_change(tchart, cpu_id, sample->time, state);
@@ -641,8 +645,10 @@ process_sample_sched_wakeup(struct timechart *tchart,
int waker = perf_sample__intval(sample, "common_pid");
int wakee = perf_sample__intval(sample, "pid");
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu %u\n", sample->cpu);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n",
+ sample->file_offset, sample->cpu);
return -1;
}
sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
@@ -658,8 +664,10 @@ process_sample_sched_switch(struct timechart *tchart,
int next_pid = perf_sample__intval(sample, "next_pid");
u64 prev_state = perf_sample__intval(sample, "prev_state");
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu %u\n", sample->cpu);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n",
+ sample->file_offset, sample->cpu);
return -1;
}
sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
@@ -676,6 +684,7 @@ process_sample_power_start(struct timechart *tchart __maybe_unused,
u64 cpu_id = perf_sample__intval(sample, "cpu_id");
u64 value = perf_sample__intval(sample, "value");
+ /* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id);
return -1;
@@ -689,6 +698,7 @@ process_sample_power_end(struct timechart *tchart,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
pr_debug("Out-of-bounds cpu %u\n", sample->cpu);
return -1;
@@ -705,6 +715,7 @@ process_sample_power_frequency(struct timechart *tchart,
u64 cpu_id = perf_sample__intval(sample, "cpu_id");
u64 value = perf_sample__intval(sample, "value");
+ /* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id);
return -1;
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 5/8] perf tools: Include file offset and event type name in skip messages
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
` (3 preceding siblings ...)
2026-06-02 23:57 ` [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 6/8] perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain Arnaldo Carvalho de Melo
` (2 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add the perf.data file offset and use perf_event__name() instead of raw
event type integers in the 'problem processing event, skipping it'
messages emitted by process_sample_event() callbacks across annotate,
c2c, diff, kmem, kvm, kwork, lock, report, script, and build-id.
This lets users cross-reference skipped events with 'perf report -D'
output. Also add explicit #include "util/event.h" and <inttypes.h>
where needed to avoid depending on transitive includes.
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-annotate.c | 5 +++--
tools/perf/builtin-c2c.c | 5 +++--
tools/perf/builtin-diff.c | 8 +++++---
tools/perf/builtin-kmem.c | 6 ++++--
tools/perf/builtin-kvm.c | 9 ++++++---
tools/perf/builtin-kwork.c | 4 +++-
tools/perf/builtin-lock.c | 6 ++++--
tools/perf/builtin-report.c | 9 ++++++---
tools/perf/builtin-script.c | 10 ++++++----
tools/perf/util/build-id.c | 5 +++--
10 files changed, 43 insertions(+), 24 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 5f450c8093c09210..b918f9eed5fd2441 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -288,8 +288,9 @@ static int process_sample_event(const struct perf_tool *tool,
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_warning("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_warning("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out_put;
}
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 36f38694992386ad..d3503be9350c03bb 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -328,8 +328,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_debug("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out;
}
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index b4ff863b304ca046..9592f44b6545bab6 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -409,8 +409,9 @@ static int diff__process_sample_event(const struct perf_tool *tool,
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_warning("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_warning("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out;
}
@@ -436,7 +437,8 @@ static int diff__process_sample_event(const struct perf_tool *tool,
case COMPUTE_STREAM:
if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
NULL)) {
- pr_debug("problem adding hist entry, skipping event\n");
+ pr_debug("problem adding hist entry at offset %#" PRIx64 ", skipping event\n",
+ sample->file_offset);
goto out;
}
break;
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 33585e353efe56cc..e1b2f5bc1ba8d887 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -22,6 +22,7 @@
#include "util/cpumap.h"
#include "util/debug.h"
+#include "util/event.h"
#include "util/string2.h"
#include "util/util.h"
@@ -987,8 +988,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
sample->tid);
if (thread == NULL) {
- pr_debug("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
return -1;
}
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index dd2ed21596aa59f9..394302ebdb161077 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -22,6 +22,7 @@
#include "util/synthetic-events.h"
#include "util/top.h"
#include "util/data.h"
+#include "util/event.h"
#include "util/ordered-events.h"
#include "util/kvm-stat.h"
#include "util/util.h"
@@ -1141,14 +1142,16 @@ static int process_sample_event(const struct perf_tool *tool,
return 0;
if (machine__resolve(machine, &kvm->al, sample) < 0) {
- pr_warning("Fail to resolve address location, skip sample.\n");
+ pr_warning("WARNING: at offset %#" PRIx64 ": fail to resolve address location, skipping sample\n",
+ sample->file_offset);
return 0;
}
thread = machine__findnew_thread(machine, sample->pid, sample->tid);
if (thread == NULL) {
- pr_debug("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
return -1;
}
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c
index 99dc293a0744726e..110de3507d48160c 100644
--- a/tools/perf/builtin-kwork.c
+++ b/tools/perf/builtin-kwork.c
@@ -9,6 +9,7 @@
#include "perf.h"
#include "util/data.h"
+#include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/header.h"
@@ -897,7 +898,8 @@ static int timehist_exit_event(struct perf_kwork *kwork,
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_debug("Problem processing event, skipping it\n");
+ pr_debug("problem processing event at offset %#" PRIx64 ", skipping it\n",
+ sample->file_offset);
ret = -1;
goto out;
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 94a8c35abb0bc991..5841d43be9718414 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -21,6 +21,7 @@
#include "util/tracepoint.h"
#include "util/debug.h"
+#include "util/event.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/data.h"
@@ -1433,8 +1434,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
sample->tid);
if (thread == NULL) {
- pr_debug("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
return -1;
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 973d97af85019e6e..cd052aa78132b65f 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -27,6 +27,7 @@
#include "perf.h"
#include "util/debug.h"
+#include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/evswitch.h"
@@ -284,8 +285,9 @@ static int process_sample_event(const struct perf_tool *tool,
addr_location__init(&al);
if (machine__resolve(machine, &al, sample) < 0) {
- pr_debug("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out_put;
}
@@ -332,7 +334,8 @@ static int process_sample_event(const struct perf_tool *tool,
ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
if (ret < 0)
- pr_debug("problem adding hist entry, skipping event\n");
+ pr_debug("problem adding hist entry at offset %#" PRIx64 ", skipping event\n",
+ sample->file_offset);
out_put:
addr_location__exit(&al);
return ret;
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 5124edf2b7a692b2..f4aa255fc3297f90 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2693,8 +2693,9 @@ static int process_sample_event(const struct perf_tool *tool,
goto out_put;
if (!al.thread && machine__resolve(machine, &al, sample) < 0) {
- pr_err("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out_put;
}
@@ -2775,8 +2776,9 @@ static int process_deferred_sample_event(const struct perf_tool *tool,
goto out_put;
if (machine__resolve(machine, &al, sample) < 0) {
- pr_err("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
ret = -1;
goto out_put;
}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index af4d874f13810ffe..8c0a9ae932aa5798 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -10,6 +10,7 @@
#include "util.h" // lsdir(), mkdir_p(), rm_rf()
#include <dirent.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -62,8 +63,8 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused,
sample->tid);
if (thread == NULL) {
- pr_err("problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_err("problem processing %s event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), sample->file_offset);
return -1;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/8] perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
` (4 preceding siblings ...)
2026-06-02 23:57 ` [PATCH 5/8] perf tools: Include file offset and event type name in skip messages Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data Arnaldo Carvalho de Melo
7 siblings, 0 replies; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
cat_backtrace() uses open_memstream() to build a backtrace string.
When an invalid callchain context is encountered, zfree(&p) frees
the memstream buffer, then the exit path calls fclose(f), which
flushes to the already-freed buffer — a use-after-free. The function
then returns a dangling pointer that the caller passes to a handler
and subsequently double-frees.
Fix by replacing the zfree(&p) with a 'corrupted' flag. At the exit
label, always fclose(f) first (which finalizes the buffer), then
conditionally free it when corrupted. This ensures the memstream
contract is honored: the buffer remains valid until fclose().
While here, update the machine__resolve failure message to include
file_offset and the event type name, matching the pattern from the
preceding series. Also update the three legacy power event handlers
under SUPPORT_OLD_POWER_EVENTS to include file_offset in their
out-of-bounds CPU messages for consistency.
Reported-by: sashiko-bot@kernel.org # Running on a local machine
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-timechart.c | 36 ++++++++++++++++++++++------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 071987241a528ba4..85a9ad0455aecccd 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -489,6 +489,10 @@ static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
}
}
+/*
+ * Returns a malloc'd backtrace string built via open_memstream, or NULL
+ * on error. Caller must free() the returned pointer.
+ */
static char *cat_backtrace(union perf_event *event,
struct perf_sample *sample,
struct machine *machine)
@@ -500,6 +504,7 @@ static char *cat_backtrace(union perf_event *event,
u8 cpumode = PERF_RECORD_MISC_USER;
struct ip_callchain *chain = sample->callchain;
FILE *f = open_memstream(&p, &p_len);
+ bool corrupted = false;
if (!f) {
perror("open_memstream error");
@@ -511,8 +516,9 @@ static char *cat_backtrace(union perf_event *event,
goto exit;
if (machine__resolve(machine, &al, sample) < 0) {
- fprintf(stderr, "problem processing %d event, skipping it.\n",
- event->header.type);
+ pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",
+ perf_event__name(event->header.type), event->header.type,
+ sample->file_offset);
goto exit;
}
@@ -537,14 +543,8 @@ static char *cat_backtrace(union perf_event *event,
cpumode = PERF_RECORD_MISC_USER;
break;
default:
- pr_debug("invalid callchain context: "
- "%"PRId64"\n", (s64) ip);
-
- /*
- * It seems the callchain is corrupted.
- * Discard all.
- */
- zfree(&p);
+ pr_debug("invalid callchain context: %" PRId64 "\n", (s64) ip);
+ corrupted = true;
goto exit;
}
continue;
@@ -561,7 +561,14 @@ static char *cat_backtrace(union perf_event *event,
}
exit:
addr_location__exit(&al);
+ /*
+ * fclose() on an open_memstream always sets p to a valid buffer,
+ * even if nothing was written — see open_memstream(3). So p is
+ * never NULL after fclose and we need the flag to discard it.
+ */
fclose(f);
+ if (corrupted)
+ zfree(&p);
return p;
}
@@ -686,7 +693,8 @@ process_sample_power_start(struct timechart *tchart __maybe_unused,
/* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %llu\n",
+ sample->file_offset, (unsigned long long)cpu_id);
return -1;
}
c_state_start(cpu_id, sample->time, value);
@@ -700,7 +708,8 @@ process_sample_power_end(struct timechart *tchart,
{
/* perf.data is untrusted input — CPU may be absent or corrupted */
if (sample->cpu >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu %u\n", sample->cpu);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n",
+ sample->file_offset, sample->cpu);
return -1;
}
c_state_end(tchart, sample->cpu, sample->time);
@@ -717,7 +726,8 @@ process_sample_power_frequency(struct timechart *tchart,
/* perf.data is untrusted input — cpu_id may be corrupted */
if (cpu_id >= MAX_CPUS) {
- pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id);
+ pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %llu\n",
+ sample->file_offset, (unsigned long long)cpu_id);
return -1;
}
p_state_change(tchart, cpu_id, sample->time, value);
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
` (5 preceding siblings ...)
2026-06-02 23:57 ` [PATCH 6/8] perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-03 1:16 ` sashiko-bot
2026-06-02 23:57 ` [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data Arnaldo Carvalho de Melo
7 siblings, 1 reply; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
latency_switch_event(), latency_runtime_event(), and map_switch_event()
use BUG_ON(cpu >= MAX_CPUS || cpu < 0) to validate the sample CPU.
When PERF_SAMPLE_CPU is absent from the sample type,
evsel__parse_sample() initializes sample->cpu to (u32)-1. Casting
this to int yields -1, which triggers the BUG_ON and aborts perf sched.
The central CPU validation in perf_session__deliver_event() intentionally
preserves the (u32)-1 sentinel for downstream tools like perf script
and perf inject, so leaf callbacks must handle it themselves.
Replace the three BUG_ON calls with graceful skips using pr_warning(),
matching the existing pattern in process_sched_switch_event() and
process_sched_runtime_event() earlier in the same file. Include the
file offset for cross-referencing with perf report -D.
Reported-by: sashiko-bot@kernel.org # Running on a local machine
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-sched.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 9ec8e049e19b0038..81833d169470582b 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1145,7 +1145,12 @@ static int latency_switch_event(struct perf_sched *sched,
int cpu = sample->cpu, err = -1;
s64 delta;
- BUG_ON(cpu >= MAX_CPUS || cpu < 0);
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
+ if (cpu >= MAX_CPUS || cpu < 0) {
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
+ sample->file_offset, cpu);
+ return 0;
+ }
timestamp0 = sched->cpu_last_switched[cpu];
sched->cpu_last_switched[cpu] = timestamp;
@@ -1215,7 +1220,13 @@ static int latency_runtime_event(struct perf_sched *sched,
if (thread == NULL)
return -1;
- BUG_ON(cpu >= MAX_CPUS || cpu < 0);
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
+ if (cpu >= MAX_CPUS || cpu < 0) {
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
+ sample->file_offset, cpu);
+ err = 0;
+ goto out_put;
+ }
if (!atoms) {
if (thread_atoms_insert(sched, thread))
goto out_put;
@@ -1640,7 +1651,12 @@ static int map_switch_event(struct perf_sched *sched, struct perf_sample *sampl
const char *str;
int ret = -1;
- BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0);
+ /* perf.data is untrusted input — CPU may be absent or corrupted */
+ if (this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0) {
+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
+ sample->file_offset, this_cpu.cpu);
+ return 0;
+ }
if (this_cpu.cpu > sched->max_cpu.cpu)
sched->max_cpu = this_cpu;
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
` (6 preceding siblings ...)
2026-06-02 23:57 ` [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip Arnaldo Carvalho de Melo
@ 2026-06-02 23:57 ` Arnaldo Carvalho de Melo
2026-06-03 1:32 ` sashiko-bot
7 siblings, 1 reply; 12+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-06-02 23:57 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, Claude Opus 4.6
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add a shell test that verifies the file_offset diagnostic messages
work correctly when perf encounters corrupted events.
The test corrupts a MMAP2 event's size field in a recorded perf.data
file, then checks that perf report produces warning messages that
include both the file offset (e.g. "at offset 0x2738:") and the
event type name with numeric id (e.g. "MMAP2 (10)").
This exercises the diagnostic improvements from the file_offset
series, which retrofitted all skip/stop/error messages to include
the position and type of the problematic event.
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
.../shell/data_file_offset_diagnostics.sh | 82 +++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100755 tools/perf/tests/shell/data_file_offset_diagnostics.sh
diff --git a/tools/perf/tests/shell/data_file_offset_diagnostics.sh b/tools/perf/tests/shell/data_file_offset_diagnostics.sh
new file mode 100755
index 0000000000000000..031f49480d999d8c
--- /dev/null
+++ b/tools/perf/tests/shell/data_file_offset_diagnostics.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+# Test that perf report includes file offsets and event type names in diagnostic messages.
+# SPDX-License-Identifier: GPL-2.0
+#
+# The file_offset diagnostic series adds "at offset 0x...: TYPE (N)"
+# to all skip/stop/error messages. This test corrupts an event's size
+# field in a perf.data file, then verifies the resulting warning
+# includes the file offset and event type.
+
+err=0
+
+cleanup() {
+ [ -n "${perfdata}" ] && rm -f "${perfdata}" "${perfdata}.old"
+ rm -f "${corrupted}" "${stderrfile}"
+ trap - EXIT TERM INT
+}
+trap 'cleanup; exit 1' TERM INT
+trap cleanup EXIT
+
+perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2
+corrupted=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2
+stderrfile=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2
+
+if ! perf record -o "${perfdata}" -- perf test -w noploop 2>/dev/null; then
+ echo "Skip: perf record failed"
+ cleanup
+ exit 2
+fi
+
+# Find the file offset of the first MMAP2 event via perf report -D.
+# Format: "timestamp 0xOFFSET [0xSIZE]: PERF_RECORD_MMAP2 ..."
+mmap2_line=$(perf report -D -i "${perfdata}" 2>/dev/null | grep "PERF_RECORD_MMAP2" | head -1)
+if [ -z "${mmap2_line}" ]; then
+ echo "Skip: no MMAP2 events found in perf.data"
+ cleanup
+ exit 2
+fi
+
+mmap2_offset=$(echo "${mmap2_line}" | awk '{print $2}')
+mmap2_offset_dec=$((mmap2_offset))
+
+# Copy the file and corrupt the MMAP2 event's size field.
+# perf_event_header layout: type(u32) misc(u16) size(u16)
+# Set size to 16 (0x10 0x00 little-endian) — below the MMAP2
+# minimum, which triggers the "event size too small" warning.
+cp "${perfdata}" "${corrupted}"
+printf '\x10\x00' | dd of="${corrupted}" bs=1 seek=$((mmap2_offset_dec + 6)) conv=notrunc 2>/dev/null
+
+perf report -i "${corrupted}" --stdio > /dev/null 2> "${stderrfile}"
+
+# Check that warnings include "at offset 0x..."
+if grep -q "at offset 0x" "${stderrfile}"; then
+ echo "File offset in diagnostics [Success]"
+else
+ echo "File offset in diagnostics [Failed: no 'at offset 0x...' found]"
+ echo " stderr was:"
+ head -5 "${stderrfile}"
+ err=1
+fi
+
+# Check that the event type name and numeric id appear: "MMAP2 (10)"
+if grep -q "MMAP2 (10)" "${stderrfile}"; then
+ echo "Event type name in diagnostics [Success]"
+else
+ echo "Event type name in diagnostics [Failed: no 'MMAP2 (10)' found]"
+ echo " stderr was:"
+ head -5 "${stderrfile}"
+ err=1
+fi
+
+# Check that the reported offset matches the actual corruption point
+if grep -q "at offset ${mmap2_offset}:" "${stderrfile}"; then
+ echo "Correct offset reported [Success]"
+else
+ echo "Correct offset reported [Failed: expected offset ${mmap2_offset}]"
+ echo " stderr was:"
+ head -5 "${stderrfile}"
+ err=1
+fi
+
+cleanup
+exit ${err}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages
2026-06-02 23:57 ` [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages Arnaldo Carvalho de Melo
@ 2026-06-03 0:36 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-03 0:36 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Out-of-bounds read vulnerability in cpu2y() when using --topology, leading to heap information leak or crash.
--
commit df9ed5394176bf63cfdd83249ffcb6787efbba52
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
perf timechart: Include file offset in CPU bounds check messages
This patch adds the perf.data file offset to the out-of-bounds CPU debug
messages in various timechart sample processing functions.
> diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
> index 630756bebe324..071987241a528 100644
> --- a/tools/perf/builtin-timechart.c
> +++ b/tools/perf/builtin-timechart.c
> @@ -605,8 +605,10 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
> u32 state = perf_sample__intval(sample, "state");
> u32 cpu_id = perf_sample__intval(sample, "cpu_id");
>
> + /* perf.data is untrusted input — cpu_id may be corrupted */
> if (cpu_id >= MAX_CPUS) {
> - pr_debug("Out-of-bounds cpu_id %u\n", cpu_id);
> + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %u\n",
> + sample->file_offset, cpu_id);
[Severity: High]
This is a pre-existing issue, but since the bounds check logic is being
modified here, does checking against MAX_CPUS adequately protect
topology_map when using --topology?
The topology_map is dynamically allocated using the number of CPUs:
tools/perf/util/svghelper.c:svg_build_topology_map() {
...
topology_map = malloc(sizeof(int) * nr_cpus);
...
}
However, the untrusted CPU ID in the perf.data sample is only checked against
MAX_CPUS (4096) here. Later, cpu2y() accesses this array using the CPU ID:
tools/perf/util/svghelper.c:cpu2y() {
...
return cpu2slot(topology_map[cpu]) * SLOT_MULT;
}
If nr_cpus is smaller than MAX_CPUS, could a crafted perf.data with a CPU ID
between nr_cpus and 4095 cause an out-of-bounds heap read, potentially leading
to a crash or an information leak into the SVG output?
> return -1;
> }
> if (state == (u32)PWR_EVENT_EXIT)
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602235709.1541603-1-acme@kernel.org?part=4
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip
2026-06-02 23:57 ` [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip Arnaldo Carvalho de Melo
@ 2026-06-03 1:16 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-03 1:16 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Pre-existing NULL pointer dereference when machine__findnew_thread fails in latency_runtime_event.
- [High] Missing `thread__put()` for `sched_out` and `sched_in` on the error path of `add_sched_out_event()`.
- [High] Missing bounds check on `sample->cpu` causes out-of-bounds reads in `test_bit()` across multiple perf tools.
--
commit a94415fdfe25f82cda0feb4b93da07a2fcbbe10d
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
perf sched: Replace BUG_ON on invalid CPU with graceful skip
This commit replaces BUG_ON assertions for invalid sample CPUs with graceful
skips and warnings in latency_switch_event(), latency_runtime_event(), and
map_switch_event(). It handles cases where PERF_SAMPLE_CPU is absent and
initialized to (u32)-1 by evsel__parse_sample().
> The central CPU validation in perf_session__deliver_event() intentionally
> preserves the (u32)-1 sentinel for downstream tools like perf script
> and perf inject, so leaf callbacks must handle it themselves.
[Severity: High]
This isn't a regression introduced by this patch, but do other leaf callbacks
properly handle the (u32)-1 sentinel when using the --cpu list option?
In process_sample_event() in tools/perf/builtin-annotate.c:
if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
goto out_put;
And similarly in hist_iter_cb_cmp() in tools/perf/builtin-diff.c:
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) {
return -1;
}
And in process_sample_event() in tools/perf/builtin-report.c:
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
goto out_put;
If sample->cpu is (u32)-1, could passing this to test_bit() compute an invalid
bit offset and cause a deterministic out-of-bounds read and segmentation fault?
> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
> index 9ec8e049e19b0..81833d1694705 100644
> --- a/tools/perf/builtin-sched.c
> +++ b/tools/perf/builtin-sched.c
> @@ -1145,7 +1145,12 @@ static int latency_switch_event(struct perf_sched *sched,
> int cpu = sample->cpu, err = -1;
> s64 delta;
>
> - BUG_ON(cpu >= MAX_CPUS || cpu < 0);
> + /* perf.data is untrusted input — CPU may be absent or corrupted */
> + if (cpu >= MAX_CPUS || cpu < 0) {
> + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n",
> + sample->file_offset, cpu);
> + return 0;
> + }
[Severity: High]
This isn't a regression introduced by this patch, but does the error path for
add_sched_out_event() further down in latency_switch_event() leak thread
references?
If machine__findnew_thread() successfully acquires references for sched_out and
sched_in, and we later fail here:
if (add_sched_out_event(out_events, prev_state, timestamp))
return -1;
Could returning -1 directly bypass the thread__put() cleanup sequence at the
out_put label and leak the thread objects?
[ ... ]
> @@ -1215,7 +1220,13 @@ static int latency_runtime_event(struct perf_sched *sched,
> if (thread == NULL)
> return -1;
[Severity: High]
This isn't a regression introduced by this patch, but could this cause a null
pointer dereference before the validation check is reached?
At the start of latency_runtime_event(), atoms is initialized by passing
thread to thread_atoms_search():
struct thread *thread = machine__findnew_thread(machine, -1, pid);
struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread,
&sched->cmp_pid);
...
if (thread == NULL)
return -1;
If machine__findnew_thread() fails and returns null, would thread_atoms_search()
dereference the null pointer before we reach this validation check?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602235709.1541603-1-acme@kernel.org?part=7
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data
2026-06-02 23:57 ` [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data Arnaldo Carvalho de Melo
@ 2026-06-03 1:32 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-03 1:32 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Hardcoded little-endian byte sequence causes the test to fail on big-endian architectures.
--
commit af35d72bcb6cca8ee9ddf5a0da66e97329974342
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
perf test: Add file offset diagnostic test for corrupted perf.data
This commit adds a shell test to verify that file_offset diagnostic messages
work correctly when perf encounters corrupted events. It intentionally
corrupts an MMAP2 event's size field in a recorded perf.data file and checks
that perf report produces a warning message that includes the file offset.
> diff --git a/tools/perf/tests/shell/data_file_offset_diagnostics.sh b/tools/perf/tests/shell/data_file_offset_diagnostics.sh
> new file mode 100755
> --- /dev/null
> +++ b/tools/perf/tests/shell/data_file_offset_diagnostics.sh
[ ... ]
> +# Copy the file and corrupt the MMAP2 event's size field.
> +# perf_event_header layout: type(u32) misc(u16) size(u16)
> +# Set size to 16 (0x10 0x00 little-endian) — below the MMAP2
> +# minimum, which triggers the "event size too small" warning.
> +cp "${perfdata}" "${corrupted}"
> +printf '\x10\x00' | dd of="${corrupted}" bs=1 seek=$((mmap2_offset_dec + 6)) conv=notrunc 2>/dev/null
[Severity: Medium]
Will this hardcoded little-endian byte sequence cause the test to fail on
big-endian architectures?
The test explicitly overwrites the 16-bit size field of a PERF_RECORD_MMAP2
event using the bytes \x10\x00. Since perf.data files are natively encoded
in the host system's byte order, on big-endian hosts (e.g., s390x) the
native u16 parsing of 0x10 at offset 0 and 0x00 at offset 1 will evaluate
to 0x1000 (4096).
Because 4096 exceeds the minimum MMAP2 event size, the internal validation
check is bypassed. This will cause perf report to skip 4096 bytes and
attempt to parse unaligned garbage data instead of emitting the expected
"too small" warning for the MMAP2 event.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602235709.1541603-1-acme@kernel.org?part=8
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-03 1:32 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-02 23:56 [PATCHES 0/8] perf tools: Diagnostic offsets in skip messages + two hardening fixes Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 1/8] perf sample: Add file_offset field to struct perf_sample Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 2/8] perf session: Include file offset in event skip/stop messages Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 3/8] perf sched: Include file offset in event skip messages Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 4/8] perf timechart: Include file offset in CPU bounds check messages Arnaldo Carvalho de Melo
2026-06-03 0:36 ` sashiko-bot
2026-06-02 23:57 ` [PATCH 5/8] perf tools: Include file offset and event type name in skip messages Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 6/8] perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain Arnaldo Carvalho de Melo
2026-06-02 23:57 ` [PATCH 7/8] perf sched: Replace BUG_ON on invalid CPU with graceful skip Arnaldo Carvalho de Melo
2026-06-03 1:16 ` sashiko-bot
2026-06-02 23:57 ` [PATCH 8/8] perf test: Add file offset diagnostic test for corrupted perf.data Arnaldo Carvalho de Melo
2026-06-03 1:32 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox