Linux Perf Users
 help / color / mirror / Atom feed
From: Wang Haoran <haoranwangsec@gmail.com>
To: acme@kernel.org
Cc: peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org,
	mark.rutland@arm.com, alexander.shishkin@linux.intel.com,
	linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org,
	haoranwangsec@gmail.com
Subject: [PATCH 0/6] perf: fix six memory-safety vulnerabilities in sched/header/subcmd
Date: Fri, 29 May 2026 14:49:43 +0800	[thread overview]
Message-ID: <178003738371.62097.10360938456907564684@gmail.com> (raw)

>From 9e71ffe9400fd54c4fc958b16229e5628271e4ad Mon Sep 17 00:00:00 2001
From: Wang Haoran <haoranwangsec@gmail.com>
Date: Thu, 28 May 2026 15:21:08 +0800
Subject: [PATCH 0/6] perf: fix six memory-safety vulnerabilities in sched/header/subcmd

Hi, I found several vulnerabilities in perf module.

This series fixes six memory-safety bugs found in perf version 7.0.6,
in the perf sched stats subsystem and related infrastructure.  All six
were confirmed via AddressSanitizer + LeakSanitizer with crafted
perf.data files; the reproducer files are attached to the individual
patch emails.


Privilege note
==============
"perf sched stats record" requires root to capture
scheduler events from the kernel.  However, "perf sched stats report"
processes a perf.data file from disk and requires NO special privileges.
This means an unprivileged attacker can hand a crafted perf.data to any
user who runs "perf sched stats report" -- the attack surface is fully
reachable without administrator rights.

Crash-to-fix mapping
====================

  crash_err234_iter50.data  -> patch 1 (memory leaks in schedstat)
  crash_sig6_iter840.data   -> patch 2 (heap overflow via bitmap_zalloc)
  crash_sig7_iter210.data   -> patch 3 (SIGBUS via out-of-bounds mmap)
  crash_sig11_iter2.data    -> patch 4 (array OOB in domain index)
  (no poc)                  -> patch 5 (list_first_entry on empty list)
  crash_err255_iter46.data  -> patch 6 (astrcat leak in parse_options)

Vulnerability summary
=====================

1. Memory leaks in perf_sched__process_schedstat() / free_schedstat()
   (tools/perf/builtin-sched.c)

   Three distinct leak paths:
   a) When zalloc() of the inner data pointer (cpu_data or domain_data)
      fails, the outer struct is returned without freeing the parent.
   b) In the after_workload_flag=true branch the temporary struct and
      its embedded data pointer are used then discarded without being
      freed.
   c) free_schedstat() frees each cpu/domain node but not the
      cpu_data/domain_data pointers allocated inside each node.

   ASAN/LSan reports 72-144 bytes leaked per crafted event processed.

2. Heap buffer overflow via u64->int truncation in do_read_bitmap()
   (tools/perf/util/header.c)

   bitmap_zalloc() takes an int but do_read_bitmap() passes a raw u64
   read from the file.  If size > INT_MAX the int wraps to a small
   value, a tiny buffer is allocated, and the subsequent loop that
   reads BITS_TO_U64(size) u64 words from the file writes arbitrarily
   far past the end of the allocation (heap buffer overflow).

3. SIGBUS from data.offset beyond file size
   (tools/perf/util/header.c)

   A crafted perf.data can set perf_file_header.data.offset to a value
   larger than the actual file size.  mmap() succeeds because the
   kernel accepts out-of-file offsets, but any access to the mapped
   region triggers SIGBUS.  Confirmed with data.offset=0xff68=65384
   against a 4760-byte file.

4. Array out-of-bounds write via unchecked domain index
   (tools/perf/util/header.c)

   process_cpu_domain_info() reads a domain index from the file and
   uses it directly to index cd_map[cpu]->domains[], an array of
   max_sched_domains entries.  A domain value >= max_sched_domains
   causes an out-of-bounds write into adjacent heap memory.

5. list_first_entry() misuse on potentially-empty lists
   (tools/perf/builtin-sched.c)

   get_all_cpu_stats() and show_schedstat_data() call
   list_first_entry() which, unlike list_first_entry_or_null(),
   never returns NULL -- it computes container_of() on the list head
   itself when the list is empty, producing a garbage pointer.  The
   NULL checks that follow are therefore dead code.  A crafted
   perf.data that causes an empty list makes these functions dereference
   the garbage pointer.  Replaced with list_first_entry_or_null() plus
   proper NULL guards.

6. Memory leak in parse_options_subcommand()
   (tools/lib/subcmd/parse-options.c)

   When subcommands are present and no usage string has been supplied,
   parse_options_subcommand() builds a usage string with astrcat() and
   stores the pointer in usagestr[0].  The pointer is never freed,
   causing a 73-byte leak on every invocation.

Testing
=======

Affected version: perf 7.0.6

Each patch was verified with:

  $ make -C tools/perf EXTRA_CFLAGS="-fsanitize=address,leak \
      -fno-omit-frame-pointer -Wno-error=stringop-truncation" \
      -j$(nproc)

  $ ./perf sched stats report -i <crash-file>

Before the patch the corresponding crash file produced the ASAN/LSan
report listed above.  After the patch the same file is either rejected
cleanly (patches 2-4) or processed without any error report (patches
1, 5, 6).

The reproducer files (crash_*.data) are attached to the individual
patch emails; each is a minimal crafted perf.data (~4 KB) that
isolates exactly one bug.

Wang Haoran (6):
  perf/sched: fix memory leaks in schedstat processing
  perf/header: validate bitmap size before allocation in do_read_bitmap
  perf/header: reject data offset beyond file size
  perf/header: add bounds check for domain index in process_cpu_domain_info
  perf/sched: replace list_first_entry with list_first_entry_or_null
  subcmd: fix memory leak in parse_options_subcommand

 tools/lib/subcmd/parse-options.c |  3 ++-
 tools/perf/builtin-sched.c       | 32 +++++++++++++++++++++++++-------
 tools/perf/util/header.c         | 17 +++++++++++++++++
 3 files changed, 44 insertions(+), 8 deletions(-)

--
2.53.0

             reply	other threads:[~2026-05-29  6:49 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-29  6:49 Wang Haoran [this message]
2026-05-29  6:49 ` [PATCH 1/6] perf/sched: fix memory leaks in schedstat processing Wang Haoran
2026-05-29  6:50 ` [PATCH 2/6] perf/header: validate bitmap size before allocation in do_read_bitmap Wang Haoran
2026-05-29  6:50 ` [PATCH 3/6] perf/header: reject data offset beyond file size Wang Haoran
2026-05-29  6:50 ` [PATCH 4/6] perf/header: add bounds check for domain index in process_cpu_domain_info Wang Haoran
2026-05-29  6:50 ` [PATCH 5/6] perf/sched: replace list_first_entry with list_first_entry_or_null Wang Haoran
2026-05-29  6:50 ` [PATCH 6/6] subcmd: fix memory leak in parse_options_subcommand Wang Haoran

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=178003738371.62097.10360938456907564684@gmail.com \
    --to=haoranwangsec@gmail.com \
    --cc=acme@kernel.org \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    /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