* [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-02 14:20 [PATCHES 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
@ 2026-08-02 14:20 ` Arnaldo Carvalho de Melo
2026-08-02 14:58 ` sashiko-bot
2026-08-02 21:08 ` David Laight
0 siblings, 2 replies; 11+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-02 14:20 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 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 2309196d8df3111c..e087a89066bdbc02 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -643,7 +643,7 @@ static int __open_dso(struct dso *dso, struct machine *machine)
if (name)
fd = do_open(name);
else
- fd = -errno;
+ fd = errno ? -errno : -ENOENT;
if (decomp)
unlink(name);
--
2.55.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-02 14:20 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
@ 2026-08-02 14:58 ` sashiko-bot
2026-08-02 21:38 ` Arnaldo Carvalho de Melo
2026-08-02 21:08 ` David Laight
1 sibling, 1 reply; 11+ messages in thread
From: sashiko-bot @ 2026-08-02 14:58 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Incomplete fix in `__open_dso()` causes error swallowing in callers by returning negative fd without setting `errno`.
Pre-existing issues:
- [Medium] Data race on adjacent `struct dso` bitfields leads to corruption and a TOCTOU mount namespace leak.
--
--- Patch [1]: [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL ---
commit db31953321ccfc3f2f28e5db31230d5fef57578f
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
perf dso: Guard against errno==0 when dso__get_filename() returns NULL
This commit updates __open_dso() to fall back to ENOENT when
dso__get_filename() returns NULL but leaves errno as 0. This ensures fd
is always negative on failure.
> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> index 2309196d8df31..e087a89066bdb 100644
> --- a/tools/perf/util/dso.c
> +++ b/tools/perf/util/dso.c
> @@ -643,7 +643,7 @@ static int __open_dso(struct dso *dso, struct machine *machine)
> if (name)
> fd = do_open(name);
> else
> - fd = -errno;
> + fd = errno ? -errno : -ENOENT;
[Severity: Medium]
Does this code need to explicitly set errno to ENOENT as well?
Callers of __open_dso() appear to check if the returned file descriptor is
negative, and then compute their return value using errno. For example, in
file_read():
tools/perf/util/dso.c:file_read() {
...
fd = data_file_open(dso, machine);
if (fd < 0)
return -errno;
...
}
Since errno is left as 0, callers like file_read() will evaluate -errno
to 0, which incorrectly reports EOF instead of an open failure. Should we
update errno directly?
[Severity: Medium]
This is a pre-existing issue, but can the adjacent bitfields in struct dso
cause a data race that leaks mount namespaces?
Looking at the struct dso definition:
tools/perf/util/dso.h:struct dso {
...
enum dso_binary_type symtab_type:8;
enum dso_binary_type binary_type:8;
...
}
If one thread calls try_to_open_dso() and locklessly modifies binary_type,
while another concurrently modifies symtab_type holding dso__lock(dso),
the concurrent read-modify-write on the shared memory word can corrupt
binary_type.
This corruption appears to break the namespace exit check in open_dso():
tools/perf/util/dso.c:open_dso() {
...
if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE) {
mutex_lock(dso__lock(dso));
nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
mutex_unlock(dso__lock(dso));
}
fd = __open_dso(dso, machine);
if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE)
nsinfo__mountns_exit(&nsc);
...
}
If the bitfield gets corrupted during __open_dso(), the exit check might
fail, skipping nsinfo__mountns_exit() and permanently stranding the thread
in the wrong mount namespace. Should access to these bitfields be protected
by dso__lock, or should they be separated to avoid sharing memory bytes?
>
> if (decomp)
> unlink(name);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802142022.154219-1-acme@kernel.org?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-02 14:20 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
2026-08-02 14:58 ` sashiko-bot
@ 2026-08-02 21:08 ` David Laight
1 sibling, 0 replies; 11+ messages in thread
From: David Laight @ 2026-08-02 21:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Namhyung Kim, 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
On Sun, 2 Aug 2026 11:20:18 -0300
Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> 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.
Except that errno isn't set to zero by successful syscalls.
It is only ever set by ones that fail.
The application may set errno to zero (eg to detect errors from some
library functions).
So paths that don't make syscalls will be returning a 'random' errno.
David
> 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 | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> index 2309196d8df3111c..e087a89066bdbc02 100644
> --- a/tools/perf/util/dso.c
> +++ b/tools/perf/util/dso.c
> @@ -643,7 +643,7 @@ static int __open_dso(struct dso *dso, struct machine *machine)
> if (name)
> fd = do_open(name);
> else
> - fd = -errno;
> + fd = errno ? -errno : -ENOENT;
>
> if (decomp)
> unlink(name);
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-02 14:58 ` sashiko-bot
@ 2026-08-02 21:38 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 11+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-02 21:38 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On Sun, Aug 02, 2026 at 02:58:28PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [Medium] Incomplete fix in `__open_dso()` causes error swallowing in callers by returning negative fd without setting `errno`.
>
> Pre-existing issues:
> - [Medium] Data race on adjacent `struct dso` bitfields leads to corruption and a TOCTOU mount namespace leak.
> --
>
> --- Patch [1]: [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL ---
> commit db31953321ccfc3f2f28e5db31230d5fef57578f
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
>
> perf dso: Guard against errno==0 when dso__get_filename() returns NULL
>
> This commit updates __open_dso() to fall back to ENOENT when
> dso__get_filename() returns NULL but leaves errno as 0. This ensures fd
> is always negative on failure.
>
> > diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> > index 2309196d8df31..e087a89066bdb 100644
> > --- a/tools/perf/util/dso.c
> > +++ b/tools/perf/util/dso.c
> > @@ -643,7 +643,7 @@ static int __open_dso(struct dso *dso, struct machine *machine)
> > if (name)
> > fd = do_open(name);
> > else
> > - fd = -errno;
> > + fd = errno ? -errno : -ENOENT;
>
> [Severity: Medium]
> Does this code need to explicitly set errno to ENOENT as well?
>
> Callers of __open_dso() appear to check if the returned file descriptor is
> negative, and then compute their return value using errno. For example, in
> file_read():
>
> tools/perf/util/dso.c:file_read() {
> ...
> fd = data_file_open(dso, machine);
> if (fd < 0)
> return -errno;
> ...
> }
>
> Since errno is left as 0, callers like file_read() will evaluate -errno
> to 0, which incorrectly reports EOF instead of an open failure. Should we
> update errno directly?
Agreed, will fix for v2.
- Arnaldo
> [Severity: Medium]
> This is a pre-existing issue, but can the adjacent bitfields in struct dso
> cause a data race that leaks mount namespaces?
>
> Looking at the struct dso definition:
>
> tools/perf/util/dso.h:struct dso {
> ...
> enum dso_binary_type symtab_type:8;
> enum dso_binary_type binary_type:8;
> ...
> }
>
> If one thread calls try_to_open_dso() and locklessly modifies binary_type,
> while another concurrently modifies symtab_type holding dso__lock(dso),
> the concurrent read-modify-write on the shared memory word can corrupt
> binary_type.
>
> This corruption appears to break the namespace exit check in open_dso():
>
> tools/perf/util/dso.c:open_dso() {
> ...
> if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE) {
> mutex_lock(dso__lock(dso));
> nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
> mutex_unlock(dso__lock(dso));
> }
> fd = __open_dso(dso, machine);
> if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE)
> nsinfo__mountns_exit(&nsc);
> ...
> }
>
> If the bitfield gets corrupted during __open_dso(), the exit check might
> fail, skipping nsinfo__mountns_exit() and permanently stranding the thread
> in the wrong mount namespace. Should access to these bitfields be protected
> by dso__lock, or should they be separated to avoid sharing memory bytes?
>
> >
> > if (decomp)
> > unlink(name);
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260802142022.154219-1-acme@kernel.org?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCHES v2 0/5] perf DSO hardening series
@ 2026-08-11 17:11 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
` (4 more replies)
0 siblings, 5 replies; 11+ 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, Song Liu
Hi,
Please consider merging,
Five hardening fixes for the perf DSO data handling code path, all found
by the sashiko-bot AI reviewer:
- __open_dso() can return a file descriptor of 0 (stdin) when
dso__get_filename() fails without setting errno, so fd = -errno = 0
looked like a successfully opened DSO;
- dso__decompress_kmodule_path() calls close(fd) with fd = -1 when
decompression fails or the DSO is not compressed, clobbering errno
with EBADF on the error path;
- file_read() reads the stale global errno after try_to_open_dso()
fails, when by then errno has been through mutex_lock(),
nsinfo__mountns_enter() and several open() attempts;
- dso_cache__memcpy() underflows when cache->size is smaller than the
RB tree offset (short pread), computing a huge memcpy length;
- dso__read_symbol() asserts on an input-dependent condition instead
of doing a runtime check, keeping the BPF metadata safe from crafted
input.
Best regards,
- Arnaldo
What changed from v1 (20260802142022.154219-1-acme@kernel.org):
PATCH 1/5:
- __open_dso() now sets errno = ENOENT directly when
dso__get_filename() fails with errno == 0, instead of only computing
fd = -ENOENT. Callers that check errno after a negative fd (e.g.
file_read() returning -errno) no longer get 0/EOF for an open
failure [sashiko-bot review of PATCH 1/5].
- Rebased onto the current perf-tools-next head (bf10e6ee2ac3034c).
This series was developed with assistance from Claude (claude-opus-4.6)
and Opencode (deepseek-v4-flash-free) for code analysis, patch
generation, and commit message composition. All changes were validated
by the maintainer.
Arnaldo Carvalho de Melo (5):
perf dso: Guard against errno==0 when dso__get_filename() returns NULL
perf dso: Guard close() against invalid fd in
dso__decompress_kmodule_path()
perf dso: Use stored fd error instead of stale errno in file_read()
perf dso: Guard against cache underflow on short reads in
dso_cache__memcpy()
perf dso: Replace assert with runtime check in dso__read_symbol()
tools/perf/util/dso.c | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
base-commit: bf10e6ee2ac3034c9068e03eed418fd16961984e
v1-head: f6cb9e46c7b8949b
^ permalink raw reply [flat|nested] 11+ messages in thread
* [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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ messages in thread
* [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL
2026-08-11 17:51 [PATCHES v3 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
@ 2026-08-11 17:51 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 11+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-11 17:51 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] 11+ messages in thread
end of thread, other threads:[~2026-08-11 17:51 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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 ` [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
-- strict thread matches above, loose matches on Subject: below --
2026-08-11 17:51 [PATCHES v3 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
2026-08-11 17:51 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
2026-08-02 14:20 [PATCHES 0/5] perf DSO hardening series Arnaldo Carvalho de Melo
2026-08-02 14:20 ` [PATCH 1/5] perf dso: Guard against errno==0 when dso__get_filename() returns NULL Arnaldo Carvalho de Melo
2026-08-02 14:58 ` sashiko-bot
2026-08-02 21:38 ` Arnaldo Carvalho de Melo
2026-08-02 21:08 ` David Laight
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.