Linux Perf Users
 help / color / mirror / Atom feed
From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: sashiko-reviews@lists.linux.dev
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH 02/12] perf jitdump: Validate code_size against total_size in code load
Date: Thu, 6 Aug 2026 09:30:23 -0300	[thread overview]
Message-ID: <anR-XwEFkI-P7UzZ@x1> (raw)
In-Reply-To: <20260805214241.A15EA1F000E9@smtp.kernel.org>

On Wed, Aug 05, 2026 at 09:42:41PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
> 
> New issues:
> - [Medium] jd->debug_data and jd->unwinding_data are leaked on the new validation failure path.

This is fixed in a later patch in this series, I'll reorder the series
so that this leak doesn't gets introduced to then be fixed in another
patch.

All the pre-existing issues reported are being tracked in TODO files for
upcoming series, one is already in the making.

Thanks,

- Arnaldo
 
> Pre-existing issues:
> - [High] Integer underflow when calculating unwinding_data_size in jit_repipe_unwinding_info leads to a massive heap allocation and out-of-bounds read.
> - [High] Missing snprintf truncation clamping in jit_repipe_code_move and jit_repipe_code_load leads to an undersized event allocation calculation and out-of-bounds heap write.
> - [High] jit_get_next_entry iterates over an unvalidated nr_entry array using fixed-size indexing for a variable-length structure, leading to out-of-bounds byte-swapping.
> - [High] The parser lacks total_size boundary checks for JIT_CODE_MOVE, leading to uninitialized heap reads.
> - [High] Lack of validation between buffer size and nr_entry leads to heap OOB read and write in debug info parsing.
> - [Medium] The perf_event allocated via calloc is unconditionally leaked in jit_repipe_code_move.
> - [Medium] Multiple memory leaks of dynamically allocated buffers in the jitdump parser, including missing cleanup in jit_close and overwrites in debug info.
> - [Medium] The ALIGN_8 macro evaluates using signed arithmetic, causing integer wrap and massive length corruption for mmap2 events.
> - [Medium] Truncation of 64-bit unwinding_mapped_size to 32-bit int can corrupt mmap2 event length.
> --
> 
> --- Patch [2]: [PATCH 02/12] perf jitdump: Validate code_size against total_size in code load ---
> commit 97b091976e049b90397f99d555b22a4934abb49d
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> perf jitdump: Validate code_size against total_size in code load
> 
> This commit introduces validation checks to ensure that the code_size
> does not exceed the available size in the JIT load record. It adds bounds
> checking to prevent out-of-bounds heap reads when calculating the code
> blob pointer.
> 
> > diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
> > index 4b7c7ba7cd95d..3195f94187164 100644
> > --- a/tools/perf/util/jitdump.c
> > +++ b/tools/perf/util/jitdump.c
> > @@ -9,6 +9,7 @@
> >  #include <fcntl.h>
> >  #include <unistd.h>
> >  #include <inttypes.h>
> > +#include <limits.h>
> >  #include <byteswap.h>
> >  #include <sys/stat.h>
> >  #include <sys/mman.h>
> > @@ -450,6 +451,16 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
> >  	csize = jr->load.code_size;
> >  	usize = jd->unwinding_mapped_size;
> >  	addr  = jr->load.code_addr;
> > +
> > +	/* code blob lives at the end of the record, validate it fits */
> > +	if (jr->load.p.total_size < sizeof(jr->load) ||
> > +	    jr->load.code_size > jr->load.p.total_size - sizeof(jr->load) ||
> > +	    jr->load.code_size > INT_MAX) {
> > +		pr_warning("jitdump: invalid code_size %" PRIu64 " (total_size=%u) in code_load record\n",
> > +			   (uint64_t)jr->load.code_size, jr->load.p.total_size);
> > +		return -1;
> 
> [Severity: Medium]
> Does this early return skip freeing the buffered debug and unwinding data?
> 
> Later in the function, these dynamically allocated buffers are freed:
> 
>     if (jd->debug_data && jd->nr_debug_entries) {
>             zfree(&jd->debug_data);
>             jd->nr_debug_entries = 0;
>     }
> 
>     if (jd->unwinding_data && jd->eh_frame_hdr_size) {
>             zfree(&jd->unwinding_data);
>             ...
> 
> If the code returns -1 here, do jd->debug_data and jd->unwinding_data remain
> allocated, permanently leaking since jit_close() does not appear to clean them
> up?
> 
> > +	}
> > +
> >  	sym   = (void *)((unsigned long)jr + sizeof(jr->load));
> >  	code  = (unsigned long)jr + jr->load.p.total_size - csize;
> >  	count = jr->load.code_index;
> 
> [ ... ]
> 
> Other findings in this file:
> 
> [Severity: High]
> This is a pre-existing issue, but does integer underflow occur when calculating
> unwinding_data_size in jit_repipe_unwinding_info()?
> 
> tools/perf/util/jitdump.c:jit_repipe_unwinding_info() {
>     ...
>     unwinding_data_size  = jr->prefix.total_size - sizeof(jr->unwinding);
>     unwinding_data = malloc(unwinding_data_size);
>     memcpy(unwinding_data, &jr->unwinding.unwinding_data, unwinding_data_size);
>     ...
> }
> 
> If total_size is smaller than sizeof(jr->unwinding), does the unsigned 32-bit
> subtraction underflow to a massive value, causing malloc to allocate too much
> memory and memcpy to perform an out-of-bounds read?
> 
> 
> [Severity: Medium]
> This is a pre-existing issue, but is the perf_event leaked in
> jit_repipe_code_move()?
> 
> tools/perf/util/jitdump.c:jit_repipe_code_move() {
>     ...
>     event = calloc(1, sizeof(*event) + 16);
>     if (!event)
>         return -1;
>     ...
>     return ret;
> }
> 
> The event pointer is allocated via calloc but is never freed on the success
> or error return paths. Does this gradually exhaust memory when parsing large
> dumps?
> 
> 
> [Severity: High]
> This is a pre-existing issue, but does missing snprintf truncation clamping
> lead to out-of-bounds heap writes?
> 
> tools/perf/util/jitdump.c:jit_repipe_code_move() {
>     ...
>     size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so", ...);
>     size++; /* for \0 */
>     event->mmap2.header.size = (sizeof(event->mmap2) - (sizeof(event->mmap2.filename) - size) + idr_size);
>     ...
> }
> 
> When generating the filename, snprintf returns the length that would have
> been written. If this exceeds PATH_MAX, does the returned size wrap in the
> unsigned arithmetic for header.size, underflowing it and pushing the trailing
> id pointer out of the allocated event buffer bounds?
> 
> 
> [Severity: Medium]
> This is a pre-existing issue, but are dynamic buffers repeatedly leaked in
> jit_repipe_debug_info() and jit_close()?
> 
> tools/perf/util/jitdump.c:jit_repipe_debug_info() {
>     ...
>     data = malloc(sz);
>     /* ... */
>     jd->debug_data       = data;
>     ...
> }
> 
> Repeated JIT_CODE_DEBUG_INFO records will malloc a new buffer and overwrite
> jd->debug_data without freeing the old one. Additionally, jit_close() frees
> the jd struct resources but does not seem to clean up internal heap buffers
> like debug_data and unwinding_data. Does this leave them un-freed
> indefinitely?
> 
> 
> [Severity: High]
> This is a pre-existing issue, but does standard array indexing on a variable
> length struct cause memory corruption during bswap?
> 
> tools/perf/util/jitdump.c:jit_get_next_entry() {
>     ...
>     for (n = 0 ; n < jr->info.nr_entry; n++) {
>         jr->info.entries[n].addr    = bswap_64(jr->info.entries[n].addr);
>     ...
> }
> 
> In cross-endian mode, this iterates over an unvalidated nr_entry array using
> fixed-size indexing. Since debug_entry includes a variable-length name[]
> string inline, doesn't fixed-size array indexing read from and byte-swap
> completely incorrect offsets?
> 
> 
> [Severity: High]
> This is a pre-existing issue, but does jit_repipe_code_move() lack boundary
> checks for the JIT_CODE_MOVE total_size?
> 
> tools/perf/util/jitdump.c:jit_repipe_code_move() {
>     ...
>     event->mmap2.start = jr->move.new_code_addr;
>     event->mmap2.len   = usize ? ALIGN_8(jr->move.code_size) + usize : jr->move.code_size;
>     ...
> }
> 
> Unlike the new validation added to JIT_CODE_LOAD in this patch,
> jit_repipe_code_move() lacks a check confirming that total_size is greater
> than or equal to sizeof(jr->move). Can malformed records cause the parser
> to read uninitialized heap or stale data from jd->buf into perf.data?
> 
> 
> [Severity: Medium]
> This is a pre-existing issue, but does the ALIGN_8 macro silently overflow
> into a negative value?
> 
> tools/perf/util/jitdump.c:jit_repipe_code_load() {
>     ...
>     int ret, csize, usize;
>     /* ... */
>     event->mmap2.len   = usize ? ALIGN_8(csize) + usize : csize;
>     ...
> }
> 
> Since csize is typed as a signed int, does calculating ALIGN_8(csize) with a
> value near INT_MAX trigger signed integer overflow to a negative value, which
> is then sign-extended into a corrupted uint64_t length?
> 
> 
> [Severity: Medium]
> This is a pre-existing issue, but can the truncation of unwinding_mapped_size
> corrupt the mmap2 event length?
> 
> tools/perf/util/jitdump.c:jit_repipe_code_move() {
>     ...
>     int usize;
>     /* ... */
>     usize = jd->unwinding_mapped_size;
>     /* ... */
>     event->mmap2.len   = usize ? ALIGN_8(jr->move.code_size) + usize : jr->move.code_size;
>     ...
> }
> 
> The 64-bit jd->unwinding_mapped_size is truncated to a 32-bit signed integer
> (usize). Could a maliciously crafted mapped_size result in a negative or
> truncated value, corrupting the final event->mmap2.len?
> 
> 
> [Severity: High]
> This is a pre-existing issue, but does the lack of validation between buffer
> size and nr_entry lead to heap out-of-bounds reads and writes?
> 
> tools/perf/util/jitdump.c:jit_repipe_debug_info() {
>     ...
>     jd->nr_debug_entries = jr->info.nr_entry;
>     ...
> }
> 
> The parser sets jd->nr_debug_entries from the unvalidated nr_entry file field
> without confirming the allocated sz bytes can accommodate them. When
> jit_process_debug_info() loops nr_entry times calling strlen on heap data,
> does this result in out-of-bounds heap writes?
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20260805212704.267779-1-acme@kernel.org?part=2

  reply	other threads:[~2026-08-06 12:30 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-05 21:26 [PATCHES v2 0/12] perf jitdump: Input validation hardening Arnaldo Carvalho de Melo
2026-08-05 21:26 ` [PATCH 01/12] perf jitdump: Fix extended header read that always fails Arnaldo Carvalho de Melo
2026-08-05 21:26 ` [PATCH 02/12] perf jitdump: Validate code_size against total_size in code load Arnaldo Carvalho de Melo
2026-08-05 21:42   ` sashiko-bot
2026-08-06 12:30     ` Arnaldo Carvalho de Melo [this message]
2026-08-05 21:26 ` [PATCH 03/12] perf jitdump: Prevent integer underflow in debug info size calculation Arnaldo Carvalho de Melo
2026-08-05 21:46   ` sashiko-bot
2026-08-05 21:26 ` [PATCH 04/12] perf jitdump: Bounds-check debug entry byte-swap loop Arnaldo Carvalho de Melo
2026-08-05 21:40   ` sashiko-bot
2026-08-05 21:26 ` [PATCH 05/12] perf jitdump: Check snprintf return before computing header size Arnaldo Carvalho de Melo
2026-08-05 21:40   ` sashiko-bot
2026-08-05 21:26 ` [PATCH 06/12] perf jitdump: Fix funlockfile on unlocked stream in jit_open() error path Arnaldo Carvalho de Melo
2026-08-05 21:26 ` [PATCH 07/12] perf jitdump: Free event in jit_repipe_code_move() Arnaldo Carvalho de Melo
2026-08-05 21:26 ` [PATCH 08/12] perf jitdump: Fix debug_data and unwinding_data leaks Arnaldo Carvalho de Melo
2026-08-05 21:26 ` [PATCH 09/12] perf jitdump: Use dirname() return value in jit_open() Arnaldo Carvalho de Melo
2026-08-05 21:45   ` sashiko-bot
2026-08-05 21:27 ` [PATCH 10/12] perf jitdump: Validate debug entries on native (non-swap) path Arnaldo Carvalho de Melo
2026-08-05 22:00   ` sashiko-bot
2026-08-05 21:27 ` [PATCH 11/12] perf jitdump: Validate sym string NUL-termination in code load Arnaldo Carvalho de Melo
2026-08-05 21:51   ` sashiko-bot
2026-08-05 21:27 ` [PATCH 12/12] perf jitdump: Validate unwinding sizes against record payload Arnaldo Carvalho de Melo
2026-08-05 21:53   ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-08-05 13:29 [PATCHES v1 0/12] perf jitdump: Input validation hardening Arnaldo Carvalho de Melo
2026-08-05 13:30 ` [PATCH 02/12] perf jitdump: Validate code_size against total_size in code load Arnaldo Carvalho de Melo
2026-08-05 13:59   ` sashiko-bot
2026-08-05 18:58   ` Ian Rogers

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=anR-XwEFkI-P7UzZ@x1 \
    --to=acme@kernel.org \
    --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