* [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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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; 40+ 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] 40+ 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 ` (4 more replies) 4 siblings, 5 replies; 40+ 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] 40+ 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 ` (3 subsequent siblings) 4 siblings, 0 replies; 40+ 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] 40+ 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 ` (2 subsequent siblings) 4 siblings, 1 reply; 40+ 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] 40+ 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; 40+ 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] 40+ 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 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ 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] 40+ 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 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ 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] 40+ messages in thread
* [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety 2026-07-20 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (3 preceding siblings ...) 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 @ 2026-07-21 17:33 ` Ian Rogers 2026-07-21 17:33 ` [PATCH v3 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers ` (4 more replies) 4 siblings, 5 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 17:33 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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. V3 addresses Sashiko AI review feedback: - Updates io__drain_line() loop condition from a while loop to a do-while loop to ensure that when ch == -2 (initial character non-hex/dec failure), the loop correctly reads and discards characters from io__get_char(io) until a newline or EOF is reached, preserving map line synchronization. V2 addresses community 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 | 288 ++++++++++++++++++++--------- 2 files changed, 208 insertions(+), 90 deletions(-) -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v3 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers @ 2026-07-21 17:33 ` Ian Rogers 2026-07-21 17:33 ` [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers ` (3 subsequent siblings) 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 17:33 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 17:33 ` [PATCH v3 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers @ 2026-07-21 17:33 ` Ian Rogers 2026-07-21 17:48 ` sashiko-bot 2026-07-21 17:33 ` [PATCH v3 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; 40+ messages in thread From: Ian Rogers @ 2026-07-21 17:33 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* Re: [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 17:33 ` [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 17:48 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 17:48 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 line when a parsing error occurs, breaking map parsing synchronization and causing inefficient cascading failures. -- --- Patch [2]: [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader --- commit ffbc393b4ac8d55ab5ee2db18d5f9a92e83c09d1 Author: Ian Rogers <irogers@google.com> perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader This commit fixes logic and boundary bugs in read_proc_maps_line() and its callers. It adds io__drain_line() to safely ignore already-consumed newlines and preserve synchronization for subsequent 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, > 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); [Severity: Medium] Will this loop correctly drain the line when a parsing error occurs? When io__get_hex() or io__get_dec() encounters an invalid first character, they consume it and return -2. read_proc_maps_line() then calls io__drain_line(io, -2). Because the loop condition strictly requires ch >= 0, it will skip entirely when passed the -2 error code, returning without consuming any characters. Instead of draining the rest of the line, the parser will re-evaluate every remaining character as the potential start of a new map entry, leading to parsing desynchronization and a tight loop of failures that could trigger the timeout watchdog. > +} > + > 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) != ' ') > + } -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721173347.9163-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 17:33 ` [PATCH v3 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 17:33 ` [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 17:33 ` Ian Rogers 2026-07-21 17:47 ` sashiko-bot 2026-07-21 17:33 ` [PATCH v3 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 1 reply; 40+ messages in thread From: Ian Rogers @ 2026-07-21 17:33 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* Re: [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis 2026-07-21 17:33 ` [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers @ 2026-07-21 17:47 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 17:47 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: Pre-existing issues: - [High] The memset used to clear the filename padding and ID header trailer will trigger a _FORTIFY_SOURCE runtime abort when a DSO name length approaches PATH_MAX. -- --- Patch [3]: [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis --- commit e862d233381463bc8c1e84b7d335152fe0a7c118 Author: Ian Rogers <irogers@google.com> perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis This commit clamps long DSO names to mmap/mmap2 filename boundaries to prevent buffer overruns in perf_event__synthesize_modules_maps_cb(). It also explicitly clears misc flags and union padding to prevent stale Build-ID state from leaking between module synthesis events. > diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c > --- a/tools/perf/util/synthetic-events.c > +++ b/tools/perf/util/synthetic-events.c [ ... ] > @@ -761,49 +762,78 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) > if (!symbol_conf.no_buildid_mmap2) { > const char *long_name = dso__long_name(dso); > > - size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); > + 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); [Severity: High] This is a pre-existing issue, but can this memset trigger a _FORTIFY_SOURCE runtime abort when a DSO name length approaches PATH_MAX? The event->mmap2.filename field is declared as a fixed-size array of PATH_MAX (4096 bytes). If a kernel module path is very long, size is clamped to 4095 and aligned_size evaluates to 4096. The memset then attempts to write (1 + args->machine->id_hdr_size) bytes starting at event->mmap2.filename + 4095. Because the memset is anchored to the filename member array, _FORTIFY_SOURCE dynamically tracks that there is only 1 byte remaining in that specific member. Since 1 + id_hdr_size > 1, this exceeds the compiler-enforced array bound, triggering __chk_fail() and terminating perf, even though the underlying allocation is large enough. > + > event->mmap2.header.size += args->machine->id_hdr_size; > event->mmap2.start = map__start(map); [ ... ] > 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); [Severity: High] This is a pre-existing issue, but this memset is subject to the same _FORTIFY_SOURCE abort as the mmap2 case above when the DSO name approaches PATH_MAX and sample IDs are enabled. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721173347.9163-1-irogers@google.com?part=3 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v3 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (2 preceding siblings ...) 2026-07-21 17:33 ` [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers @ 2026-07-21 17:33 ` Ian Rogers 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 17:33 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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 | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 068323b9510d..6477a726c8ba 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -298,8 +298,9 @@ static void io__drain_line(struct io *io, int ch) if (ch == -2 && io->data > io->buf && io->data[-1] == '\n') return; - while (ch >= 0 && ch != '\n') + do { ch = io__get_char(io); + } while (ch >= 0 && ch != '\n'); } static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end, @@ -2447,13 +2448,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 +2479,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] 40+ messages in thread
* [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (3 preceding siblings ...) 2026-07-21 17:33 ` [PATCH v3 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers @ 2026-07-21 18:21 ` Ian Rogers 2026-07-21 18:21 ` [PATCH v4 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers ` (4 more replies) 4 siblings, 5 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 18:21 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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 - note, the filename buffer is bounds checked but this makes the synthesis more similar to the kernel approach. With Gemini's help try to address other correctness and overrun issues. V4 addresses review feedback: - Ensures io__drain_line()'s do-while loop is properly included and committed directly within Patch 2. When ch == -2 is passed on parsing errors, the loop correctly reads and discards remaining line characters until a newline or EOF is reached, preventing tight failure loops and preserving line synchronization. - Replaces member array memset destination pointers in Patches 2 & 3 with offsetof() casts on (char *)event to avoid _FORTIFY_SOURCE runtime array bound aborts when zeroing padding trailers for long pathnames. V3 addresses review feedback: - Updates io__drain_line() loop condition from a while loop to a do-while loop to ensure initial non-hex/dec failures correctly drain line remnants. V2 addresses community 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 | 288 ++++++++++++++++++++--------- 2 files changed, 208 insertions(+), 90 deletions(-) -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v4 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers @ 2026-07-21 18:21 ` Ian Rogers 2026-07-21 18:21 ` [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers ` (3 subsequent siblings) 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 18:21 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 18:21 ` [PATCH v4 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers @ 2026-07-21 18:21 ` Ian Rogers 2026-07-21 19:26 ` sashiko-bot 2026-07-21 18:21 ` [PATCH v4 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; 40+ messages in thread From: Ian Rogers @ 2026-07-21 18:21 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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, using a do-while loop to read and discard remaining characters until a newline or EOF is reached. Use standard '//toolong' fallback literal for over-length pathnames, emit timeout flags for truncated entries securely via goto out;, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts when zeroing trailers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/synthetic-events.c | 203 ++++++++++++++++++++--------- 1 file changed, 138 insertions(+), 65 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index b75f9dcf4dbf..60bdf2d918ea 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -291,6 +291,18 @@ 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; + + 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 +311,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 +533,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 +589,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((char *)event + offsetof(struct perf_record_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); @@ -703,11 +781,6 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1); - /* Clear stale build ID 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; - perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false); } else { size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 18:21 ` [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 19:26 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 19:26 UTC (permalink / raw) To: Ian Rogers; +Cc: linux-perf-users Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: New issues: - [Critical] Bypassing _FORTIFY_SOURCE array bounds checks introduces an exploitable stack buffer overflow when zeroing event trailers. - [High] Removal of the stale build ID clearing leaks state and corrupts subsequent kernel module events. Pre-existing issues: - [High] Incomplete fix: _FORTIFY_SOURCE array bounds aborts still exist in multiple other synthesis functions. -- --- Patch [2]: [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader --- commit ba0b89af1798a9c26d45cb174748a43558ce4d9d Author: Ian Rogers <irogers@google.com> perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader This commit fixes bugs in read_proc_maps_line() and its callers by ensuring proper line draining on parsing failures. It also adds standard fallbacks for over-length paths, safely emits timeout flags, and introduces pointer casts to prevent _FORTIFY_SOURCE array bounds aborts when zeroing trailers. > diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c > index 5307d707711d8..e0680d770462c 100644 > --- a/tools/perf/util/synthetic-events.c > +++ b/tools/perf/util/synthetic-events.c [ ... ] > @@ -463,45 +533,53 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool, [ ... ] > - memset(event->mmap2.filename + size, 0, machine->id_hdr_size + > - (aligned_size - size)); > - event->mmap2.header.size += machine->id_hdr_size; > + /* Zero the padding and ID header trailer safely! */ > + memset((char *)event + offsetof(struct perf_record_mmap2, filename) + size, 0, > + (aligned_size - size) + machine->id_hdr_size); > + > + event->mmap2.header.size += machine->id_hdr_size; [Severity: Critical] Can bypassing the _FORTIFY_SOURCE array bounds checks introduce a stack buffer overflow regression here? When this function is called via machine__init_live(), the event union is allocated on the stack without the extra padding for id_hdr_size: tools/perf/util/machine.c:machine__init_live() { union perf_event event; memset(&event, 0, sizeof(event)); ... perf_event__synthesize_mmap_events(tool, &event, ...); } If an attacker provides a pathname approaching PATH_MAX in /proc/pid/maps, size pushes the memset destination to the end of the union perf_event structure. By bypassing the fortify check, this memset blindly writes machine->id_hdr_size bytes of zeroes completely outside the bounds of the stack variable, silently corrupting the stack frame instead of safely aborting. [Severity: High] This is a pre-existing issue, but does the same _FORTIFY_SOURCE array bounds abort regression still exist in other synthesis functions? While this patch applies a cast bypass here in perf_event__synthesize_mmap_events(), similar bounds-violating memset calls remain in other paths. For example, in perf_event__prepare_comm(): tools/perf/util/synthetic-events.c:perf_event__prepare_comm() { ... memset(event->comm.comm + size, 0, machine->id_hdr_size); ... } Since event->comm.comm is a static 16-byte array, if a process name is 15 characters long, the memset begins exactly at the end of the array and writes id_hdr_size bytes. Under modern compiler flags (_FORTIFY_SOURCE=2 or 3), this reliably triggers an abort because the compiler detects the memset writing past the bounds of the array. The same pattern is also present in perf_event__synthesize_cgroups(). [ ... ] > @@ -703,11 +781,6 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) > > memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1); > > - /* Clear stale build ID 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; > - > perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false); > } else { > size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); [Severity: High] Could removing this explicit clearing of the shared union's build ID leak state between loop iterations? The perf_event__synthesize_modules() function allocates a single union perf_event structure outside the loop and reuses it for every kernel module via maps__for_each_map(). By removing the explicit clearing of PERF_RECORD_MISC_MMAP_BUILD_ID, if one module successfully reads a build ID, the flag remains set for all subsequent modules. When processing subsequent modules, perf_record_mmap2__read_build_id() sees the flag is already set, assumes the event already contains a valid build ID, and reads the stale build ID from the previous iteration. This could attribute samples to incorrect binaries. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721182150.94016-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v4 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 18:21 ` [PATCH v4 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 18:21 ` [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 18:21 ` Ian Rogers 2026-07-21 18:21 ` [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 18:21 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal Clamp long DSO names to mmap/mmap2 filename boundaries to prevent buffer overruns in perf_event__synthesize_modules_maps_cb(). Explicitly clear misc flags and union padding to prevent stale Build-ID state from leaking between module synthesis events, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts when zeroing padding trailers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/synthetic-events.c | 71 ++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 60bdf2d918ea..e547874b77b2 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -755,6 +755,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) @@ -762,44 +763,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((char *)event + offsetof(struct perf_record_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 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((char *)event + offsetof(struct perf_record_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, @@ -824,13 +859,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] 40+ messages in thread
* [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (2 preceding siblings ...) 2026-07-21 18:21 ` [PATCH v4 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers @ 2026-07-21 18:21 ` Ian Rogers 2026-07-21 19:25 ` sashiko-bot 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 1 reply; 40+ messages in thread From: Ian Rogers @ 2026-07-21 18:21 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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 e547874b77b2..06a960e8ba85 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2448,13 +2448,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)) @@ -2474,16 +2479,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] 40+ messages in thread
* Re: [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis 2026-07-21 18:21 ` [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers @ 2026-07-21 19:25 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 19:25 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] Latent buffer overflow and potential FORTIFY_SOURCE abort due to incorrect string length bounds check against union capacity instead of array size. -- --- Patch [4]: [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis --- commit a02cc1d93b140eea5444d6b7ee95e98c02c00905 Author: Ian Rogers <irogers@google.com> perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis This commit modifies bounds and union member access in mmap2 build_id synthesis. It replaces the previous array-based bounds check with a new calculation based on the size of the event union. > diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c > index 63c9c85b02f37..8928c6964fc9d 100644 > --- a/tools/perf/util/synthetic-events.c > +++ b/tools/perf/util/synthetic-events.c [ ... ] > @@ -2448,13 +2448,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; [Severity: Medium] Will this calculation cause an issue if the size of the union perf_event ever grows larger than struct perf_record_mmap2? Since max_filename_len is derived from sizeof(ev) rather than the capacity of the ev.mmap2.filename array, any future expansion of the union could cause max_filename_len to exceed the actual array bounds. > > - 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)); [ ... ] > @@ -2474,16 +2479,15 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, [ ... ] > - memcpy(ev.mmap2.filename, filename, min(strlen(filename), sizeof(ev.mmap.filename))); > + strlcpy(ev.mmap2.filename, filename, filename_len + 1); [Severity: Medium] If filename_len is bounded by sizeof(ev) and the union expands, could this strlcpy write past the ev.mmap2.filename array capacity and trigger a _FORTIFY_SOURCE abort? When built with _FORTIFY_SOURCE=2, bounds checks evaluate against the sub-object array capacity (ev.mmap2.filename) rather than the enclosing struct. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721182150.94016-1-irogers@google.com?part=4 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (3 preceding siblings ...) 2026-07-21 18:21 ` [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers @ 2026-07-21 20:57 ` Ian Rogers 2026-07-21 20:57 ` [PATCH v5 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers ` (4 more replies) 4 siblings, 5 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 20:57 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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 - note, the filename buffer is bounds checked but this makes the synthesis more similar to the kernel approach. With Gemini's help try to address other correctness and overrun issues. V5 addresses technical review feedback: - Clamps pathname buffer sizes in read_proc_maps_line() and module synthesis callbacks by subtracting machine->id_hdr_size. This guarantees that when sample ID headers are appended to events (such as stack-allocated events in machine__init_live()), the event size never exceeds sizeof(union perf_event), eliminating stack buffer overflow risks. - Casts member array memset destination pointers to (char *)event + offsetof(...) across all synthesis handlers (prepare_comm, cgroups, mmap, modules) to prevent _FORTIFY_SOURCE array bounds aborts when zeroing padding trailers. - Confirms explicit clearing of PERF_RECORD_MISC_MMAP_BUILD_ID, build_id, and reserved union members in perf_event__synthesize_modules_maps_cb() to prevent stale Build-ID state leakage between module iterations. - Restricts max_filename_len in perf_event__synthesize_mmap2_build_id() to the minimum of filename array capacity and union payload space, preventing strlcpy fortify aborts even if union perf_event expands in the future. V4 addresses review feedback: - Ensures io__drain_line()'s do-while loop is committed directly in Patch 2. V3 addresses review feedback: - Updates io__drain_line() loop condition from a while loop to a do-while loop. V2 addresses community review feedback: - Corrects read_proc_maps_line() and io__drain_line() to safely handle already consumed newlines. - Restores early exit block for timeouts so TIMEOUT flag is emitted to tools. - Removes unused assignment to avoid promoting warnings to build errors. 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 | 294 ++++++++++++++++++++--------- 2 files changed, 212 insertions(+), 92 deletions(-) -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v5 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers @ 2026-07-21 20:57 ` Ian Rogers 2026-07-21 20:57 ` [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers ` (3 subsequent siblings) 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 20:57 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 20:57 ` [PATCH v5 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers @ 2026-07-21 20:57 ` Ian Rogers 2026-07-21 21:15 ` sashiko-bot 2026-07-21 20:57 ` [PATCH v5 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; 40+ messages in thread From: Ian Rogers @ 2026-07-21 20:57 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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, using a do-while loop to read and discard remaining characters until a newline or EOF is reached. Clamp pathname extraction size to account for trailing sample ID headers, use standard '//toolong' fallback literal for over-length pathnames, emit timeout flags for truncated entries securely via goto out;, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts across synthesis handlers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/synthetic-events.c | 209 ++++++++++++++++++++--------- 1 file changed, 142 insertions(+), 67 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index b75f9dcf4dbf..e73f2e526e83 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -179,7 +179,8 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid, pid_t ti size = strlen(event->comm.comm) + 1; size = PERF_ALIGN(size, sizeof(u64)); - memset(event->comm.comm + size, 0, machine->id_hdr_size); + memset((char *)event + offsetof(struct perf_record_comm, comm) + size, + 0, machine->id_hdr_size); event->comm.header.size = (sizeof(event->comm) - (sizeof(event->comm.comm) - size) + machine->id_hdr_size); @@ -291,6 +292,18 @@ 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; + + 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 +312,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 +534,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, clamping for id_hdr_size! */ + if (!read_proc_maps_line(&io, &start, &end, + &prot, &flags, &pgoff, + &maj, &min, &ino, + sizeof(event->mmap2.filename) - machine->id_hdr_size, + 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 +590,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((char *)event + offsetof(struct perf_record_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); @@ -579,7 +658,8 @@ static int perf_event__synthesize_cgroup(const struct perf_tool *tool, event->cgroup.id = handle.cgroup_id; strncpy(event->cgroup.path, path + mount_len, path_len); - memset(event->cgroup.path + path_len, 0, machine->id_hdr_size); + memset((char *)event + offsetof(struct perf_record_cgroup, path) + path_len, + 0, machine->id_hdr_size); if (perf_tool__process_synth_event(tool, event, machine, process) < 0) { pr_debug("process synth event failed\n"); @@ -703,11 +783,6 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1); - /* Clear stale build ID 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; - perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false); } else { size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 20:57 ` [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 21:15 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 21:15 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] Stale build ID leakage across kernel module synthesis due to removed loop cleanup. -- --- Patch [2]: [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader --- commit 7ed1842aa319aa810201b4aedeba9993fa02dfd6 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 parsing failures invoke safe drain paths, clamps pathname extraction sizes, and correctly handles timeout flags. > diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c > index 5307d707711d8..e379174ae002d 100644 > --- a/tools/perf/util/synthetic-events.c > +++ b/tools/perf/util/synthetic-events.c [ ... ] > @@ -703,11 +783,6 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) > > memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1); > > - /* Clear stale build ID 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; > - [Severity: Medium] Does this removal cause stale build IDs to leak across module syntheses? Since the event structure is allocated once and reused across all kernel modules in the loop, if perf_record_mmap2__read_build_id() fails to read a build ID for a subsequent module, it seems the event will retain the PERF_RECORD_MISC_MMAP_BUILD_ID flag and the build ID from the last successfully processed module. Will this cause perf to misattribute samples from the failing module to the debug symbols of a completely different module? > perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false); > } else { > size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721205746.183206-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v5 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 20:57 ` [PATCH v5 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 20:57 ` [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 20:57 ` Ian Rogers 2026-07-21 20:57 ` [PATCH v5 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 20:57 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal Clamp long DSO names to mmap/mmap2 filename boundaries accounting for sample ID headers to prevent buffer overruns in perf_event__synthesize_modules_maps_cb(). Explicitly clear misc flags and union padding to prevent stale Build-ID state from leaking between module synthesis events, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts when zeroing padding trailers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/synthetic-events.c | 71 ++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index e73f2e526e83..4043d17a6140 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -757,6 +757,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) @@ -764,44 +765,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) - args->machine->id_hdr_size) + size = sizeof(event->mmap2.filename) - args->machine->id_hdr_size - 1; + + strlcpy(event->mmap2.filename, long_name, + sizeof(event->mmap2.filename) - args->machine->id_hdr_size); + + 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((char *)event + offsetof(struct perf_record_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 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) - args->machine->id_hdr_size) + size = sizeof(event->mmap.filename) - args->machine->id_hdr_size - 1; + + strlcpy(event->mmap.filename, long_name, + sizeof(event->mmap.filename) - args->machine->id_hdr_size); + + 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((char *)event + offsetof(struct perf_record_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, @@ -826,13 +861,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] 40+ messages in thread
* [PATCH v5 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (2 preceding siblings ...) 2026-07-21 20:57 ` [PATCH v5 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers @ 2026-07-21 20:57 ` Ian Rogers 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 4 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 20:57 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal Modify bounds and union member access in mmap2 build_id synthesis. Bound max_filename_len against the minimum of filename array capacity and the outer union stack layout minus sample ID trailers. This prevents both -E2BIG overruns and _FORTIFY_SOURCE array bounds aborts on strlcpy even if the enclosing union expands. 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 4043d17a6140..704760e1dd5d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2450,13 +2450,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 = min(sizeof(ev.mmap2.filename) - 1, + 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)) @@ -2476,16 +2481,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] 40+ messages in thread
* [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (3 preceding siblings ...) 2026-07-21 20:57 ` [PATCH v5 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers @ 2026-07-21 23:09 ` Ian Rogers 2026-07-21 23:09 ` [PATCH v6 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers ` (3 more replies) 4 siblings, 4 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 23:09 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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 - note, the filename buffer is bounds checked but this makes the synthesis more similar to the kernel approach. With Gemini's help try to address other correctness and overrun issues. V6 addresses review feedback: - Ensures Patch 2 leaves perf_event__synthesize_modules_maps_cb() completely untouched, preserving the pre-existing build ID clearing logic in baseline. - Patch 3 explicitly clears PERF_RECORD_MISC_MMAP_BUILD_ID, build_id, and reserved union padding members in module synthesis callbacks to guarantee no stale Build-ID state leaks between module synthesis iterations. V5 addresses technical review feedback: - Clamps pathname buffer sizes in read_proc_maps_line() and module synthesis callbacks by subtracting machine->id_hdr_size. - Casts member array memset destination pointers to (char *)event + offsetof(...) across all synthesis handlers. - Restricts max_filename_len in perf_event__synthesize_mmap2_build_id() to the minimum of filename array capacity and union payload space. V4 addresses review feedback: - Ensures io__drain_line()'s do-while loop is committed directly in Patch 2. V3 addresses review feedback: - Updates io__drain_line() loop condition from a while loop to a do-while loop. V2 addresses community review feedback: - Corrects read_proc_maps_line() and io__drain_line() to safely handle already consumed newlines. - Restores early exit block for timeouts so TIMEOUT flag is emitted to tools. - Removes unused assignment to avoid promoting warnings to build errors. 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 | 294 ++++++++++++++++++++--------- 2 files changed, 212 insertions(+), 92 deletions(-) -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v6 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers @ 2026-07-21 23:09 ` Ian Rogers 2026-07-21 23:09 ` [PATCH v6 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; 40+ messages in thread From: Ian Rogers @ 2026-07-21 23:09 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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] 40+ messages in thread
* [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 23:09 ` [PATCH v6 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers @ 2026-07-21 23:09 ` Ian Rogers 2026-07-21 23:24 ` sashiko-bot 2026-07-21 23:09 ` [PATCH v6 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers 2026-07-21 23:09 ` [PATCH v6 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 3 siblings, 1 reply; 40+ messages in thread From: Ian Rogers @ 2026-07-21 23:09 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal 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, using a do-while loop to read and discard remaining characters until a newline or EOF is reached. Clamp pathname extraction size to account for trailing sample ID headers, use standard '//toolong' fallback literal for over-length pathnames, emit timeout flags for truncated entries securely via goto out;, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts across synthesis handlers. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/synthetic-events.c | 204 ++++++++++++++++++++--------- 1 file changed, 142 insertions(+), 62 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index b75f9dcf4dbf..832b74ffb4db 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -179,7 +179,8 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid, pid_t ti size = strlen(event->comm.comm) + 1; size = PERF_ALIGN(size, sizeof(u64)); - memset(event->comm.comm + size, 0, machine->id_hdr_size); + memset((char *)event + offsetof(struct perf_record_comm, comm) + size, + 0, machine->id_hdr_size); event->comm.header.size = (sizeof(event->comm) - (sizeof(event->comm.comm) - size) + machine->id_hdr_size); @@ -291,6 +292,18 @@ 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; + + 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 +312,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 +534,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, clamping for id_hdr_size! */ + if (!read_proc_maps_line(&io, &start, &end, + &prot, &flags, &pgoff, + &maj, &min, &ino, + sizeof(event->mmap2.filename) - machine->id_hdr_size, + 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 +590,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((char *)event + offsetof(struct perf_record_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); @@ -579,7 +658,8 @@ static int perf_event__synthesize_cgroup(const struct perf_tool *tool, event->cgroup.id = handle.cgroup_id; strncpy(event->cgroup.path, path + mount_len, path_len); - memset(event->cgroup.path + path_len, 0, machine->id_hdr_size); + memset((char *)event + offsetof(struct perf_record_cgroup, path) + path_len, + 0, machine->id_hdr_size); if (perf_tool__process_synth_event(tool, event, machine, process) < 0) { pr_debug("process synth event failed\n"); -- 2.55.0.229.g6434b31f56-goog ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader 2026-07-21 23:09 ` [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 23:24 ` sashiko-bot 0 siblings, 0 replies; 40+ messages in thread From: sashiko-bot @ 2026-07-21 23:24 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: Pre-existing issues: - [High] Stack buffer overflow in `perf_event__synthesize_cgroup` due to out-of-bounds null byte padding. -- --- Patch [2]: [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader --- commit 23e7a3b35e7f0fff60a5a4e566945538124d1c0f 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 handles parsing failures by draining lines appropriately and secures truncated entries by extracting pathnames within safe bounds. > diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c > index 5307d707711d8..e9952c27b3bc0 100644 > --- a/tools/perf/util/synthetic-events.c > +++ b/tools/perf/util/synthetic-events.c [ ... ] > @@ -579,7 +658,8 @@ static int perf_event__synthesize_cgroup(const struct perf_tool *tool, > > event->cgroup.id = handle.cgroup_id; > strncpy(event->cgroup.path, path + mount_len, path_len); > - memset(event->cgroup.path + path_len, 0, machine->id_hdr_size); > + memset((char *)event + offsetof(struct perf_record_cgroup, path) + path_len, > + 0, machine->id_hdr_size); > > if (perf_tool__process_synth_event(tool, event, machine, process) < 0) { > pr_debug("process synth event failed\n"); [Severity: High] This is a pre-existing issue, but can this code path trigger a stack buffer overflow when aligning the path string? In perf_event__synthesize_cgroups(), the path buffer is allocated on the stack: tools/perf/util/synthetic-events.c:perf_event__synthesize_cgroups() { ... char cgrp_root[PATH_MAX]; ... } This buffer is passed as path and recursively appended to in perf_event__walk_cgroup_tree(), which allows the length to reach PATH_MAX - 1: tools/perf/util/synthetic-events.c:perf_event__walk_cgroup_tree() { ... /* any sane path should be less than PATH_MAX */ if (strlen(path) + strlen(dent->d_name) + 1 >= PATH_MAX) continue; ... } When this path is then passed into perf_event__synthesize_cgroup(), the following loop aligns path_len to an 8-byte boundary: tools/perf/util/synthetic-events.c:perf_event__synthesize_cgroup() { ... while (path_len % sizeof(u64)) path[mount_len + path_len++] = '\0'; ... } If strlen(path) is exactly PATH_MAX - 1, and path_len is not a multiple of 8, will this loop write null bytes past the end of the cgrp_root stack buffer? It appears that an attacker with permissions to create nested cgroups could create a directory path of exactly the required length. When an administrator runs perf with cgroup synthesis enabled, could this overflow trigger and potentially overwrite adjacent stack variables or control data, providing a vector for local privilege escalation? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260721230952.267754-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v6 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 23:09 ` [PATCH v6 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 23:09 ` [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers @ 2026-07-21 23:09 ` Ian Rogers 2026-07-21 23:09 ` [PATCH v6 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 3 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 23:09 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal Clamp long DSO names to mmap/mmap2 filename boundaries accounting for sample ID headers to prevent buffer overruns in perf_event__synthesize_modules_maps_cb(). Explicitly clear misc flags and union padding to prevent stale Build-ID state from leaking between module synthesis events, and cast event buffer pointers to avoid _FORTIFY_SOURCE array bounds aborts when zeroing padding trailers. 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 832b74ffb4db..4043d17a6140 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -757,6 +757,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) @@ -764,49 +765,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) - args->machine->id_hdr_size) + size = sizeof(event->mmap2.filename) - args->machine->id_hdr_size - 1; + + strlcpy(event->mmap2.filename, long_name, + sizeof(event->mmap2.filename) - args->machine->id_hdr_size); + + 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((char *)event + offsetof(struct perf_record_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) - args->machine->id_hdr_size) + size = sizeof(event->mmap.filename) - args->machine->id_hdr_size - 1; + + strlcpy(event->mmap.filename, long_name, + sizeof(event->mmap.filename) - args->machine->id_hdr_size); + + 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((char *)event + offsetof(struct perf_record_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, @@ -831,13 +861,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] 40+ messages in thread
* [PATCH v6 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers ` (2 preceding siblings ...) 2026-07-21 23:09 ` [PATCH v6 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers @ 2026-07-21 23:09 ` Ian Rogers 3 siblings, 0 replies; 40+ messages in thread From: Ian Rogers @ 2026-07-21 23:09 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, ravi.bangoria, swapnil.sapkal Modify bounds and union member access in mmap2 build_id synthesis. Bound max_filename_len against the minimum of filename array capacity and the outer union stack layout minus sample ID trailers. This prevents both -E2BIG overruns and _FORTIFY_SOURCE array bounds aborts on strlcpy even if the enclosing union expands. 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 4043d17a6140..704760e1dd5d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2450,13 +2450,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 = min(sizeof(ev.mmap2.filename) - 1, + 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)) @@ -2476,16 +2481,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] 40+ messages in thread
end of thread, other threads:[~2026-07-21 23:24 UTC | newest] Thread overview: 40+ 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 2026-07-21 17:33 ` [PATCH v3 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 17:33 ` [PATCH v3 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 17:33 ` [PATCH v3 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers 2026-07-21 17:48 ` sashiko-bot 2026-07-21 17:33 ` [PATCH v3 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers 2026-07-21 17:47 ` sashiko-bot 2026-07-21 17:33 ` [PATCH v3 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 18:21 ` [PATCH v4 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 18:21 ` [PATCH v4 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 18:21 ` [PATCH v4 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers 2026-07-21 19:26 ` sashiko-bot 2026-07-21 18:21 ` [PATCH v4 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers 2026-07-21 18:21 ` [PATCH v4 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 19:25 ` sashiko-bot 2026-07-21 20:57 ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 20:57 ` [PATCH v5 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 20:57 ` [PATCH v5 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers 2026-07-21 21:15 ` sashiko-bot 2026-07-21 20:57 ` [PATCH v5 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers 2026-07-21 20:57 ` [PATCH v5 4/4] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers 2026-07-21 23:09 ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers 2026-07-21 23:09 ` [PATCH v6 1/4] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers 2026-07-21 23:09 ` [PATCH v6 2/4] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers 2026-07-21 23:24 ` sashiko-bot 2026-07-21 23:09 ` [PATCH v6 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers 2026-07-21 23:09 ` [PATCH v6 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