* [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety
@ 2026-07-20 17:54 Ian Rogers
2026-07-20 17:54 ` [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 17:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Ravi Bangoria, Swapnil Sapkal, linux-perf-users, linux-kernel
It turns out that PATH_MAX is respected by system calls but isn't
respected by file paths in /proc/pid/maps and /proc/pid/smaps. In the
kernel "//toolong" is placed in the filename of mmap/mmap2 events
where the filename is longer than PATH_MAX, do the same in the
mmap/mmap2 synthesis code to avoid overrunning the event buffer. With
Gemini's help try to address other correctness and overrun issues.
Ian Rogers (4):
perf find-map: Remove PATH_MAX 128-byte stack array restriction
perf synthetic-events: Fix line synchronization, bounds, and
truncation bugs in proc maps reader
perf synthetic-events: Fix bounds, stale state, and misc flags in
kernel module synthesis
perf synthetic-events: Fix bounds and union member access in mmap2
build_id synthesis
tools/perf/util/find-map.c | 11 +-
tools/perf/util/synthetic-events.c | 271 ++++++++++++++++++++---------
2 files changed, 196 insertions(+), 86 deletions(-)
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
@ 2026-07-20 17:54 ` Ian Rogers
2026-07-20 18:05 ` sashiko-bot
2026-07-20 17:54 ` [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
` (3 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 17:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Ravi Bangoria, Swapnil Sapkal, linux-perf-users, linux-kernel
Use getline() to dynamically allocate the required line buffer for maps
parsing, guaranteeing bounds safety and preventing buffer overruns.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/find-map.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/find-map.c b/tools/perf/util/find-map.c
index 7b2300588ece..4d740b32814f 100644
--- a/tools/perf/util/find-map.c
+++ b/tools/perf/util/find-map.c
@@ -1,8 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
static int find_map(void **start, void **end, const char *name)
{
FILE *maps;
- char line[128];
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t read_ret;
int found = 0;
maps = fopen("/proc/self/maps", "r");
@@ -11,7 +17,7 @@ static int find_map(void **start, void **end, const char *name)
return -1;
}
- while (!found && fgets(line, sizeof(line), maps)) {
+ while (!found && (read_ret = getline(&line, &len, maps)) != -1) {
int m = -1;
/* We care only about private r-x mappings. */
@@ -25,6 +31,7 @@ static int find_map(void **start, void **end, const char *name)
found = 1;
}
+ free(line);
fclose(maps);
return !found;
}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 17:54 ` [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
@ 2026-07-20 17:54 ` Ian Rogers
2026-07-20 18:08 ` sashiko-bot
2026-07-20 17:54 ` [PATCH v1 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 17:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Ravi Bangoria, Swapnil Sapkal, linux-perf-users, linux-kernel
Fix critical logic and boundary bugs in read_proc_maps_line() and caller.
Ensure any mid-line hex/dec/char parsing failure invokes io__drain_line()
to preserve line synchronization for subsequent map entries. Replace the
truncation bug (which improperly cleared over-length pathnames to an empty
string) with the kernel's standard '//toolong' fallback literal, and clamp
and pad structure size boundaries safely.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 183 ++++++++++++++++++++---------
1 file changed, 127 insertions(+), 56 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index b75f9dcf4dbf..9161dc728e6e 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -291,6 +291,15 @@ static int perf_event__synthesize_fork(const struct perf_tool *tool,
return 0;
}
+static void io__drain_line(struct io *io)
+{
+ int ch;
+
+ do {
+ ch = io__get_char(io);
+ } while (ch >= 0 && ch != '\n');
+}
+
static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
u32 *prot, u32 *flags, __u64 *offset,
u32 *maj, u32 *min,
@@ -299,69 +308,121 @@ static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
{
__u64 temp;
int ch;
- char *start_pathname = pathname;
+ size_t written = 0;
+ bool overflowed = false;
- if (io__get_hex(io, start) != '-')
+ if (io__get_hex(io, start) != '-') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
- if (io__get_hex(io, end) != ' ')
+ }
+ if (io__get_hex(io, end) != ' ') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
/* map protection and flags bits */
*prot = 0;
ch = io__get_char(io);
if (ch == 'r')
*prot |= PROT_READ;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
ch = io__get_char(io);
if (ch == 'w')
*prot |= PROT_WRITE;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
ch = io__get_char(io);
if (ch == 'x')
*prot |= PROT_EXEC;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
ch = io__get_char(io);
if (ch == 's')
*flags = MAP_SHARED;
else if (ch == 'p')
*flags = MAP_PRIVATE;
- else
+ else {
+ if (!io->eof)
+ io__drain_line(io);
return false;
- if (io__get_char(io) != ' ')
+ }
+ if (io__get_char(io) != ' ') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
- if (io__get_hex(io, offset) != ' ')
+ if (io__get_hex(io, offset) != ' ') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
- if (io__get_hex(io, &temp) != ':')
+ if (io__get_hex(io, &temp) != ':') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
*maj = temp;
- if (io__get_hex(io, &temp) != ' ')
+ if (io__get_hex(io, &temp) != ' ') {
+ if (!io->eof)
+ io__drain_line(io);
return false;
+ }
*min = temp;
ch = io__get_dec(io, inode);
if (ch != ' ') {
- *pathname = '\0';
- return ch == '\n';
+ if (ch == '\n') {
+ pathname[0] = '\0';
+ return true;
+ }
+ if (!io->eof)
+ io__drain_line(io);
+ return false;
}
+
do {
ch = io__get_char(io);
} while (ch == ' ');
+
while (true) {
- if (ch < 0)
- return false;
- if (ch == '\0' || ch == '\n' ||
- (pathname + 1 - start_pathname) >= pathname_size) {
- *pathname = '\0';
- return true;
+ if (ch < 0) {
+ if (overflowed) {
+ strlcpy(pathname, "//toolong", pathname_size);
+ return true;
+ }
+ pathname[written] = '\0';
+ return written > 0;
}
- *pathname++ = ch;
+ if (ch == '\0' || ch == '\n')
+ break;
+
+ if (written < (size_t)pathname_size - 1)
+ pathname[written++] = (char)ch;
+ else
+ overflowed = true;
ch = io__get_char(io);
}
+
+ if (overflowed)
+ strlcpy(pathname, "//toolong", pathname_size);
+ else
+ pathname[written] = '\0';
+
+ return true;
}
static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event,
@@ -457,29 +518,25 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool,
}
io__init(&io, io.fd, bf, sizeof(bf));
- event->header.type = PERF_RECORD_MMAP2;
t = rdclock();
while (!io.eof) {
static const char anonstr[] = "//anon";
size_t size, aligned_size;
-
- /* ensure null termination since stack will be reused. */
- event->mmap2.filename[0] = '\0';
+ __u64 start, end, pgoff, ino;
+ u32 prot, flags, maj, min;
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- if (!read_proc_maps_line(&io,
- &event->mmap2.start,
- &event->mmap2.len,
- &event->mmap2.prot,
- &event->mmap2.flags,
- &event->mmap2.pgoff,
- &event->mmap2.maj,
- &event->mmap2.min,
- &event->mmap2.ino,
- sizeof(event->mmap2.filename),
- event->mmap2.filename))
+ /* Read directly into event->mmap2.filename! */
+ if (!read_proc_maps_line(&io, &start, &end,
+ &prot, &flags, &pgoff,
+ &maj, &min, &ino,
+ sizeof(event->mmap2.filename),
+ event->mmap2.filename)) {
+ if (io.eof)
+ break;
continue;
+ }
if ((rdclock() - t) > timeout) {
pr_warning("Reading %s/proc/%d/task/%d/maps time out. "
@@ -487,50 +544,64 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool,
"the time limit by --proc-map-timeout\n",
machine->root_dir, pid, pid);
truncation = true;
- goto out;
}
- event->mmap2.ino_generation = 0;
+ if (!strcmp(event->mmap2.filename, ""))
+ strcpy(event->mmap2.filename, anonstr);
+
+ if (hugetlbfs_mnt_len &&
+ !strncmp(event->mmap2.filename, hugetlbfs_mnt, hugetlbfs_mnt_len)) {
+ strcpy(event->mmap2.filename, anonstr);
+ flags |= MAP_HUGETLB;
+ }
+
+ size = strlen(event->mmap2.filename) + 1;
+ aligned_size = PERF_ALIGN(size, sizeof(u64));
+
+ event->mmap2.header.type = PERF_RECORD_MMAP2;
/*
- * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
+ * Just like the kernel, see perf_misc_flags() in
+ * kernel/events/core.c
*/
if (machine__is_host(machine))
event->header.misc = PERF_RECORD_MISC_USER;
else
event->header.misc = PERF_RECORD_MISC_GUEST_USER;
- if ((event->mmap2.prot & PROT_EXEC) == 0) {
- if (!mmap_data || (event->mmap2.prot & PROT_READ) == 0)
+ if ((prot & PROT_EXEC) == 0) {
+ if (!mmap_data || (prot & PROT_READ) == 0) {
+ if (truncation)
+ break;
continue;
+ }
event->header.misc |= PERF_RECORD_MISC_MMAP_DATA;
}
-out:
if (truncation)
event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT;
- if (!strcmp(event->mmap2.filename, ""))
- strcpy(event->mmap2.filename, anonstr);
+ event->mmap2.header.size =
+ offsetof(struct perf_record_mmap2, filename) +
+ aligned_size;
- if (hugetlbfs_mnt_len &&
- !strncmp(event->mmap2.filename, hugetlbfs_mnt,
- hugetlbfs_mnt_len)) {
- strcpy(event->mmap2.filename, anonstr);
- event->mmap2.flags |= MAP_HUGETLB;
- }
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap2.filename + size, 0,
+ (aligned_size - size) + machine->id_hdr_size);
- size = strlen(event->mmap2.filename) + 1;
- aligned_size = PERF_ALIGN(size, sizeof(u64));
- event->mmap2.len -= event->mmap.start;
- event->mmap2.header.size = (sizeof(event->mmap2) -
- (sizeof(event->mmap2.filename) - aligned_size));
- memset(event->mmap2.filename + size, 0, machine->id_hdr_size +
- (aligned_size - size));
event->mmap2.header.size += machine->id_hdr_size;
+ event->mmap2.start = start;
+ event->mmap2.len = end - start;
+ event->mmap2.pgoff = pgoff;
+ event->mmap2.maj = maj;
+ event->mmap2.min = min;
+ event->mmap2.ino = ino;
+ event->mmap2.ino_generation = 0;
event->mmap2.pid = tgid;
event->mmap2.tid = pid;
+ event->mmap2.prot = prot;
+ event->mmap2.flags = flags;
if (!symbol_conf.no_buildid_mmap2)
perf_record_mmap2__read_build_id(&event->mmap2, machine, false);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 17:54 ` [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
2026-07-20 17:54 ` [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
@ 2026-07-20 17:54 ` Ian Rogers
2026-07-20 17:54 ` [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
4 siblings, 0 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 17:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Ravi Bangoria, Swapnil Sapkal, linux-perf-users, linux-kernel
Fix potential buffer overruns in perf_event__synthesize_modules_maps_cb()
by safely clamping long DSO names to the mmap/mmap2 filename boundaries.
Explicitly assign and clear the misc flags and union padding across all
iterations to prevent stale Build-ID state from leaking between module
synthesis events.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 70 +++++++++++++++++++++---------
1 file changed, 50 insertions(+), 20 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 9161dc728e6e..aab958cb3bc5 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -748,6 +748,7 @@ struct perf_event__synthesize_modules_maps_cb_args {
perf_event__handler_t process;
struct machine *machine;
union perf_event *event;
+ u16 misc;
};
static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data)
@@ -755,49 +756,78 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data)
struct perf_event__synthesize_modules_maps_cb_args *args = data;
union perf_event *event = args->event;
struct dso *dso;
- size_t size;
+ size_t size, aligned_size;
+ int rc = 0;
if (!__map__is_kmodule(map))
return 0;
dso = map__dso(map);
if (!symbol_conf.no_buildid_mmap2) {
- size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64));
+ const char *long_name = dso__long_name(dso);
+
+ size = strlen(long_name);
+ if (size >= sizeof(event->mmap2.filename))
+ size = sizeof(event->mmap2.filename) - 1;
+
+ strlcpy(event->mmap2.filename, long_name,
+ sizeof(event->mmap2.filename));
+
+ aligned_size = PERF_ALIGN(size + 1, sizeof(u64));
event->mmap2.header.type = PERF_RECORD_MMAP2;
- event->mmap2.header.size = (sizeof(event->mmap2) -
- (sizeof(event->mmap2.filename) - size));
- memset(event->mmap2.filename + size, 0, args->machine->id_hdr_size);
+ event->mmap2.header.misc = args->misc;
+ event->mmap2.header.size =
+ offsetof(struct perf_record_mmap2, filename) +
+ aligned_size;
+
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap2.filename + size, 0,
+ (aligned_size - size) + args->machine->id_hdr_size);
+
event->mmap2.header.size += args->machine->id_hdr_size;
event->mmap2.start = map__start(map);
event->mmap2.len = map__size(map);
event->mmap2.pid = args->machine->pid;
- memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1);
-
- /* Clear stale build ID from previous module iteration */
+ /* Clear stale build ID and entire union from previous module iteration */
event->mmap2.header.misc &= ~PERF_RECORD_MISC_MMAP_BUILD_ID;
memset(event->mmap2.build_id, 0, sizeof(event->mmap2.build_id));
event->mmap2.build_id_size = 0;
+ event->mmap2.__reserved_1 = 0;
+ event->mmap2.__reserved_2 = 0;
perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false);
} else {
- size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64));
+ const char *long_name = dso__long_name(dso);
+
+ size = strlen(long_name);
+ if (size >= sizeof(event->mmap.filename))
+ size = sizeof(event->mmap.filename) - 1;
+
+ strlcpy(event->mmap.filename, long_name,
+ sizeof(event->mmap.filename));
+
+ aligned_size = PERF_ALIGN(size + 1, sizeof(u64));
event->mmap.header.type = PERF_RECORD_MMAP;
- event->mmap.header.size = (sizeof(event->mmap) -
- (sizeof(event->mmap.filename) - size));
- memset(event->mmap.filename + size, 0, args->machine->id_hdr_size);
+ event->mmap.header.misc = args->misc;
+ event->mmap.header.size =
+ offsetof(struct perf_record_mmap, filename) +
+ aligned_size;
+
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap.filename + size, 0,
+ (aligned_size - size) + args->machine->id_hdr_size);
+
event->mmap.header.size += args->machine->id_hdr_size;
event->mmap.start = map__start(map);
event->mmap.len = map__size(map);
event->mmap.pid = args->machine->pid;
-
- memcpy(event->mmap.filename, dso__long_name(dso), dso__long_name_len(dso) + 1);
}
if (perf_tool__process_synth_event(args->tool, event, args->machine, args->process) != 0)
- return -1;
+ rc = -1;
- return 0;
+ return rc;
}
int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process,
@@ -822,13 +852,13 @@ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__han
}
/*
- * kernel uses 0 for user space maps, see kernel/perf_event.c
- * __perf_event_mmap
+ * Just like the kernel, see perf_misc_flags() in
+ * kernel/events/core.c
*/
if (machine__is_host(machine))
- args.event->header.misc = PERF_RECORD_MISC_KERNEL;
+ args.misc = PERF_RECORD_MISC_KERNEL;
else
- args.event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
+ args.misc = PERF_RECORD_MISC_GUEST_KERNEL;
rc = maps__for_each_map(maps, perf_event__synthesize_modules_maps_cb, &args);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
` (2 preceding siblings ...)
2026-07-20 17:54 ` [PATCH v1 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
@ 2026-07-20 17:54 ` Ian Rogers
2026-07-20 18:28 ` sashiko-bot
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
4 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 17:54 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Ravi Bangoria, Swapnil Sapkal, linux-perf-users, linux-kernel
Fix a critical logic bug in perf_event__synthesize_mmap2_build_id() where
the wrong union member structure size and offset boundaries were utilized.
Safely calculate and clamp maximum filename length to guarantee absolute
stack boundary protections for ID sample trailers, and remove void-pointer
arithmetic to meet strict standard C compliance.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index aab958cb3bc5..cb5b659822f5 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -2441,13 +2441,16 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool,
size_t filename_len = strlen(filename);
size_t ev_len;
u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0;
- void *array;
+ void *array = &ev;
int ret;
+ size_t max_filename_len;
- if (filename_len >= sizeof(ev.mmap2.filename))
- return -EINVAL;
+ max_filename_len = sizeof(ev.mmap2.filename) - (MAX_ID_HDR_ENTRIES * sizeof(__u64));
- ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + filename_len + 1;
+ if (filename_len > max_filename_len)
+ filename_len = max_filename_len;
+
+ ev_len = offsetof(struct perf_record_mmap2, filename) + filename_len + 1;
ev_len = PERF_ALIGN(ev_len, sizeof(u64));
if (ev_len + MAX_ID_HDR_ENTRIES * sizeof(__u64) > sizeof(ev))
@@ -2467,16 +2470,15 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool,
ev.mmap2.build_id_size = bid->size;
if (ev.mmap2.build_id_size > sizeof(ev.mmap2.build_id))
- ev.build_id.size = sizeof(ev.mmap2.build_id);
+ ev.mmap2.build_id_size = sizeof(ev.mmap2.build_id);
memcpy(ev.mmap2.build_id, bid->data, ev.mmap2.build_id_size);
ev.mmap2.prot = prot;
ev.mmap2.flags = flags;
- memcpy(ev.mmap2.filename, filename, min(strlen(filename), sizeof(ev.mmap.filename)));
+ strlcpy(ev.mmap2.filename, filename, filename_len + 1);
- array = &ev;
- array += ev.header.size;
+ array = (void *)((char *)&ev + ev.header.size);
ret = perf_event__synthesize_id_sample(array, sample_type, sample);
if (ret < 0)
return ret;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction
2026-07-20 17:54 ` [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
@ 2026-07-20 18:05 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-20 18:05 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `read_ret` variable is assigned the result of `getline()` but is never subsequently used, which triggers a `-Wunused-but-set-variable` compilation error.
--
--- Patch [1]: [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction ---
commit 9da3c5d9150546a40fb376c8547c0725fc587af7
Author: Ian Rogers <irogers@google.com>
perf find-map: Remove PATH_MAX 128-byte stack array restriction
This commit updates the find_map function to dynamically allocate the
line buffer using getline. This prevents potential buffer overruns
associated with the previous 128-byte stack array limitation.
> diff --git a/tools/perf/util/find-map.c b/tools/perf/util/find-map.c
> index 7b2300588ece0..4d740b32814f8 100644
> --- a/tools/perf/util/find-map.c
> +++ b/tools/perf/util/find-map.c
[ ... ]
> @@ -1,8 +1,14 @@
> // SPDX-License-Identifier: GPL-2.0
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> static int find_map(void **start, void **end, const char *name)
> {
> FILE *maps;
> - char line[128];
> + char *line = NULL;
> + size_t len = 0;
> + ssize_t read_ret;
> int found = 0;
>
> maps = fopen("/proc/self/maps", "r");
> @@ -11,7 +17,7 @@ static int find_map(void **start, void **end, const char *name)
> return -1;
> }
>
> - while (!found && fgets(line, sizeof(line), maps)) {
> + while (!found && (read_ret = getline(&line, &len, maps)) != -1) {
[Severity: Low]
Does this leave the read_ret variable set but never used?
Because perf is typically built with -Werror, this unused assignment
triggers a -Wunused-but-set-variable compiler warning which gets promoted
to a build failure.
Could the read_ret variable be removed and the loop condition simplified to
just evaluate getline(&line, &len, maps) != -1 directly?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720175455.3645946-1-irogers@google.com?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
2026-07-20 17:54 ` [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
@ 2026-07-20 18:08 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-20 18:08 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Error recovery via `io__drain_line()` incorrectly consumes and discards the next valid map entry if a parsing failure occurs due to a premature newline character.
- [Medium] The PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT flag is silently dropped if the truncation timeout triggers on a map entry that fails the PROT_EXEC/mmap_data filters.
--
--- Patch [2]: [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader ---
commit 21e332ccbb14a1f50a59ea36a8f2a24a05ff7df8
Author: Ian Rogers <irogers@google.com>
perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
This commit addresses logic and boundary bugs in read_proc_maps_line() and its
caller. It ensures that mid-line parsing failures invoke io__drain_line() to
preserve synchronization and replaces truncation bugs with a standard fallback
literal.
> diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
> index 5307d707711d8..723b7323c06d9 100644
> --- a/tools/perf/util/synthetic-events.c
> +++ b/tools/perf/util/synthetic-events.c
> @@ -291,6 +291,15 @@ static int perf_event__synthesize_fork(const struct perf_tool *tool,
> return 0;
> }
>
> +static void io__drain_line(struct io *io)
> +{
> + int ch;
> +
> + do {
> + ch = io__get_char(io);
> + } while (ch >= 0 && ch != '\n');
> +}
> +
> static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
> u32 *prot, u32 *flags, __u64 *offset,
> u32 *maj, u32 *min,
> @@ -299,69 +308,121 @@ static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
> {
> __u64 temp;
> int ch;
> - char *start_pathname = pathname;
> + size_t written = 0;
> + bool overflowed = false;
>
> - if (io__get_hex(io, start) != '-')
> + if (io__get_hex(io, start) != '-') {
> + if (!io->eof)
> + io__drain_line(io);
[Severity: High]
If a parser like io__get_hex() or io__get_char() fails because it encounters a
premature newline character, does calling io__drain_line() here unintentionally
consume and discard the next valid map entry?
Since the newline character for the malformed line would have already been
consumed by the parser, it appears io__drain_line() might read until the end
of the subsequent line, causing it to be skipped.
> + return false;
> + }
[ ... ]
> @@ -487,50 +544,64 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool,
[ ... ]
> - if ((event->mmap2.prot & PROT_EXEC) == 0) {
> - if (!mmap_data || (event->mmap2.prot & PROT_READ) == 0)
> + if ((prot & PROT_EXEC) == 0) {
> + if (!mmap_data || (prot & PROT_READ) == 0) {
> + if (truncation)
> + break;
[Severity: Medium]
When the truncation timeout triggers on a map entry that fails the PROT_EXEC
or mmap_data filters, does this break prevent the timeout flag
(PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT) from being set and emitted?
By breaking out of the loop early here, it seems the code bypasses both the
PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT assignment and the call to
perf_tool__process_synth_event(), which might leave analysis tools unaware
that the data stream was truncated.
> continue;
> + }
>
> event->header.misc |= PERF_RECORD_MISC_MMAP_DATA;
> }
>
> -out:
> if (truncation)
> event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720175455.3645946-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis
2026-07-20 17:54 ` [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
@ 2026-07-20 18:28 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-20 18:28 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly introduced `max_filename_len` clamping mechanism fails to prevent `-E2BIG` errors for long filenames because it does not account for alignment padding and the NUL byte.
--
--- Patch [4]: [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis ---
commit 2f96e2c59cef660df31770f938440d336831e6e7
Author: Ian Rogers <irogers@google.com>
perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis
Fix a critical logic bug in perf_event__synthesize_mmap2_build_id() where
the wrong union member structure size and offset boundaries were utilized.
> diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
> index 043f902f1bd2e..3a4030835a320 100644
> --- a/tools/perf/util/synthetic-events.c
> +++ b/tools/perf/util/synthetic-events.c
> @@ -2441,13 +2441,16 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool,
> size_t filename_len = strlen(filename);
> size_t ev_len;
> u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0;
> - void *array;
> + void *array = &ev;
> int ret;
> + size_t max_filename_len;
>
> - if (filename_len >= sizeof(ev.mmap2.filename))
> - return -EINVAL;
> + max_filename_len = sizeof(ev.mmap2.filename) - (MAX_ID_HDR_ENTRIES * sizeof(__u64));
>
> - ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + filename_len + 1;
> + if (filename_len > max_filename_len)
> + filename_len = max_filename_len;
> +
> + ev_len = offsetof(struct perf_record_mmap2, filename) + filename_len + 1;
> ev_len = PERF_ALIGN(ev_len, sizeof(u64));
>
> if (ev_len + MAX_ID_HDR_ENTRIES * sizeof(__u64) > sizeof(ev))
> return -E2BIG;
[Severity: Medium]
Does this clamping logic fail to prevent -E2BIG errors for very long
filenames?
If filename_len is clamped to max_filename_len (4048 bytes), ev_len is
subsequently calculated as offsetof(struct perf_record_mmap2, filename) +
4048 + 1 (for the NUL byte), which equals 4129.
PERF_ALIGN(4129, sizeof(u64)) aligns this up to 4136.
When checking if the padded length plus the ID header trailer exceeds the
maximum event size:
4136 + 48 = 4184, which exceeds sizeof(ev) (4176).
This causes the function to return -E2BIG instead of successfully truncating
and processing the event. Should max_filename_len account for the NUL byte
and the 8-byte alignment padding to ensure the clamped size fits within
the boundaries?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720175455.3645946-1-irogers@google.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
` (3 preceding siblings ...)
2026-07-20 17:54 ` [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
@ 2026-07-20 22:51 ` Ian Rogers
2026-07-20 22:51 ` [PATCH v2 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
` (3 more replies)
4 siblings, 4 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 22:51 UTC (permalink / raw)
To: acme, namhyung
Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
mingo, peterz, ravi.bangoria, swapnil.sapkal, Ian Rogers
It turns out that PATH_MAX is respected by system calls but isn't
respected by file paths in /proc/pid/maps and /proc/pid/smaps. In the
kernel "//toolong" is placed in the filename of mmap/mmap2 events
where the filename is longer than PATH_MAX, do the same in the
mmap/mmap2 synthesis code to avoid overrunning the event buffer. With
Gemini's help try to address other correctness and overrun issues.
V2 addresses the following Sashiko review feedback:
- Corrects read_proc_maps_line() and io__drain_line() to safely handle
already consumed newlines, preventing valid map data from being skipped.
- Restores the early exit block for timeouts so the TIMEOUT flag is
always emitted to tools.
- Removes an unused assignment to avoid promoting warnings to build errors.
- Clarifies and dynamically computes the maximum filename length boundaries
using offsetof() to prevent E2BIG errors regardless of struct layout.
Ian Rogers (4):
perf find-map: Remove PATH_MAX 128-byte stack array restriction
perf synthetic-events: Fix line synchronization, bounds, and
truncation bugs in proc maps reader
perf synthetic-events: Fix bounds, stale state, and misc flags in
kernel module synthesis
perf synthetic-events: Fix bounds and union member access in mmap2
build_id synthesis
tools/perf/util/find-map.c | 10 +-
tools/perf/util/synthetic-events.c | 287 ++++++++++++++++++++---------
2 files changed, 207 insertions(+), 90 deletions(-)
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
@ 2026-07-20 22:51 ` Ian Rogers
2026-07-20 22:51 ` [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
` (2 subsequent siblings)
3 siblings, 0 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 22:51 UTC (permalink / raw)
To: acme, namhyung
Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
mingo, peterz, ravi.bangoria, swapnil.sapkal, Ian Rogers
Use getline() to dynamically allocate the required line buffer for maps
parsing, guaranteeing bounds safety and avoiding compiler warnings
by evaluating the return value in the loop condition directly.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/find-map.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/find-map.c b/tools/perf/util/find-map.c
index 7b2300588ece..bba511795a69 100644
--- a/tools/perf/util/find-map.c
+++ b/tools/perf/util/find-map.c
@@ -1,8 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
static int find_map(void **start, void **end, const char *name)
{
FILE *maps;
- char line[128];
+ char *line = NULL;
+ size_t len = 0;
int found = 0;
maps = fopen("/proc/self/maps", "r");
@@ -11,7 +16,7 @@ static int find_map(void **start, void **end, const char *name)
return -1;
}
- while (!found && fgets(line, sizeof(line), maps)) {
+ while (!found && getline(&line, &len, maps) != -1) {
int m = -1;
/* We care only about private r-x mappings. */
@@ -25,6 +30,7 @@ static int find_map(void **start, void **end, const char *name)
found = 1;
}
+ free(line);
fclose(maps);
return !found;
}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 22:51 ` [PATCH v2 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
@ 2026-07-20 22:51 ` Ian Rogers
2026-07-20 23:03 ` sashiko-bot
2026-07-20 22:51 ` [PATCH v2 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
2026-07-20 22:52 ` [PATCH v2 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
3 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 22:51 UTC (permalink / raw)
To: acme, namhyung
Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
mingo, peterz, ravi.bangoria, swapnil.sapkal, Ian Rogers
Fix critical logic and boundary bugs in read_proc_maps_line() and caller.
Ensure any mid-line hex/dec/char parsing failure invokes io__drain_line()
safely, ignoring already-consumed newlines to preserve synchronization
for subsequent map entries. Use standard '//toolong' fallback literal
for over-length pathnames, and emit timeout flags for truncated entries
securely via goto out;.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 197 ++++++++++++++++++++---------
1 file changed, 137 insertions(+), 60 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index b75f9dcf4dbf..9a2b4d561e9e 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -291,6 +291,17 @@ static int perf_event__synthesize_fork(const struct perf_tool *tool,
return 0;
}
+static void io__drain_line(struct io *io, int ch)
+{
+ if (ch == '\n')
+ return;
+ if (ch == -2 && io->data > io->buf && io->data[-1] == '\n')
+ return;
+
+ while (ch >= 0 && ch != '\n')
+ ch = io__get_char(io);
+}
+
static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
u32 *prot, u32 *flags, __u64 *offset,
u32 *maj, u32 *min,
@@ -299,69 +310,127 @@ static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
{
__u64 temp;
int ch;
- char *start_pathname = pathname;
+ size_t written = 0;
+ bool overflowed = false;
- if (io__get_hex(io, start) != '-')
+ ch = io__get_hex(io, start);
+ if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
- if (io__get_hex(io, end) != ' ')
+ }
+ ch = io__get_hex(io, end);
+ if (ch != ' ') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
/* map protection and flags bits */
*prot = 0;
ch = io__get_char(io);
if (ch == 'r')
*prot |= PROT_READ;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
ch = io__get_char(io);
if (ch == 'w')
*prot |= PROT_WRITE;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
ch = io__get_char(io);
if (ch == 'x')
*prot |= PROT_EXEC;
- else if (ch != '-')
+ else if (ch != '-') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
ch = io__get_char(io);
if (ch == 's')
*flags = MAP_SHARED;
else if (ch == 'p')
*flags = MAP_PRIVATE;
- else
+ else {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
- if (io__get_char(io) != ' ')
+ }
+ ch = io__get_char(io);
+ if (ch != ' ') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
- if (io__get_hex(io, offset) != ' ')
+ ch = io__get_hex(io, offset);
+ if (ch != ' ') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
- if (io__get_hex(io, &temp) != ':')
+ ch = io__get_hex(io, &temp);
+ if (ch != ':') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
*maj = temp;
- if (io__get_hex(io, &temp) != ' ')
+ ch = io__get_hex(io, &temp);
+ if (ch != ' ') {
+ if (!io->eof)
+ io__drain_line(io, ch);
return false;
+ }
*min = temp;
ch = io__get_dec(io, inode);
if (ch != ' ') {
- *pathname = '\0';
- return ch == '\n';
+ if (ch == '\n') {
+ pathname[0] = '\0';
+ return true;
+ }
+ if (!io->eof)
+ io__drain_line(io, ch);
+ return false;
}
+
do {
ch = io__get_char(io);
} while (ch == ' ');
+
while (true) {
- if (ch < 0)
- return false;
- if (ch == '\0' || ch == '\n' ||
- (pathname + 1 - start_pathname) >= pathname_size) {
- *pathname = '\0';
- return true;
+ if (ch < 0) {
+ if (overflowed) {
+ strlcpy(pathname, "//toolong", pathname_size);
+ return true;
+ }
+ pathname[written] = '\0';
+ return written > 0;
}
- *pathname++ = ch;
+ if (ch == '\0' || ch == '\n')
+ break;
+
+ if (written < (size_t)pathname_size - 1)
+ pathname[written++] = (char)ch;
+ else
+ overflowed = true;
ch = io__get_char(io);
}
+
+ if (overflowed)
+ strlcpy(pathname, "//toolong", pathname_size);
+ else
+ pathname[written] = '\0';
+
+ return true;
}
static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event,
@@ -463,45 +532,53 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool,
while (!io.eof) {
static const char anonstr[] = "//anon";
size_t size, aligned_size;
-
- /* ensure null termination since stack will be reused. */
- event->mmap2.filename[0] = '\0';
+ __u64 start, end, pgoff, ino;
+ u32 prot, flags, maj, min;
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- if (!read_proc_maps_line(&io,
- &event->mmap2.start,
- &event->mmap2.len,
- &event->mmap2.prot,
- &event->mmap2.flags,
- &event->mmap2.pgoff,
- &event->mmap2.maj,
- &event->mmap2.min,
- &event->mmap2.ino,
- sizeof(event->mmap2.filename),
- event->mmap2.filename))
+ /* Read directly into event->mmap2.filename! */
+ if (!read_proc_maps_line(&io, &start, &end,
+ &prot, &flags, &pgoff,
+ &maj, &min, &ino,
+ sizeof(event->mmap2.filename),
+ event->mmap2.filename)) {
+ if (io.eof)
+ break;
continue;
+ }
- if ((rdclock() - t) > timeout) {
- pr_warning("Reading %s/proc/%d/task/%d/maps time out. "
- "You may want to increase "
- "the time limit by --proc-map-timeout\n",
- machine->root_dir, pid, pid);
- truncation = true;
- goto out;
+ if (!strcmp(event->mmap2.filename, ""))
+ strcpy(event->mmap2.filename, anonstr);
+
+ if (hugetlbfs_mnt_len &&
+ !strncmp(event->mmap2.filename, hugetlbfs_mnt, hugetlbfs_mnt_len)) {
+ strcpy(event->mmap2.filename, anonstr);
+ flags |= MAP_HUGETLB;
}
- event->mmap2.ino_generation = 0;
+ size = strlen(event->mmap2.filename) + 1;
+ aligned_size = PERF_ALIGN(size, sizeof(u64));
+
+ event->mmap2.header.type = PERF_RECORD_MMAP2;
/*
- * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
+ * Just like the kernel, see perf_misc_flags() in
+ * kernel/events/core.c
*/
if (machine__is_host(machine))
event->header.misc = PERF_RECORD_MISC_USER;
else
event->header.misc = PERF_RECORD_MISC_GUEST_USER;
- if ((event->mmap2.prot & PROT_EXEC) == 0) {
- if (!mmap_data || (event->mmap2.prot & PROT_READ) == 0)
+ if ((rdclock() - t) > timeout) {
+ pr_warning("Reading %s/proc/%d/task/%d/maps time out. You may want to increase the time limit by --proc-map-timeout\n",
+ machine->root_dir, pid, pid);
+ truncation = true;
+ goto out;
+ }
+
+ if ((prot & PROT_EXEC) == 0) {
+ if (!mmap_data || (prot & PROT_READ) == 0)
continue;
event->header.misc |= PERF_RECORD_MISC_MMAP_DATA;
@@ -511,26 +588,26 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool,
if (truncation)
event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT;
- if (!strcmp(event->mmap2.filename, ""))
- strcpy(event->mmap2.filename, anonstr);
+ event->mmap2.header.size =
+ offsetof(struct perf_record_mmap2, filename) +
+ aligned_size;
- if (hugetlbfs_mnt_len &&
- !strncmp(event->mmap2.filename, hugetlbfs_mnt,
- hugetlbfs_mnt_len)) {
- strcpy(event->mmap2.filename, anonstr);
- event->mmap2.flags |= MAP_HUGETLB;
- }
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap2.filename + size, 0,
+ (aligned_size - size) + machine->id_hdr_size);
- size = strlen(event->mmap2.filename) + 1;
- aligned_size = PERF_ALIGN(size, sizeof(u64));
- event->mmap2.len -= event->mmap.start;
- event->mmap2.header.size = (sizeof(event->mmap2) -
- (sizeof(event->mmap2.filename) - aligned_size));
- memset(event->mmap2.filename + size, 0, machine->id_hdr_size +
- (aligned_size - size));
event->mmap2.header.size += machine->id_hdr_size;
+ event->mmap2.start = start;
+ event->mmap2.len = end - start;
+ event->mmap2.pgoff = pgoff;
+ event->mmap2.maj = maj;
+ event->mmap2.min = min;
+ event->mmap2.ino = ino;
+ event->mmap2.ino_generation = 0;
event->mmap2.pid = tgid;
event->mmap2.tid = pid;
+ event->mmap2.prot = prot;
+ event->mmap2.flags = flags;
if (!symbol_conf.no_buildid_mmap2)
perf_record_mmap2__read_build_id(&event->mmap2, machine, false);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 22:51 ` [PATCH v2 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
2026-07-20 22:51 ` [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
@ 2026-07-20 22:51 ` Ian Rogers
2026-07-20 22:52 ` [PATCH v2 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
3 siblings, 0 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 22:51 UTC (permalink / raw)
To: acme, namhyung
Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
mingo, peterz, ravi.bangoria, swapnil.sapkal, Ian Rogers
Fix potential buffer overruns in perf_event__synthesize_modules_maps_cb()
by safely clamping long DSO names to the mmap/mmap2 filename boundaries.
Explicitly assign and clear the misc flags and union padding across all
iterations to prevent stale Build-ID state from leaking between module
synthesis events.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 70 +++++++++++++++++++++---------
1 file changed, 50 insertions(+), 20 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 9a2b4d561e9e..068323b9510d 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -754,6 +754,7 @@ struct perf_event__synthesize_modules_maps_cb_args {
perf_event__handler_t process;
struct machine *machine;
union perf_event *event;
+ u16 misc;
};
static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data)
@@ -761,49 +762,78 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data)
struct perf_event__synthesize_modules_maps_cb_args *args = data;
union perf_event *event = args->event;
struct dso *dso;
- size_t size;
+ size_t size, aligned_size;
+ int rc = 0;
if (!__map__is_kmodule(map))
return 0;
dso = map__dso(map);
if (!symbol_conf.no_buildid_mmap2) {
- size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64));
+ const char *long_name = dso__long_name(dso);
+
+ size = strlen(long_name);
+ if (size >= sizeof(event->mmap2.filename))
+ size = sizeof(event->mmap2.filename) - 1;
+
+ strlcpy(event->mmap2.filename, long_name,
+ sizeof(event->mmap2.filename));
+
+ aligned_size = PERF_ALIGN(size + 1, sizeof(u64));
event->mmap2.header.type = PERF_RECORD_MMAP2;
- event->mmap2.header.size = (sizeof(event->mmap2) -
- (sizeof(event->mmap2.filename) - size));
- memset(event->mmap2.filename + size, 0, args->machine->id_hdr_size);
+ event->mmap2.header.misc = args->misc;
+ event->mmap2.header.size =
+ offsetof(struct perf_record_mmap2, filename) +
+ aligned_size;
+
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap2.filename + size, 0,
+ (aligned_size - size) + args->machine->id_hdr_size);
+
event->mmap2.header.size += args->machine->id_hdr_size;
event->mmap2.start = map__start(map);
event->mmap2.len = map__size(map);
event->mmap2.pid = args->machine->pid;
- memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1);
-
- /* Clear stale build ID from previous module iteration */
+ /* Clear stale build ID and entire union from previous module iteration */
event->mmap2.header.misc &= ~PERF_RECORD_MISC_MMAP_BUILD_ID;
memset(event->mmap2.build_id, 0, sizeof(event->mmap2.build_id));
event->mmap2.build_id_size = 0;
+ event->mmap2.__reserved_1 = 0;
+ event->mmap2.__reserved_2 = 0;
perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false);
} else {
- size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64));
+ const char *long_name = dso__long_name(dso);
+
+ size = strlen(long_name);
+ if (size >= sizeof(event->mmap.filename))
+ size = sizeof(event->mmap.filename) - 1;
+
+ strlcpy(event->mmap.filename, long_name,
+ sizeof(event->mmap.filename));
+
+ aligned_size = PERF_ALIGN(size + 1, sizeof(u64));
event->mmap.header.type = PERF_RECORD_MMAP;
- event->mmap.header.size = (sizeof(event->mmap) -
- (sizeof(event->mmap.filename) - size));
- memset(event->mmap.filename + size, 0, args->machine->id_hdr_size);
+ event->mmap.header.misc = args->misc;
+ event->mmap.header.size =
+ offsetof(struct perf_record_mmap, filename) +
+ aligned_size;
+
+ /* Zero the padding and ID header trailer safely! */
+ memset(event->mmap.filename + size, 0,
+ (aligned_size - size) + args->machine->id_hdr_size);
+
event->mmap.header.size += args->machine->id_hdr_size;
event->mmap.start = map__start(map);
event->mmap.len = map__size(map);
event->mmap.pid = args->machine->pid;
-
- memcpy(event->mmap.filename, dso__long_name(dso), dso__long_name_len(dso) + 1);
}
if (perf_tool__process_synth_event(args->tool, event, args->machine, args->process) != 0)
- return -1;
+ rc = -1;
- return 0;
+ return rc;
}
int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process,
@@ -828,13 +858,13 @@ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__han
}
/*
- * kernel uses 0 for user space maps, see kernel/perf_event.c
- * __perf_event_mmap
+ * Just like the kernel, see perf_misc_flags() in
+ * kernel/events/core.c
*/
if (machine__is_host(machine))
- args.event->header.misc = PERF_RECORD_MISC_KERNEL;
+ args.misc = PERF_RECORD_MISC_KERNEL;
else
- args.event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
+ args.misc = PERF_RECORD_MISC_GUEST_KERNEL;
rc = maps__for_each_map(maps, perf_event__synthesize_modules_maps_cb, &args);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
` (2 preceding siblings ...)
2026-07-20 22:51 ` [PATCH v2 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
@ 2026-07-20 22:52 ` Ian Rogers
3 siblings, 0 replies; 14+ messages in thread
From: Ian Rogers @ 2026-07-20 22:52 UTC (permalink / raw)
To: acme, namhyung
Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
mingo, peterz, ravi.bangoria, swapnil.sapkal, Ian Rogers
Fix a critical logic bug in perf_event__synthesize_mmap2_build_id() where
the wrong union member structure size and offset boundaries were utilized.
Safely calculate the exact maximum allowed filename length to guarantee
absolute stack and alignment boundaries for ID sample trailers, preventing
-E2BIG overruns on very long filenames while meeting strict standard C
compliance.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/synthetic-events.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 068323b9510d..fb4f736f64c0 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -2447,13 +2447,18 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool,
size_t filename_len = strlen(filename);
size_t ev_len;
u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0;
- void *array;
+ void *array = &ev;
int ret;
+ size_t max_filename_len;
- if (filename_len >= sizeof(ev.mmap2.filename))
- return -EINVAL;
+ max_filename_len = sizeof(ev) -
+ (MAX_ID_HDR_ENTRIES * sizeof(__u64)) -
+ offsetof(struct perf_record_mmap2, filename) - 1;
- ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + filename_len + 1;
+ if (filename_len > max_filename_len)
+ filename_len = max_filename_len;
+
+ ev_len = offsetof(struct perf_record_mmap2, filename) + filename_len + 1;
ev_len = PERF_ALIGN(ev_len, sizeof(u64));
if (ev_len + MAX_ID_HDR_ENTRIES * sizeof(__u64) > sizeof(ev))
@@ -2473,16 +2478,15 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool,
ev.mmap2.build_id_size = bid->size;
if (ev.mmap2.build_id_size > sizeof(ev.mmap2.build_id))
- ev.build_id.size = sizeof(ev.mmap2.build_id);
+ ev.mmap2.build_id_size = sizeof(ev.mmap2.build_id);
memcpy(ev.mmap2.build_id, bid->data, ev.mmap2.build_id_size);
ev.mmap2.prot = prot;
ev.mmap2.flags = flags;
- memcpy(ev.mmap2.filename, filename, min(strlen(filename), sizeof(ev.mmap.filename)));
+ strlcpy(ev.mmap2.filename, filename, filename_len + 1);
- array = &ev;
- array += ev.header.size;
+ array = (void *)((char *)&ev + ev.header.size);
ret = perf_event__synthesize_id_sample(array, sample_type, sample);
if (ret < 0)
return ret;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
2026-07-20 22:51 ` [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
@ 2026-07-20 23:03 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-07-20 23:03 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] `io__drain_line()` fails to drain the remainder of the line when `ch == -2` is passed, causing parsing synchronization failure.
--
--- Patch [2]: [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader ---
commit f1400c3fd8b406d3bc0aa09991216359f2f3eb18
Author: Ian Rogers <irogers@google.com>
Subject: perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
This commit addresses logic and boundary issues in the proc maps reader. It
adds explicit line drainage to handle mid-line parsing failures and introduces
timeout handling for truncated map entries.
> diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
> index 5307d707711d8..059dd558a564c 100644
> --- a/tools/perf/util/synthetic-events.c
> +++ b/tools/perf/util/synthetic-events.c
[ ... ]
> @@ -291,6 +291,17 @@ static int perf_event__synthesize_fork(const struct perf_tool *tool,
[ ... ]
> +static void io__drain_line(struct io *io, int ch)
> +{
> + if (ch == '\n')
> + return;
> + if (ch == -2 && io->data > io->buf && io->data[-1] == '\n')
> + return;
> +
> + while (ch >= 0 && ch != '\n')
> + ch = io__get_char(io);
[Severity: Medium]
Will this loop properly drain the line if ch is initially passed as -2?
When read_proc_maps_line() encounters a malformed line where io__get_hex()
or io__get_dec() fails immediately, they return the error code -2. This
value is passed into io__drain_line() as the ch argument.
Because the loop condition requires ch >= 0, passing -2 causes the loop to
be skipped entirely without reading any characters from io__get_char(io).
This appears to leave the stream positioned mid-line, potentially causing
the parser to repeatedly fail character-by-character on the exact same
line, defeating the line synchronization mechanism.
> +}
> +
> static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720225200.3810501-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-07-20 23:03 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20 17:54 [PATCH v1 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 17:54 ` [PATCH v1 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
2026-07-20 18:05 ` sashiko-bot
2026-07-20 17:54 ` [PATCH v1 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
2026-07-20 18:08 ` sashiko-bot
2026-07-20 17:54 ` [PATCH v1 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
2026-07-20 17:54 ` [PATCH v1 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
2026-07-20 18:28 ` sashiko-bot
2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-20 22:51 ` [PATCH v2 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
2026-07-20 22:51 ` [PATCH v2 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
2026-07-20 23:03 ` sashiko-bot
2026-07-20 22:51 ` [PATCH v2 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
2026-07-20 22:52 ` [PATCH v2 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox