[PATCH 05/12] perf jitdump: Bounds-check debug entry byte-swap loop

From: Arnaldo Carvalho de Melo

Date: Thu Aug 06 2026 - 08:38:42 EST


From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>

The byte-swap loop for JIT_CODE_DEBUG_INFO uses array indexing
(jr->info.entries[n]) to iterate debug entries. struct debug_entry has
a flexible array member name[], so each entry has a different size.
Array indexing computes offsets assuming fixed-size elements, landing
inside variable-length name strings after the first entry and
byte-swapping garbage.

Additionally, nr_entry is read from untrusted jitdump input without
validation against total_size, so a crafted value causes OOB reads.

Replace the array indexing with debug_entry_next() pointer arithmetic
(which correctly accounts for the variable-length name) and
bounds-check each entry against the record's total_size before
byte-swapping.

Fixes: 9b07e27f88b9 ("perf inject: Add jitdump mmap injection support")
Reported-by: sashiko-bot <sashiko-bot@xxxxxxxxxx>
Cc: Stephane Eranian <eranian@xxxxxxxxxx>
Assisted-by: Claude:claude-opus-4.6
Reviewed-by: Ian Rogers <irogers@xxxxxxxxxx>
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/util/jitdump.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index f79e9420c6bd7c51..7efbaa07f1ba73f9 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -320,14 +320,32 @@ jit_get_next_entry(struct jit_buf_desc *jd)
switch(id) {
case JIT_CODE_DEBUG_INFO:
if (jd->needs_bswap) {
+ void *end = (void *)jr + jr->prefix.total_size;
+ struct debug_entry *ent;
uint64_t n;
+
jr->info.code_addr = bswap_64(jr->info.code_addr);
jr->info.nr_entry = bswap_64(jr->info.nr_entry);
- for (n = 0 ; n < jr->info.nr_entry; n++) {
- jr->info.entries[n].addr = bswap_64(jr->info.entries[n].addr);
- jr->info.entries[n].lineno = bswap_32(jr->info.entries[n].lineno);
- jr->info.entries[n].discrim = bswap_32(jr->info.entries[n].discrim);
+
+ /*
+ * debug_entry has a variable-length name[], so array
+ * indexing would compute wrong offsets — use
+ * debug_entry_next() and bounds-check each entry.
+ */
+ ent = &jr->info.entries[0];
+ for (n = 0; n < jr->info.nr_entry; n++) {
+ if ((void *)ent + sizeof(*ent) > end)
+ break;
+ /* name must be NUL-terminated within the record */
+ if (!memchr(ent->name, '\0', (char *)end - ent->name))
+ break;
+ ent->addr = bswap_64(ent->addr);
+ ent->lineno = bswap_32(ent->lineno);
+ ent->discrim = bswap_32(ent->discrim);
+ ent = debug_entry_next(ent);
}
+ /* clamp so downstream consumers don't overrun */
+ jr->info.nr_entry = n;
}
break;
case JIT_CODE_UNWINDING_INFO:
--
2.55.0