The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [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; 37+ 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] 37+ 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 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, 0 replies; 37+ 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] 37+ 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 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, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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 22:51 ` [PATCH v2 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  4 siblings, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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 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, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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: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, 0 replies; 37+ 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] 37+ 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: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, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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 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, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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 20:57       ` [PATCH v5 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  4 siblings, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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 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, 0 replies; 37+ 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] 37+ 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; 37+ 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] 37+ 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; 37+ 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] 37+ 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
                             ` (4 more replies)
  4 siblings, 5 replies; 37+ 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] 37+ 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
                             ` (3 subsequent siblings)
  4 siblings, 0 replies; 37+ 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] 37+ 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:09           ` [PATCH v6 3/4] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
                             ` (2 subsequent siblings)
  4 siblings, 0 replies; 37+ 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] 37+ 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
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  4 siblings, 0 replies; 37+ 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] 37+ 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
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  4 siblings, 0 replies; 37+ 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] 37+ messages in thread

* [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety
  2026-07-21 23:09         ` [PATCH v6 0/4] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
                             ` (3 preceding siblings ...)
  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
@ 2026-07-21 23:52           ` Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 1/5] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
                               ` (5 more replies)
  4 siblings, 6 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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.

V7 addresses security review feedback:
- Adds a standalone patch (Patch 3/5) to fix a pre-existing stack
  buffer overflow in perf_event__synthesize_cgroup(). Eliminates
  in-place null padding mutation of the path stack buffer, calculates
  aligned path_len using PERF_ALIGN, clamps raw_path_len to account
  for machine->id_hdr_size, and uses strlcpy with combined memset for
  alignment and sample ID header padding.

V6 addresses review feedback:
- Ensures Patch 2 leaves module synthesis callbacks completely
  untouched, preserving the pre-existing build ID clearing logic in
  baseline.
- Patch 4 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 (5):
  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 stack buffer overflow and bounds in cgroup
    synthesis
  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 | 309 ++++++++++++++++++++---------
 2 files changed, 223 insertions(+), 96 deletions(-)

-- 
2.55.0.229.g6434b31f56-goog


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v7 1/5] perf find-map: Remove PATH_MAX 128-byte stack array restriction
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
@ 2026-07-21 23:52             ` Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 2/5] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
                               ` (4 subsequent siblings)
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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] 37+ messages in thread

* [PATCH v7 2/5] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 1/5] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
@ 2026-07-21 23:52             ` Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 3/5] perf synthetic-events: Fix stack buffer overflow and bounds in cgroup synthesis Ian Rogers
                               ` (3 subsequent siblings)
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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] 37+ messages in thread

* [PATCH v7 3/5] perf synthetic-events: Fix stack buffer overflow and bounds in cgroup synthesis
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 1/5] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 2/5] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
@ 2026-07-21 23:52             ` Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 4/5] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
                               ` (2 subsequent siblings)
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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 pre-existing stack buffer overflow bug in
perf_event__synthesize_cgroup() where an in-place null padding loop wrote
bytes past the end of the cgrp_root stack array buffer during cgroup tree
traversal. Eliminate in-place path mutation, use PERF_ALIGN for path_len,
clamp raw_path_len to prevent sample ID header trailer overruns, and use
strlcpy with combined zero padding for alignment and sample ID headers.

Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/synthetic-events.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 832b74ffb4db..05075840707c 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -635,15 +635,22 @@ static int perf_event__synthesize_cgroup(const struct perf_tool *tool,
 					 struct machine *machine)
 {
 	size_t event_size = sizeof(event->cgroup) - sizeof(event->cgroup.path);
-	size_t path_len = strlen(path) - mount_len + 1;
+	size_t raw_path_len, path_len, max_path_len;
 	struct {
 		struct file_handle fh;
 		uint64_t cgroup_id;
 	} handle;
 	int mount_id;
 
-	while (path_len % sizeof(u64))
-		path[mount_len + path_len++] = '\0';
+	if (strlen(path) < mount_len)
+		return -1;
+
+	max_path_len = sizeof(event->cgroup.path) - machine->id_hdr_size;
+	raw_path_len = strlen(path) - mount_len + 1;
+	if (raw_path_len > max_path_len)
+		raw_path_len = max_path_len;
+
+	path_len = PERF_ALIGN(raw_path_len, sizeof(u64));
 
 	memset(&event->cgroup, 0, event_size);
 
@@ -657,9 +664,9 @@ 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((char *)event + offsetof(struct perf_record_cgroup, path) + path_len,
-	       0, machine->id_hdr_size);
+	strlcpy(event->cgroup.path, path + mount_len, raw_path_len);
+	memset((char *)event + offsetof(struct perf_record_cgroup, path) + raw_path_len,
+	       0, (path_len - raw_path_len) + 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] 37+ messages in thread

* [PATCH v7 4/5] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
                               ` (2 preceding siblings ...)
  2026-07-21 23:52             ` [PATCH v7 3/5] perf synthetic-events: Fix stack buffer overflow and bounds in cgroup synthesis Ian Rogers
@ 2026-07-21 23:52             ` Ian Rogers
  2026-07-21 23:52             ` [PATCH v7 5/5] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
  2026-07-22  0:13             ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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 05075840707c..5bbf024ad606 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -764,6 +764,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)
@@ -771,49 +772,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,
@@ -838,13 +868,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] 37+ messages in thread

* [PATCH v7 5/5] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
                               ` (3 preceding siblings ...)
  2026-07-21 23:52             ` [PATCH v7 4/5] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
@ 2026-07-21 23:52             ` Ian Rogers
  2026-07-22  0:13             ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-21 23:52 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 5bbf024ad606..75a32ae8ef62 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -2457,13 +2457,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))
@@ -2483,16 +2488,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] 37+ messages in thread

* Re: [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety
  2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
                               ` (4 preceding siblings ...)
  2026-07-21 23:52             ` [PATCH v7 5/5] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
@ 2026-07-22  0:13             ` Ian Rogers
  5 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-07-22  0:13 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users,
	mingo, peterz, ravi.bangoria, swapnil.sapkal

On Tue, Jul 21, 2026 at 4:52 PM Ian Rogers <irogers@google.com> wrote:
>
> 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.
>
> V7 addresses security review feedback:
> - Adds a standalone patch (Patch 3/5) to fix a pre-existing stack
>   buffer overflow in perf_event__synthesize_cgroup(). Eliminates
>   in-place null padding mutation of the path stack buffer, calculates
>   aligned path_len using PERF_ALIGN, clamps raw_path_len to account
>   for machine->id_hdr_size, and uses strlcpy with combined memset for
>   alignment and sample ID header padding.
>
> V6 addresses review feedback:
> - Ensures Patch 2 leaves module synthesis callbacks completely
>   untouched, preserving the pre-existing build ID clearing logic in
>   baseline.
> - Patch 4 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 (5):
>   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 stack buffer overflow and bounds in cgroup
>     synthesis
>   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

So while Sashiko is reporting yet more pre-existing issues, I'm going
to stop here. The changes in these patches have no issues:
https://sashiko.dev/#/patchset/20260721235254.294053-1-irogers%40google.com

Thanks,
Ian

>  tools/perf/util/find-map.c         |  10 +-
>  tools/perf/util/synthetic-events.c | 309 ++++++++++++++++++++---------
>  2 files changed, 223 insertions(+), 96 deletions(-)
>
> --
> 2.55.0.229.g6434b31f56-goog
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2026-07-22  0:14 UTC | newest]

Thread overview: 37+ 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 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 ` [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 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   ` [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: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     ` [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 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 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         ` [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: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
2026-07-21 23:52           ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers
2026-07-21 23:52             ` [PATCH v7 1/5] perf find-map: Remove PATH_MAX 128-byte stack array restriction Ian Rogers
2026-07-21 23:52             ` [PATCH v7 2/5] perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader Ian Rogers
2026-07-21 23:52             ` [PATCH v7 3/5] perf synthetic-events: Fix stack buffer overflow and bounds in cgroup synthesis Ian Rogers
2026-07-21 23:52             ` [PATCH v7 4/5] perf synthetic-events: Fix bounds, stale state, and misc flags in kernel module synthesis Ian Rogers
2026-07-21 23:52             ` [PATCH v7 5/5] perf synthetic-events: Fix bounds and union member access in mmap2 build_id synthesis Ian Rogers
2026-07-22  0:13             ` [PATCH v7 0/5] perf: Fix and optimize maps parsing, boundaries, and bounds safety Ian Rogers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox