From: Namhyung Kim <namhyung@kernel.org>
To: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Ian Rogers <irogers@google.com>, Jiri Olsa <jolsa@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@kernel.org>,
LKML <linux-kernel@vger.kernel.org>,
linux-perf-users@vger.kernel.org
Subject: [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode
Date: Fri, 23 Oct 2026 00:11:35 -0700 [thread overview]
Message-ID: <20261023071141.134149-6-namhyung@kernel.org> (raw)
In-Reply-To: <20261023071141.134149-1-namhyung@kernel.org>
Verify /proc/<pid>/task/<tid>/status exists and has Tgid == pid before
reading or entering a thread's namespaces to guard against PID/TID
recycling.
Also record the mount namespace inode (mntns_ino) during initialization
and verify via fstat() that the opened namespace file descriptor still
refers to the same mount namespace.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/namespaces.c | 120 +++++++++++++++++++++++++----------
tools/perf/util/namespaces.h | 4 ++
2 files changed, 91 insertions(+), 33 deletions(-)
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 843d075d30409029..bb119a6cc6064d89 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -65,6 +65,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
char *statln = NULL;
size_t linesz = 0;
char *nspid;
+ bool got_tgid = false;
f = fopen(path, "r");
if (f == NULL)
@@ -75,6 +76,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
if (strstr(statln, "Tgid:") != NULL) {
*tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10);
*nstgid = *tgid;
+ got_tgid = true;
}
if (strstr(statln, "NStgid:") != NULL) {
@@ -91,7 +93,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
fclose(f);
free(statln);
- return 0;
+ return got_tgid ? 0 : -1;
}
int nsinfo__init(struct nsinfo *nsi)
@@ -103,17 +105,34 @@ int nsinfo__init(struct nsinfo *nsi)
struct stat new_stat;
int rv = -1;
- if (!nsi)
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
return -1;
RC_CHK_ACCESS(nsi)->inited = true;
+ /*
+ * Verify that the thread (pid, tid) still exists in /proc and its Tgid
+ * matches pid (guarding against PID/TID recycling), while resolving the
+ * innermost tgid if the process is in a different PID namespace.
+ */
+ if (snprintf(spath, PATH_MAX, "/proc/%d/task/%d/status",
+ nsinfo__pid(nsi), nsinfo__tid(nsi)) >= PATH_MAX)
+ goto out;
+
+ rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
+ &RC_CHK_ACCESS(nsi)->in_pidns, spath);
+ if (rv < 0 || RC_CHK_ACCESS(nsi)->tgid != nsinfo__pid(nsi)) {
+ nsinfo__set_exited(nsi);
+ rv = -1;
+ goto out;
+ }
+
if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
- return rv;
+ return -1;
if (asprintf(&newns, "/proc/%d/task/%d/ns/mnt",
nsinfo__pid(nsi), nsinfo__tid(nsi)) == -1)
- return rv;
+ return -1;
if (stat(oldns, &old_stat) < 0)
goto out;
@@ -127,19 +146,10 @@ int nsinfo__init(struct nsinfo *nsi)
if (old_stat.st_ino != new_stat.st_ino) {
RC_CHK_ACCESS(nsi)->need_setns = true;
RC_CHK_ACCESS(nsi)->mntns_path = newns;
+ RC_CHK_ACCESS(nsi)->mntns_ino = new_stat.st_ino;
newns = NULL;
}
- /* If we're dealing with a process that is in a different PID namespace,
- * attempt to work out the innermost tgid for the process.
- */
- if (snprintf(spath, PATH_MAX, "/proc/%d/task/%d/status",
- nsinfo__pid(nsi), nsinfo__tid(nsi)) >= PATH_MAX)
- goto out;
-
- rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
- &RC_CHK_ACCESS(nsi)->in_pidns, spath);
-
out:
free(newns);
return rv;
@@ -147,7 +157,7 @@ int nsinfo__init(struct nsinfo *nsi)
static void nsinfo__ensure_init(struct nsinfo *nsi)
{
- if (nsi && !RC_CHK_ACCESS(nsi)->inited)
+ if (nsi && !RC_CHK_ACCESS(nsi)->inited && !RC_CHK_ACCESS(nsi)->exited)
nsinfo__init(nsi);
}
@@ -208,7 +218,9 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
RC_CHK_ACCESS(nnsi)->tid = nsinfo__tid(nsi);
RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
+ RC_CHK_ACCESS(nnsi)->mntns_ino = RC_CHK_ACCESS(nsi)->mntns_ino;
RC_CHK_ACCESS(nnsi)->inited = RC_CHK_ACCESS(nsi)->inited;
+ RC_CHK_ACCESS(nnsi)->exited = RC_CHK_ACCESS(nsi)->exited;
RC_CHK_ACCESS(nnsi)->need_setns = RC_CHK_ACCESS(nsi)->need_setns;
RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
if (nsinfo__mntns_path(nsi)) {
@@ -260,7 +272,7 @@ bool nsinfo__need_setns(const struct nsinfo *nsi)
return false;
nsinfo__ensure_init((struct nsinfo *)nsi);
- return RC_CHK_ACCESS(nsi)->need_setns;
+ return !RC_CHK_ACCESS(nsi)->exited && RC_CHK_ACCESS(nsi)->need_setns;
}
void nsinfo__clear_need_setns(struct nsinfo *nsi)
@@ -273,15 +285,44 @@ void nsinfo__clear_need_setns(struct nsinfo *nsi)
zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
}
-void nsinfo__reset_init(struct nsinfo *nsi)
+void nsinfo__set_exited(struct nsinfo *nsi)
{
if (!nsi)
return;
+ RC_CHK_ACCESS(nsi)->exited = true;
+ nsinfo__clear_need_setns(nsi);
+}
+
+void nsinfo__reset_init(struct nsinfo *nsi)
+{
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
+ return;
+
nsinfo__clear_need_setns(nsi);
RC_CHK_ACCESS(nsi)->inited = false;
}
+bool nsinfo__check_proc(struct nsinfo *nsi)
+{
+ char spath[64];
+ pid_t tgid = 0, nstgid = 0;
+ bool in_pidns = false;
+
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
+ return false;
+
+ scnprintf(spath, sizeof(spath), "/proc/%d/task/%d/status",
+ nsinfo__pid(nsi), nsinfo__tid(nsi));
+ if (nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, spath) < 0 ||
+ tgid != nsinfo__pid(nsi)) {
+ nsinfo__set_exited(nsi);
+ return false;
+ }
+
+ return true;
+}
+
pid_t nsinfo__tgid(const struct nsinfo *nsi)
{
nsinfo__ensure_init((struct nsinfo *)nsi);
@@ -328,10 +369,14 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
nc->oldns = -1;
nc->newns = -1;
+ nc->oldcwd = NULL;
if (!nsi || !nsinfo__need_setns(nsi))
return;
+ if (!nsinfo__check_proc(nsi))
+ return;
+
if (!getcwd(curpath, sizeof(curpath)))
return;
@@ -339,16 +384,26 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
if (!oldcwd)
return;
- oldns = open("/proc/self/ns/mnt", O_RDONLY);
- if (oldns < 0)
- goto errout;
+ if (RC_CHK_ACCESS(nsi)->need_setns) {
+ struct stat st;
+
+ oldns = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (oldns < 0)
+ goto errout;
- newns = open(nsinfo__mntns_path(nsi), O_RDONLY);
- if (newns < 0)
- goto errout;
+ newns = open(nsinfo__mntns_path(nsi), O_RDONLY | O_CLOEXEC);
+ if (newns < 0)
+ goto errout;
- if (setns(newns, CLONE_NEWNS) < 0)
- goto errout;
+ if (fstat(newns, &st) < 0 ||
+ st.st_ino != RC_CHK_ACCESS(nsi)->mntns_ino) {
+ nsinfo__set_exited(nsi);
+ goto errout;
+ }
+
+ if (setns(newns, CLONE_NEWNS) < 0)
+ goto errout;
+ }
nc->oldcwd = oldcwd;
nc->oldns = oldns;
@@ -365,17 +420,11 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
void nsinfo__mountns_exit(struct nscookie *nc)
{
- if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
+ if (nc == NULL || !nc->oldcwd)
return;
- setns(nc->oldns, CLONE_NEWNS);
-
- if (nc->oldcwd) {
- WARN_ON_ONCE(chdir(nc->oldcwd));
- zfree(&nc->oldcwd);
- }
-
if (nc->oldns > -1) {
+ setns(nc->oldns, CLONE_NEWNS);
close(nc->oldns);
nc->oldns = -1;
}
@@ -384,6 +433,11 @@ void nsinfo__mountns_exit(struct nscookie *nc)
close(nc->newns);
nc->newns = -1;
}
+
+ if (nc->oldcwd) {
+ WARN_ON_ONCE(chdir(nc->oldcwd));
+ zfree(&nc->oldcwd);
+ }
}
char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 77fe6f0eaf2f0618..165e9c9994461595 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -35,7 +35,9 @@ DECLARE_RC_STRUCT(nsinfo) {
pid_t tid;
pid_t tgid;
pid_t nstgid;
+ ino_t mntns_ino;
bool inited;
+ bool exited;
bool need_setns;
bool in_pidns;
char *mntns_path;
@@ -58,7 +60,9 @@ void nsinfo__put(struct nsinfo *nsi);
bool nsinfo__need_setns(const struct nsinfo *nsi);
void nsinfo__clear_need_setns(struct nsinfo *nsi);
+void nsinfo__set_exited(struct nsinfo *nsi);
void nsinfo__reset_init(struct nsinfo *nsi);
+bool nsinfo__check_proc(struct nsinfo *nsi);
pid_t nsinfo__tgid(const struct nsinfo *nsi);
pid_t nsinfo__nstgid(const struct nsinfo *nsi);
pid_t nsinfo__pid(const struct nsinfo *nsi);
--
2.56.0.257.gf7497c7d27-goog
next prev parent reply other threads:[~2026-10-23 7:11 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 01/11] perf tools: Add HEADER_BOOT_ID feature Namhyung Kim
2026-10-23 7:23 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 02/11] perf tools: Add perf_env__is_same_boot() Namhyung Kim
2026-10-23 7:22 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 03/11] perf tools: Add nsinfo__new_thread() to track tid Namhyung Kim
2026-10-23 7:21 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 04/11] perf tools: Defer /proc lookup until nsinfo is used Namhyung Kim
2026-10-23 7:26 ` sashiko-bot
2026-10-23 7:11 ` Namhyung Kim [this message]
2026-10-23 7:28 ` [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode sashiko-bot
2026-10-23 7:11 ` [PATCH v1 06/11] perf tools: Check thread liveness when accessing /proc Namhyung Kim
2026-10-23 7:44 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 07/11] perf tools: Handle chroot inside nsinfo__mountns_enter() Namhyung Kim
2026-10-23 7:51 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 08/11] perf tools: Export filename__read_build_id_ns() Namhyung Kim
2026-10-23 7:40 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 09/11] perf tools: Remove redundant chroot handling Namhyung Kim
2026-10-23 7:37 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 10/11] perf tools: Only enter mount namespace if necessary Namhyung Kim
2026-10-23 7:47 ` sashiko-bot
2026-10-23 7:11 ` [PATCH v1 11/11] perf tools: Scope mount namespace entry in dso__load() Namhyung Kim
2026-10-23 7:39 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261023071141.134149-6-namhyung@kernel.org \
--to=namhyung@kernel.org \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=peterz@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.