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

[email protected] Wed, 05 Aug 2026 14:27:59 +0000
Newsgroups org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete fix for out-of-bounds read/write in JIT_CODE_DEBUG_INFO parsing
- [High] Missing bounds validation for JIT_CODE_UNWINDING_INFO sizes leads to OOB heap read and information leak
- [High] Missing null-terminator validation for JIT_CODE_LOAD symbol string causes OOB read and information leak
--

--- Patch [4]: [PATCH 04/12] perf jitdump: Bounds-check debug entry byte-swap loop ---
commit 2ca1b158f7ca3eafd8478fe7713f2806ce7b4fce
Author: Arnaldo Carvalho de Melo <[email protected]>
Subject: perf jitdump: Bounds-check debug entry byte-swap loop

This commit replaces array indexing with debug_entry_next() pointer arithmetic
for JIT_CODE_DEBUG_INFO records, and adds bounds-checking against the record's
total_size. This prevents out-of-bounds reads during byte-swapping of untrusted
jitdump input.

> diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
> index 787f8a03dae87..078d3304d2b7e 100644
> --- a/tools/perf/util/jitdump.c
> +++ b/tools/perf/util/jitdump.c
> @@ -318,14 +318,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;

[Severity: High]
This is a pre-existing issue, but does this skip validation for native-endian
files?

The bounds checking and clamping are only performed when jd->needs_bswap is true.
If the file is native-endian, the untrusted nr_entry is passed directly to the
downstream loop without validation:

tools/perf/util/genelf_debug.c:jit_process_debug_info() {
    ...
    for (i = 0; i < nr_debug_entries; i++) {
        ent->addr = ent->addr - code_addr;
        ent = debug_entry_next(ent);
    }
    ...
}

Could this lead to out-of-bounds heap reads and writes when processing a crafted
JIT_CODE_DEBUG_INFO record with native endianness?

[ ... ]

> +			/* clamp so downstream consumers don't overrun */
> +			jr->info.nr_entry = n;
>  		}
>  		break;
>  	case JIT_CODE_UNWINDING_INFO:

[Severity: High]
This is a pre-existing issue, but is the unwinding_size bounded by the actual
record size?

In jit_repipe_unwinding_info(), jd->unwinding_size is set directly from the
untrusted jr->unwinding.unwinding_size. Later, this unvalidated size is passed
to libelf:

tools/perf/util/genelf.c:jit_add_eh_frame_info() {
    ...
    d->d_size = unwinding_table_size;
    ...
}

Could this cause an out-of-bounds heap read and embed adjacent heap memory into
the generated ELF file?

>  		if (jd->needs_bswap) {
>  			jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size);
>  			jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size);
>  			jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size);
>  		}
>  		break;
>  	case JIT_CODE_CLOSE:
>  		break;
>  	case JIT_CODE_LOAD:

[Severity: High]
This is a pre-existing issue, but is the symbol string in JIT_CODE_LOAD checked
for null-termination within the record bounds?

In jit_repipe_code_load(), the string pointer is derived from the record
without validation:

tools/perf/util/jitdump.c:jit_repipe_code_load() {
    ...
    sym = (void *)((unsigned long)jr + sizeof(jr->load));
    ...
}

This unvalidated string is later passed to strlen() and strcpy() in
jit_write_elf(), which could scan past the record into adjacent heap memory and
leak data into the generated ELF string table.

>  		if (jd->needs_bswap) {
>  			jr->load.pid       = bswap_32(jr->load.pid);
>  			jr->load.tid       = bswap_32(jr->load.tid);

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=4