[PATCH 03/15] perf symbols: Use fixed buffer in sysfs__read_build_id() for no-libelf build
From: Arnaldo Carvalho de Melo
Date: Thu Jun 11 2026 - 20:36:25 EST
From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
sysfs__read_build_id() in the no-libelf build path uses fstat() to get
the file size and then allocates an exact-sized buffer. Sysfs pseudo-
files (/sys/kernel/notes) often report st_size=0 or a page-sized value
(4096) regardless of actual content. When st_size is 0, malloc(0)
returns a zero-sized allocation and read(fd, buf, 0) returns 0, which
matches the expected (ssize_t)buf_size check, then read_build_id() is
called with a zero-length buffer and never finds any notes.
Replace the fstat + malloc approach with a fixed BUFSIZ stack buffer and
a single read(), matching the strategy used by the libelf-based
sysfs__read_build_id() in symbol-elf.c. This also eliminates the
malloc/free overhead for what is always a small sysfs file.
Reported-by: sashiko-bot <sashiko-bot@xxxxxxxxxx>
Fixes: b691f64360ecec49 ("perf symbols: Implement poor man's ELF parser")
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Assisted-by: Claude Opus 4.6 <noreply@xxxxxxxxxxxxx>
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/util/symbol-minimal.c | 41 +++++++++++++++++++-------------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index 0a71d146395271a6..ea2de3d50d33cf33 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -220,29 +220,36 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid)
{
int fd;
int ret = -1;
- struct stat stbuf;
- size_t buf_size;
- void *buf;
+ /*
+ * read_build_id() casts this buffer to a u32 note header pointer,
+ * so it must be aligned for u32 access.
+ *
+ * Accessing a char[] through a u32* is technically type-punning
+ * under C strict aliasing rules, but perf unconditionally builds
+ * with -fno-strict-aliasing (Makefile.config), so this is safe.
+ *
+ * This file is only compiled when libelf is not available — the
+ * common case uses the libelf-based path in symbol-elf.c instead.
+ */
+ char buf[BUFSIZ] __aligned(4);
+ ssize_t len;
fd = open(filename, O_RDONLY);
if (fd < 0)
return -1;
- if (fstat(fd, &stbuf) < 0)
- goto out;
-
- buf_size = stbuf.st_size;
- buf = malloc(buf_size);
- if (buf == NULL)
- goto out;
-
- if (read(fd, buf, buf_size) != (ssize_t) buf_size)
- goto out_free;
+ /*
+ * Use a fixed buffer and a single read() instead of fstat() + malloc(),
+ * because sysfs pseudo-files often report st_size=0 or 4096
+ * regardless of actual content size.
+ *
+ * BUFSIZ (8192) is more than sufficient: a sysfs build-id note is
+ * ~36 bytes (12-byte note header + 4-byte "GNU" name + 20-byte SHA1).
+ */
+ len = read(fd, buf, sizeof(buf));
+ if (len > 0)
+ ret = read_build_id(buf, len, bid, false);
- ret = read_build_id(buf, buf_size, bid, false);
-out_free:
- free(buf);
-out:
close(fd);
return ret;
}
--
2.54.0