summaryrefslogtreecommitdiff
path: root/tools/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-23 11:34:49 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-23 11:34:49 -0700
commit05d2a3da153bc08c5fe7937584b5d86505747b9e (patch)
tree69fe4a48b15fe7eb1b74d2298df710f4dbbd8b44 /tools/lib
parentf31c00c377ccf07c85442712f7c940a855cb3371 (diff)
parent3287a1881ca528b89b964d9fa6d28880d277d9e2 (diff)
Merge tag 'perf-tools-for-v7.2-1-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools
Pull perf tools updates from Arnaldo Carvalho de Melo: - Introduce 'perf inject --aslr' to remap ASLR-randomized addresses in perf.data files, enabling reproducible analysis across runs with different address space layouts - Refactor evsel out of sample processing paths: store evsel in struct perf_sample and remove the redundant evsel parameter from tool APIs, tracepoint handlers, hist entry iterators, and db-export, simplifying the entire tool callback chain - Switch architecture detection from string-based perf_env__arch() comparisons to the numeric ELF e_machine field across the codebase (capstone, print_insn, c2c, lock-contention, sort, sample-raw, machine, header), making cross-analysis more robust - Overhaul ARM CoreSight ETM tests: add deterministic and named_threads workloads, speed up basic and disassembly tests, add process attribution and concurrent threads tests, remove unused workloads and duplicate tests, queue context packets for the frontend decoder - Add ARM SPE IMPDEF event decoding for Arm Neoverse N1, store MIDR in arm_spe_pkt for per-CPU event mapping, handle missing CPU IDs gracefully - Refactor libunwind support: remove the libunwind-local backend, make register reading cross-platform, add RISC-V libunwind support, allow dynamic selection between libdw and libunwind unwinding at runtime - Extensive hardening of perf.data parsing against crafted files: add bounds checks and byte-swap validation for session records, feature sections, header attributes, BPF metadata, auxtrace errors, compressed events, CPU maps, build ID notes, and ELF program headers. Add minimum event size validation and file offset diagnostics - Fix libdw API contract violations across dwarf-aux, libdw, probe-finder, annotate-data, and debuginfo subsystems. Fix callchain parent update in ORDER_CALLER mode, support DWARF line 0 in inline lists, handle multiple address spaces in callchains - Fix numerous 'perf sched' bugs: thread reference leaks, memory leaks, heap overflows with cross-machine recordings, NULL dereferences, replace BUG_ON assertions with graceful error handling, bounds-check CPU indices, fix SIGCHLD vs pause() races in sched stats - Overhaul the build system: move BPF skeleton generation out of Makefile.perf into bpf_skel.mak, decouple pmu-events from the prepare target, make beauty generated C code standalone .o files, compile BPF skeletons with -mcpu=v3, fix continuous rebuilds, various cleanups - Add 'perf test' JUnit XML reporting with -j/--junit option, split monolithic test suites into sub-tests, add summary reporting, refactor parallel poll loop, fix test failures on musl-based systems - Fix 'perf c2c' memory leaks in hist entry and format list handling, use-after-free in error paths, bounds-check CPU and node IDs - Fix 'perf bpf' metadata leaks on duplicate insert and alloc failure, bounds-check array offsets, validate event sizes and func_info fields, add NULL checks - Fix hwmon PMU: off-by-one null termination on sysfs reads, strlcpy buffer overflow in parse_hwmon_filename(), fd 0 check, empty label reads, scnprintf usage - Fix symbols subsystem: bounds-check ELF and sysfs build ID note iteration, validate p_filesz, fix 32-bit ELF bswap error, fix signed overflow in size checks, bounds-check .gnu_debuglink section - Fix tools lib api: null termination in filename__read_int/ull(), uninitialized stack data in filename__write_int(), snprintf truncation in mount_overload() - Replace libbabeltrace with babeltrace2-ctf-writer for CTF conversion in 'perf data' - Add RISC-V SDT argument parsing for static tracepoints - Add 'perf trace --show-cpu' option to display CPU id - Add 'perf bench sched pipe --write-size' option - Add a perf-specific .clang-format that overrides some kernel style behaviors - Update Intel vendor events for Alder Lake, Arrow Lake, Clearwater Forest, Emerald Rapids, Granite Rapids, Grand Ridge, Lunar Lake, Meteor Lake, Panther Lake, Sapphire Rapids, Sierra Forest - Add IOMMU metrics for AMD and Intel - Fix AMD event: switch l2_itlb_misses to bp_l1_tlb_miss_l2_tlb_miss.all - Add AMD IBS improvements: decode Streaming-store and Remote-Socket flags, suppress bogus fields on Zen4+, skip privilege test on Zen6+ - Fix 'perf lock contention' SIGCHLD vs pause() race, allow 'mmap_lock' in -L filter, enable end-timestamp for cgroup aggregation, fix non-atomic data updates - Fix 'perf stat' false NMI watchdog warning in aggregation modes, bounds-check CPU index in topology callbacks, add aggr_nr metric parser support for uncore scaling - Fix 'perf timechart' memory leaks, CPU bounds checking, use-after-free on corrupted callchains - Fix 'perf inject' itrace branch stack synthesis, fix synthesized sample size with branch stacks - Fix DSO heap overflow on decompressed paths, uninitialized pathname on fallback, set proper error codes - Fix various snprintf/scnprintf usages to prevent buffer overflows and truncation across the codebase - Fix off-by-one stack buffer overflow in kallsyms__parse() - Fix 'perf kwork' memory management, address sanitizer issues, bounds check work->cpu - Fix 'perf tpebs' concurrent stop races and PID reuse hazards - Add O_CLOEXEC to open() calls and use mkostemp() for temporary files to prevent file descriptor leaks to child processes - Fix s390 Python extension TEXTREL by compiling as PIC - Fix build with ASAN for jitdump - Fix build failure due to btf_vlen() return type change * tag 'perf-tools-for-v7.2-1-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools: (343 commits) perf bpf: Fix up build failure due to change of btf_vlen() return type perf dso: Set standard errno on decompression failure perf bpf: Validate array presence before casting BPF prog info pointers perf c2c: Fix hist entry and format list leaks in c2c_he_free() perf c2c: Free format list entries when c2c_hists__init() fails perf cs-etm: Bounds-check CPU in cs_etm__get_queue() perf cs-etm: Require full global header in auxtrace_info size check perf cs-etm: Validate num_cpu before metadata allocation perf machine: Use snprintf() for guestmount path construction perf machine: Propagate machine__init() error to callers perf trace: Guard __probe_ip suppression with evsel__is_probe() perf evsel: Add lazy-initialized probe type detection helpers perf evsel: Add no-libtraceevent stubs for evsel__field() and evsel__common_field() perf cs-etm: Reject CPU IDs that would overflow signed comparison perf c2c: Free format list entries when releasing c2c hist entries perf bpf: Bounds-check array offsets in bpil_offs_to_addr() perf bpf: Reject oversized BPF metadata events that truncate header.size perf bpf: Validate func_info_rec_size and sub_id in synthesize_bpf_prog_name() perf sched: Replace (void*)1 sentinel with proper runtime allocation perf hwmon: Fix fd check to accept fd 0 in hwmon_pmu__describe_items() ...
Diffstat (limited to 'tools/lib')
-rw-r--r--tools/lib/api/fs/fs.c19
-rw-r--r--tools/lib/perf/TODO30
-rw-r--r--tools/lib/perf/include/perf/cpumap.h8
-rw-r--r--tools/lib/perf/include/perf/event.h9
-rw-r--r--tools/lib/subcmd/run-command.c69
-rw-r--r--tools/lib/symbol/kallsyms.c5
6 files changed, 127 insertions, 13 deletions
diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index edec23406dbc..cbd8eab0d1df 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -261,8 +261,8 @@ static const char *mount_overload(struct fs *fs)
/* "PERF_" + name + "_ENVIRONMENT" + '\0' */
char upper_name[5 + name_len + 12 + 1];
- snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name);
- mem_toupper(upper_name, name_len);
+ snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name);
+ mem_toupper(upper_name, strlen(upper_name));
return getenv(upper_name) ?: *fs->mounts;
}
@@ -294,11 +294,14 @@ int filename__read_int(const char *filename, int *value)
{
char line[64];
int fd = open(filename, O_RDONLY), err = -1;
+ ssize_t n;
if (fd < 0)
return -errno;
- if (read(fd, line, sizeof(line)) > 0) {
+ n = read(fd, line, sizeof(line) - 1);
+ if (n > 0) {
+ line[n] = '\0';
*value = atoi(line);
err = 0;
}
@@ -312,11 +315,14 @@ static int filename__read_ull_base(const char *filename,
{
char line[64];
int fd = open(filename, O_RDONLY), err = -1;
+ ssize_t n;
if (fd < 0)
return -errno;
- if (read(fd, line, sizeof(line)) > 0) {
+ n = read(fd, line, sizeof(line) - 1);
+ if (n > 0) {
+ line[n] = '\0';
*value = strtoull(line, NULL, base);
if (*value != ULLONG_MAX)
err = 0;
@@ -370,12 +376,13 @@ int filename__write_int(const char *filename, int value)
{
int fd = open(filename, O_WRONLY), err = -1;
char buf[64];
+ int len;
if (fd < 0)
return -errno;
- sprintf(buf, "%d", value);
- if (write(fd, buf, sizeof(buf)) == sizeof(buf))
+ len = sprintf(buf, "%d", value);
+ if (write(fd, buf, len) == len)
err = 0;
close(fd);
diff --git a/tools/lib/perf/TODO b/tools/lib/perf/TODO
new file mode 100644
index 000000000000..e179728697d8
--- /dev/null
+++ b/tools/lib/perf/TODO
@@ -0,0 +1,30 @@
+Future ABI changes
+==================
+
+This file collects items that require a libperf ABI bump. Each entry
+should describe the current limitation, the desired end state, and the
+scope of the change so that a future ABI revision can batch them
+together.
+
+1. Widen struct perf_cpu.cpu from int16_t to int
+ - Current limit: 32767 CPUs. No architecture exceeds this today
+ (x86_64 max is 8192, arm64 is 4096), but NR_CPUS limits keep
+ growing. perf clamps to INT16_MAX in set_max_cpu_num() as a
+ safety net.
+ - Code simplification: the int16_t forces defensive truncation
+ checks at every boundary where a wider CPU index (int from
+ sample->cpu, al->cpu, etc.) is narrowed into struct perf_cpu.
+ Without these checks, values > 32767 silently wrap to negative
+ numbers (two's complement), bypassing bounds validation.
+ Widening to int eliminates this entire class of silent
+ truncation bugs and removes the need for the INT16_MAX clamp
+ in set_max_cpu_num().
+ - Scope: struct perf_cpu is embedded everywhere — perf_cpu_map__cpu(),
+ perf_cpu_map__min(), perf_cpu_map__max(), perf_cpu_map__has(), the
+ for_each_cpu macros, and all internal callers. The perf_cpu_map
+ internal array (RC_CHK_ACCESS(map)->map[]) stores struct perf_cpu
+ directly. Widening changes the struct layout and every function
+ that returns or accepts struct perf_cpu by value.
+ - Migration: bump LIBPERF version in libperf.map, audit all
+ sizeof(struct perf_cpu) assumptions, update perf.data
+ serialization if needed.
diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h
index a1dd25db65b6..e1a0b0d27210 100644
--- a/tools/lib/perf/include/perf/cpumap.h
+++ b/tools/lib/perf/include/perf/cpumap.h
@@ -6,7 +6,13 @@
#include <stdbool.h>
#include <stdint.h>
-/** A wrapper around a CPU to avoid confusion with the perf_cpu_map's map's indices. */
+/**
+ * struct perf_cpu - wrapper around a CPU number.
+ * @cpu: CPU number, -1 for the "any CPU"/dummy value.
+ *
+ * int16_t limits this to 32767 CPUs. Widening to int requires a libperf
+ * ABI bump — see tools/lib/perf/TODO for the full scope.
+ */
struct perf_cpu {
int16_t cpu;
};
diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
index 9043dc72b5d6..fdced574c889 100644
--- a/tools/lib/perf/include/perf/event.h
+++ b/tools/lib/perf/include/perf/event.h
@@ -8,7 +8,14 @@
#include <linux/bpf.h>
#include <sys/types.h> /* pid_t */
-#define event_contains(obj, mem) ((obj).header.size > offsetof(typeof(obj), mem))
+/*
+ * Verify the full field fits within the event, not just its start offset.
+ * Only valid for fixed-size scalar fields — for trailing arrays like
+ * filename[PATH_MAX], sizeof() evaluates to the declared maximum, not
+ * the actual string length, so this would spuriously return false.
+ */
+#define event_contains(obj, mem) \
+ ((obj).header.size >= offsetof(typeof(obj), mem) + sizeof((obj).mem))
struct perf_record_mmap {
struct perf_event_header header;
diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c
index b7510f83209a..bd21b8bfd58b 100644
--- a/tools/lib/subcmd/run-command.c
+++ b/tools/lib/subcmd/run-command.c
@@ -169,8 +169,18 @@ int start_command(struct child_process *cmd)
static int wait_or_whine(struct child_process *cmd, bool block)
{
- bool finished = cmd->finished;
- int result = cmd->finish_result;
+ bool finished;
+ int result;
+
+ if (cmd->pid <= 0) {
+ cmd->finished = 1;
+ if (cmd->pid < 0 && cmd->finish_result == 0)
+ cmd->finish_result = -ERR_RUN_COMMAND_FORK;
+ return cmd->finish_result;
+ }
+
+ finished = cmd->finished;
+ result = cmd->finish_result;
while (!finished) {
int status, code;
@@ -233,7 +243,18 @@ int check_if_command_finished(struct child_process *cmd)
char filename[6 + MAX_STRLEN_TYPE(typeof(cmd->pid)) + 7 + 1];
char status_line[256];
FILE *status_file;
+#endif
+ if (cmd->finished)
+ return 1;
+ if (cmd->pid <= 0) {
+ cmd->finished = 1;
+ if (cmd->pid < 0 && cmd->finish_result == 0)
+ cmd->finish_result = -ERR_RUN_COMMAND_FORK;
+ return 1;
+ }
+
+#ifdef __linux__
/*
* Check by reading /proc/<pid>/status as calling waitpid causes
* stdout/stderr to be closed and data lost.
@@ -241,8 +262,48 @@ int check_if_command_finished(struct child_process *cmd)
sprintf(filename, "/proc/%u/status", cmd->pid);
status_file = fopen(filename, "r");
if (status_file == NULL) {
- /* Open failed assume finish_command was called. */
- return true;
+ int status;
+ pid_t waiting;
+
+ /*
+ * fopen() can fail with ENOENT if the process has been reaped.
+ * It can also fail with EMFILE/ENFILE if RLIMIT_NOFILE is reached.
+ * In those cases, use waitpid(..., WNOHANG) to robustly check
+ * and reap the process if it has exited.
+ */
+ if (errno == ENOENT)
+ return 1;
+
+ waiting = waitpid(cmd->pid, &status, WNOHANG);
+ if (waiting == cmd->pid) {
+ int result;
+ int code;
+
+ cmd->finished = 1;
+ if (WIFSIGNALED(status)) {
+ result = -ERR_RUN_COMMAND_WAITPID_SIGNAL;
+ } else if (!WIFEXITED(status)) {
+ result = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
+ } else {
+ code = WEXITSTATUS(status);
+ switch (code) {
+ case 127:
+ result = -ERR_RUN_COMMAND_EXEC;
+ break;
+ case 0:
+ result = 0;
+ break;
+ default:
+ result = -code;
+ break;
+ }
+ }
+ cmd->finish_result = result;
+ return 1;
+ }
+ if (waiting < 0 && (errno == ECHILD || errno == ESRCH))
+ return 1;
+ return 0;
}
while (fgets(status_line, sizeof(status_line), status_file) != NULL) {
char *p;
diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c
index e335ac2b9e19..d64bd9cc82a9 100644
--- a/tools/lib/symbol/kallsyms.c
+++ b/tools/lib/symbol/kallsyms.c
@@ -60,7 +60,7 @@ int kallsyms__parse(const char *filename, void *arg,
read_to_eol(&io);
continue;
}
- for (i = 0; i < sizeof(symbol_name); i++) {
+ for (i = 0; i < KSYM_NAME_LEN; i++) {
ch = io__get_char(&io);
if (ch < 0 || ch == '\n')
break;
@@ -68,6 +68,9 @@ int kallsyms__parse(const char *filename, void *arg,
}
symbol_name[i] = '\0';
+ if (i == KSYM_NAME_LEN)
+ read_to_eol(&io);
+
err = process_symbol(arg, symbol_name, symbol_type, start);
if (err)
break;