* [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-11 17:11 [PATCHES v2 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
@ 2026-08-11 17:11 ` Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 2/5] perf dso: Guard close() against invalid fd in dso__decompress_kmodule_path() Arnaldo Carvalho de Melo
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:11 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot
From: Arnaldo Carvalho de Melo <acme@redhat.com>
__open_dso() computes fd = -errno when dso__get_filename() returns NULL.
Some failure paths in dso__get_filename() (e.g. binary type mismatch)
return NULL without making a syscall, leaving errno at 0 from a prior
successful call. fd = -0 = 0, which is stdin — subsequent code treats
it as a valid file descriptor.
Fall back to ENOENT when errno is 0, ensuring fd is always negative on
failure.
Fixes: eba5102d2f0b ("perf tools: Add global list of opened dso objects")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/dso.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 2309196d8df3111c..fcd4b462992be6f2 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -640,10 +640,13 @@ static int __open_dso(struct dso *dso, struct machine *machine)
mutex_lock(dso__lock(dso));
name = dso__get_filename(dso, machine ? machine->root_dir : "", &decomp);
- if (name)
+ if (name) {
fd = do_open(name);
- else
+ } else {
+ if (errno == 0)
+ errno = ENOENT;
fd = -errno;
+ }
if (decomp)
unlink(name);
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 2/5] perf dso: Guard close() against invalid fd in dso__decompress_kmodule_path()
2026-08-11 17:11 [PATCHES v2 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
@ 2026-08-11 17:11 ` Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 3/5] perf dso: Use stored fd error instead of stale errno in file_read() Arnaldo Carvalho de Melo
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:11 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot
From: Arnaldo Carvalho de Melo <acme@redhat.com>
dso__decompress_kmodule_path() unconditionally calls close(fd) on the
return value of decompress_kmodule(). When decompression fails or the
DSO is not compressed, decompress_kmodule() returns -1. close(-1)
fails with EBADF and clobbers errno, which callers up the chain
(dso__get_filename → __open_dso) depend on for error propagation.
Guard the close() call with fd >= 0 so only valid file descriptors are
closed.
Fixes: 42b3fa670825 ("perf tools: Introduce dso__decompress_kmodule_{fd,path}")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/dso.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index fcd4b462992be6f2..124193453675ca18 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -395,7 +395,9 @@ int dso__decompress_kmodule_path(struct dso *dso, const char *name,
{
int fd = decompress_kmodule(dso, name, pathname, len);
- close(fd);
+ /* decompress_kmodule() returns -1 on failure, don't close(-1) */
+ if (fd >= 0)
+ close(fd);
return fd >= 0 ? 0 : -1;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/5] perf dso: Use stored fd error instead of stale errno in file_read()
2026-08-11 17:11 [PATCHES v2 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 2/5] perf dso: Guard close() against invalid fd in dso__decompress_kmodule_path() Arnaldo Carvalho de Melo
@ 2026-08-11 17:11 ` Arnaldo Carvalho de Melo
2026-08-11 17:11 ` [PATCH 4/5] perf dso: Guard against cache underflow on short reads in dso_cache__memcpy() Arnaldo Carvalho de Melo
2026-08-11 17:12 ` [PATCH 5/5] perf dso: Replace assert with runtime check in dso__read_symbol() Arnaldo Carvalho de Melo
4 siblings, 0 replies; 7+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:11 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot
From: Arnaldo Carvalho de Melo <acme@redhat.com>
file_read() uses ret = -errno when dso__data(dso)->fd is negative after
try_to_open_dso() fails. By this point errno has been through
mutex_lock(), nsinfo__mountns_enter(), and multiple open() attempts
inside try_to_open_dso() — it no longer reflects the actual open
failure. If errno happens to be 0, ret = 0 looks like EOF rather than
an error.
The fd field already carries the negated errno from __open_dso()
(e.g. -EINVAL, -ENOENT), so use it directly instead of reading the
stale global errno.
Fixes: 33bdedcea2d7 ("perf tools: Protect dso cache fd with a mutex")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/dso.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 124193453675ca18..845a384a4d56779b 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1029,7 +1029,8 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,
if (dso__data(dso)->fd < 0) {
dso__data(dso)->status = DSO_DATA_STATUS_ERROR;
- ret = -errno;
+ /* fd already carries the negated errno from __open_dso() */
+ ret = dso__data(dso)->fd;
goto out;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 4/5] perf dso: Guard against cache underflow on short reads in dso_cache__memcpy()
2026-08-11 17:11 [PATCHES v2 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
` (2 preceding siblings ...)
2026-08-11 17:11 ` [PATCH 3/5] perf dso: Use stored fd error instead of stale errno in file_read() Arnaldo Carvalho de Melo
@ 2026-08-11 17:11 ` Arnaldo Carvalho de Melo
2026-08-11 17:12 ` [PATCH 5/5] perf dso: Replace assert with runtime check in dso__read_symbol() Arnaldo Carvalho de Melo
4 siblings, 0 replies; 7+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:11 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot
From: Arnaldo Carvalho de Melo <acme@redhat.com>
dso_cache__memcpy() computes cache_offset = offset - cache->offset,
then cache_size = min(cache->size - cache_offset, size). The RB tree
lookup in __dso_cache__find() matches using the full
DSO__DATA_CACHE_SIZE window, but cache->size reflects the actual pread
return value from dso_cache__populate().
A short pread (e.g. near end-of-file) makes cache->size smaller than
DSO__DATA_CACHE_SIZE. If a subsequent access targets an offset past
cache->offset + cache->size but within the DSO__DATA_CACHE_SIZE
window, the cache entry is found but cache_offset exceeds cache->size.
Since both are u64, the subtraction cache->size - cache_offset wraps
to a large value, min() selects the caller's size, and memcpy reads
out of bounds.
Return 0 (cache miss) when cache_offset falls outside the valid cached
range, so the caller re-reads from the backing file.
Fixes: 366df72657e0 ("perf dso: Refactor dso_cache__read()")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/dso.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 845a384a4d56779b..2cf9f44a87903d7d 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1005,7 +1005,17 @@ static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data,
u64 size, bool out)
{
u64 cache_offset = offset - cache->offset;
- u64 cache_size = min(cache->size - cache_offset, size);
+ u64 cache_size;
+
+ /*
+ * The RB tree matches using DSO__DATA_CACHE_SIZE, but a short
+ * pread may leave cache->size smaller. Treat an offset past
+ * the valid data as a cache miss so the caller re-reads.
+ */
+ if (cache_offset >= cache->size)
+ return 0;
+
+ cache_size = min(cache->size - cache_offset, size);
if (out)
memcpy(data, cache->data + cache_offset, cache_size);
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5/5] perf dso: Replace assert with runtime check in dso__read_symbol()
2026-08-11 17:11 [PATCHES v2 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
` (3 preceding siblings ...)
2026-08-11 17:11 ` [PATCH 4/5] perf dso: Guard against cache underflow on short reads in dso_cache__memcpy() Arnaldo Carvalho de Melo
@ 2026-08-11 17:12 ` Arnaldo Carvalho de Melo
4 siblings, 0 replies; 7+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:12 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ingo Molnar, Thomas Gleixner, James Clark, Jiri Olsa, Ian Rogers,
Adrian Hunter, Clark Williams, linux-kernel, linux-perf-users,
Arnaldo Carvalho de Melo, sashiko-bot, Song Liu
From: Arnaldo Carvalho de Melo <acme@redhat.com>
dso__read_symbol() asserts that len <= jited_prog_len, where len comes
from sym->end - sym->start (parsed from PERF_RECORD_KSYMBOL in
perf.data). Both values originate from untrusted file input.
With NDEBUG (production builds), the assert is compiled out, allowing
an out-of-bounds heap read when the BPF program buffer is accessed.
Without NDEBUG, a crafted perf.data crashes perf with an assertion
failure.
Replace the assert with a runtime bounds check that returns NULL with
an appropriate error code, matching the existing error handling
pattern in this function.
Fixes: aa04707f507e ("perf dso: Support BPF programs in dso__read_symbol()")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Ian Rogers <irogers@google.com>
Cc: Song Liu <song@kernel.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/dso.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 2cf9f44a87903d7d..031de8f1da0329b2 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -2027,7 +2027,12 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
return NULL;
}
- assert(len <= info_linear->info.jited_prog_len);
+ if (len > info_linear->info.jited_prog_len) {
+ pr_debug("BPF symbol length %zu exceeds jited_prog_len %u\n",
+ len, info_linear->info.jited_prog_len);
+ errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
+ return NULL;
+ }
*out_buf_len = len;
return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns);
#else
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread