[PATCH 14/15] perf symbols: Add O_NONBLOCK to DSO open() calls for untrusted paths

From: Arnaldo Carvalho de Melo

Date: Thu Jun 11 2026 - 20:42:24 EST


From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>

DSO paths from perf.data MMAP events could reference FIFOs, device
nodes, or other special files. Opening such paths without O_NONBLOCK
can hang perf report/script indefinitely waiting for a writer or device.

Add O_NONBLOCK to all open() calls in the DSO symbol resolution paths:
filename__read_build_id(), filename__read_debuglink(), symsrc__init(),
do_open(), filename__decompress(), and __dso__read_symbol().

O_NONBLOCK is a no-op on regular files but prevents blocking on special
files, following the systemd "foreign file" convention. Also add missing
O_CLOEXEC flags in symbol-minimal.c for consistency.

Fixes: a08cae03f430b971 ("perf tools: Allow to close dso fd in case of open failure")
Reported-by: sashiko-bot <sashiko-bot@xxxxxxxxxx>
Closes: https://sashiko.dev/finding/6
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Assisted-by: Claude Opus 4.6 <noreply@xxxxxxxxxxxxx>
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/util/dso.c | 6 +++---
tools/perf/util/symbol-elf.c | 7 ++++---
tools/perf/util/symbol-minimal.c | 6 +++---
3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 1a2fc6d18da74d6c..dc0c8b9c63017239 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -344,7 +344,7 @@ int filename__decompress(const char *name, char *pathname,
* descriptor to the uncompressed file.
*/
if (!compressions[comp].is_compressed(name)) {
- fd = open(name, O_RDONLY | O_CLOEXEC);
+ fd = open(name, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
*err = errno;
if (pathname && len > 0)
@@ -547,7 +547,7 @@ static void close_first_dso(void);
static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
{
do {
- int fd = open(name, O_RDONLY|O_CLOEXEC);
+ int fd = open(name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);

if (fd >= 0)
return fd;
@@ -1927,7 +1927,7 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename,
int saved_errno;

nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- fd = open(symfs_filename, O_RDONLY | O_CLOEXEC);
+ fd = open(symfs_filename, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
saved_errno = errno;
nsinfo__mountns_exit(&nsc);
if (fd < 0) {
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index dc48e2d2763379b9..01c7b09fc82bcddb 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -886,7 +886,8 @@ static int read_build_id(const char *filename, struct build_id *bid)
if (size < BUILD_ID_SIZE)
goto out;

- fd = open(filename, O_RDONLY | O_CLOEXEC);
+ /* O_NONBLOCK avoids hangs on FIFOs/devices from crafted perf.data paths */
+ fd = open(filename, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
goto out;

@@ -1022,7 +1023,7 @@ int filename__read_debuglink(const char *filename, char *debuglink,
if (err >= 0)
goto out;

- fd = open(filename, O_RDONLY | O_CLOEXEC);
+ fd = open(filename, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
goto out;

@@ -1187,7 +1188,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,

type = dso__symtab_type(dso);
} else {
- fd = open(name, O_RDONLY | O_CLOEXEC);
+ fd = open(name, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
*dso__load_errno(dso) = errno;
return -1;
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index ea2de3d50d33cf33..dc4a06b2a03ad3cb 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -118,7 +118,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid)
if (!is_regular_file(filename))
return errno == 0 ? -EWOULDBLOCK : -errno;

- fd = open(filename, O_RDONLY);
+ fd = open(filename, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
return -1;

@@ -234,7 +234,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid)
char buf[BUFSIZ] __aligned(4);
ssize_t len;

- fd = open(filename, O_RDONLY);
+ fd = open(filename, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
return -1;

@@ -257,7 +257,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid)
int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
enum dso_binary_type type)
{
- int fd = open(name, O_RDONLY);
+ int fd = open(name, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
goto out_errno;

--
2.54.0