mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
perf synthetic-events: Fix line synchronization, bounds, and truncation bugs in proc maps reader
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> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
parent
e46a9b8150
commit
505a498a37
|
|
@ -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;
|
||||
}
|
||||
|
||||
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 ((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");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user