* [PATCH v2 1/3] fs: Introduce task path helpers
2026-08-31 2:59 [PATCH v2 0/3] pidfd: add task path ioctls Chen Linxuan via B4 Relay
@ 2026-08-31 2:59 ` Chen Linxuan via B4 Relay
2026-08-31 2:59 ` [PATCH v2 2/3] pidfd: Use scoped cleanup for task access Chen Linxuan via B4 Relay
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Chen Linxuan via B4 Relay @ 2026-08-31 2:59 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn
Cc: linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api, Chen Linxuan
From: Chen Linxuan <me@black-desk.cn>
Introduce helpers that acquire a referenced struct path for a task's
executable, root, and working directory. Reuse them for procfs task
links and AppArmor executable-path handling instead of duplicating file
and path reference handling at each call site.
Assisted-by: LLM
Signed-off-by: Chen Linxuan <me@black-desk.cn>
---
fs/fs_struct.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
fs/proc/base.c | 34 ++--------------------------------
include/linux/fs_struct.h | 3 +++
include/linux/mm.h | 1 +
kernel/fork.c | 21 +++++++++++++++++++++
security/apparmor/task.c | 12 +++---------
6 files changed, 74 insertions(+), 41 deletions(-)
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 34699f3b6f88..5c772896260a 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -10,6 +10,50 @@
#include "internal.h"
#include "mount.h"
+/**
+ * get_task_root - acquire a reference to the task's root path
+ * @task: The task.
+ * @root: The task's root path.
+ *
+ * Returns 0 if the task has a root path, or -ENOENT if it does not. The
+ * caller must release the path through path_put() on success.
+ */
+int get_task_root(struct task_struct *task, struct path *root)
+{
+ int ret = -ENOENT;
+
+ task_lock(task);
+ if (task->real_fs) {
+ get_fs_root(task->real_fs, root);
+ ret = 0;
+ }
+ task_unlock(task);
+
+ return ret;
+}
+
+/**
+ * get_task_pwd - acquire a reference to the task's working directory
+ * @task: The task.
+ * @pwd: The task's working directory.
+ *
+ * Returns 0 if the task has a working directory, or -ENOENT if it does not.
+ * The caller must release the path through path_put() on success.
+ */
+int get_task_pwd(struct task_struct *task, struct path *pwd)
+{
+ int ret = -ENOENT;
+
+ task_lock(task);
+ if (task->real_fs) {
+ get_fs_pwd(task->real_fs, pwd);
+ ret = 0;
+ }
+ task_unlock(task);
+
+ return ret;
+}
+
/*
* Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
* It can block.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 6a39de424f62..7e2c0538323c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -206,31 +206,10 @@ static unsigned int __init pid_entry_nlink(const struct pid_entry *entries,
return count;
}
-static int get_task_root(struct task_struct *task, struct path *root)
-{
- int result = -ENOENT;
-
- task_lock(task);
- if (task->real_fs) {
- get_fs_root(task->real_fs, root);
- result = 0;
- }
- task_unlock(task);
- return result;
-}
-
static int proc_cwd_link(struct dentry *dentry, struct path *path,
struct task_struct *task)
{
- int result = -ENOENT;
-
- task_lock(task);
- if (task->real_fs) {
- get_fs_pwd(task->real_fs, path);
- result = 0;
- }
- task_unlock(task);
- return result;
+ return get_task_pwd(task, path);
}
static int proc_root_link(struct dentry *dentry, struct path *path,
@@ -1761,16 +1740,7 @@ static const struct file_operations proc_pid_set_comm_operations = {
static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
struct task_struct *task)
{
- struct file *exe_file;
-
- exe_file = get_task_exe_file(task);
- if (exe_file) {
- *exe_path = exe_file->f_path;
- path_get(&exe_file->f_path);
- fput(exe_file);
- return 0;
- } else
- return -ENOENT;
+ return get_task_exe_path(task, exe_path);
}
static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out)
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index 97eef8d3863d..fb725707754d 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -42,6 +42,9 @@ static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd)
read_sequnlock_excl(&fs->seq);
}
+int get_task_root(struct task_struct *task, struct path *root);
+int get_task_pwd(struct task_struct *task, struct path *pwd);
+
struct fs_struct *switch_fs_struct(struct fs_struct *new_fs);
extern bool current_chrooted(void);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index dd09c438fa23..a65f24b2e65b 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -4239,6 +4239,7 @@ extern int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
extern int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
extern struct file *get_mm_exe_file(struct mm_struct *mm);
extern struct file *get_task_exe_file(struct task_struct *task);
+int get_task_exe_path(struct task_struct *task, struct path *exe_path);
extern void vm_stat_account(struct mm_struct *, vm_flags_t, long npages);
diff --git a/kernel/fork.c b/kernel/fork.c
index 416758c8a3d4..a493de3ae5c2 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1371,6 +1371,27 @@ struct file *get_task_exe_file(struct task_struct *task)
return exe_file;
}
+/**
+ * get_task_exe_path - acquire a reference to the task's executable path
+ * @task: The task.
+ * @exe_path: The task's executable path.
+ *
+ * Returns 0 if the task has an executable path, or -ENOENT if it does not.
+ * The caller must release the path through path_put() on success.
+ */
+int get_task_exe_path(struct task_struct *task, struct path *exe_path)
+{
+ struct file *exe_file = get_task_exe_file(task);
+
+ if (!exe_file)
+ return -ENOENT;
+
+ *exe_path = exe_file->f_path;
+ path_get(exe_path);
+ fput(exe_file);
+ return 0;
+}
+
/**
* get_task_mm - acquire a reference to the task's mm
* @task: The task.
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index e16ff4130bc2..05cba261dd34 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -13,6 +13,7 @@
*/
#include <linux/gfp.h>
+#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/task_work.h>
@@ -330,22 +331,15 @@ int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
static const char *get_current_exe_path(char *buffer, int buffer_size)
{
- struct file *exe_file;
- struct path p;
+ struct path p __free(path_put) = {};
const char *path_str;
- exe_file = get_task_exe_file(current);
- if (!exe_file)
+ if (get_task_exe_path(current, &p))
return ERR_PTR(-ENOENT);
- p = exe_file->f_path;
- path_get(&p);
if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
path_str = ERR_PTR(-ENOMEM);
- fput(exe_file);
- path_put(&p);
-
return path_str;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 2/3] pidfd: Use scoped cleanup for task access
2026-08-31 2:59 [PATCH v2 0/3] pidfd: add task path ioctls Chen Linxuan via B4 Relay
2026-08-31 2:59 ` [PATCH v2 1/3] fs: Introduce task path helpers Chen Linxuan via B4 Relay
@ 2026-08-31 2:59 ` Chen Linxuan via B4 Relay
2026-08-31 2:59 ` [PATCH v2 3/3] pidfd: Add task path ioctls Chen Linxuan via B4 Relay
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Chen Linxuan via B4 Relay @ 2026-08-31 2:59 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn
Cc: linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api, Chen Linxuan
From: Chen Linxuan <me@black-desk.cn>
Split namespace acquisition from namespace fd creation and represent a
privileged target task as a scoped class that owns both the task
reference and exec_update_lock. Use the class for namespace lookups so
ptrace access checks and task state reads remain tied to the same exec
critical section, while open_namespace() stays outside the lock.
Assisted-by: LLM
Signed-off-by: Chen Linxuan <me@black-desk.cn>
---
fs/pidfs.c | 115 ++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 71 insertions(+), 44 deletions(-)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index a6a643f15d08..49a1ab0c42f9 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -527,62 +527,67 @@ static bool pidfs_ioctl_valid(unsigned int cmd)
return false;
}
-static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static inline void pidfd_put_task_locked(struct task_struct *task)
{
- struct task_struct *task __free(put_task) = NULL;
- struct nsproxy *nsp __free(put_nsproxy) = NULL;
- struct ns_common *ns_common = NULL;
- int error;
-
- if (!pidfs_ioctl_valid(cmd))
- return -ENOIOCTLCMD;
-
- if (cmd == FS_IOC_GETVERSION) {
- if (!arg)
- return -EINVAL;
-
- __u32 __user *argp = (__u32 __user *)arg;
- return put_user(file_inode(file)->i_generation, argp);
+ if (!IS_ERR_OR_NULL(task)) {
+ up_read(&task->signal->exec_update_lock);
+ put_task_struct(task);
}
+}
- /* Extensible IOCTL that does not open namespace FDs, take a shortcut */
- if (_IOC_NR(cmd) == _IOC_NR(PIDFD_GET_INFO))
- return pidfd_info(file, cmd, arg);
+/*
+ * Return @pid's task with @task's exec_update_lock held. The ptrace check
+ * and the callers' task state lookup must be performed while the lock is held
+ * so that they cannot race with a concurrent execve().
+ */
+static struct task_struct *pidfd_get_task_locked(struct pid *pid,
+ unsigned long arg)
+{
+ struct task_struct *task __free(put_task) =
+ get_pid_task(pid, PIDTYPE_PID);
+ int error;
- task = get_pid_task(pidfd_pid(file), PIDTYPE_PID);
if (!task)
- return -ESRCH;
+ return ERR_PTR(-ESRCH);
if (arg)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- /*
- * We're trying to open a file descriptor to the namespace so perform a
- * filesystem cred ptrace check. Hold @task's exec_update_lock for the
- * duration of the ptrace check and the namespace lookup so that the
- * credentials used for the access decision match those of @task at the
- * time its namespace is read, preventing a concurrent execve() from
- * swapping the task's credentials in between the check and the use. We
- * mirror nsfs behavior.
- */
error = down_read_killable(&task->signal->exec_update_lock);
if (error)
- return error;
+ return ERR_PTR(error);
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
- error = -EACCES;
- goto out_unlock;
+ up_read(&task->signal->exec_update_lock);
+ return ERR_PTR(-EACCES);
}
+ return_ptr(task);
+}
+
+DEFINE_CLASS(pidfd_task_locked, struct task_struct *,
+ pidfd_put_task_locked(_T),
+ pidfd_get_task_locked(pid, arg),
+ struct pid *pid, unsigned long arg)
+
+static struct ns_common *pidfd_get_namespace(struct pid *pid,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct nsproxy *nsp __free(put_nsproxy) = NULL;
+ struct ns_common *ns_common = NULL;
+
+ CLASS(pidfd_task_locked, task)(pid, arg);
+ if (IS_ERR(task))
+ return ERR_CAST(task);
+
scoped_guard(task_lock, task) {
nsp = task->nsproxy;
if (nsp)
get_nsproxy(nsp);
}
- if (!nsp) {
- error = -ESRCH; /* just pretend it didn't exist */
- goto out_unlock;
- }
+ if (!nsp)
+ return ERR_PTR(-ESRCH); /* just pretend it didn't exist */
switch (cmd) {
/* Namespaces that hang of nsproxy. */
@@ -664,16 +669,38 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#endif
break;
default:
- error = -ENOIOCTLCMD;
+ return ERR_PTR(-ENOIOCTLCMD);
}
- if (!error && !ns_common)
- error = -EOPNOTSUPP;
+ if (!ns_common)
+ return ERR_PTR(-EOPNOTSUPP);
-out_unlock:
- up_read(&task->signal->exec_update_lock);
- if (error)
- return error;
+ return ns_common;
+}
+
+static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ns_common *ns_common = NULL;
+
+ if (!pidfs_ioctl_valid(cmd))
+ return -ENOIOCTLCMD;
+
+ if (cmd == FS_IOC_GETVERSION) {
+ if (!arg)
+ return -EINVAL;
+
+ __u32 __user *argp = (__u32 __user *)arg;
+
+ return put_user(file_inode(file)->i_generation, argp);
+ }
+
+ /* Extensible IOCTL that does not open namespace FDs, take a shortcut */
+ if (_IOC_NR(cmd) == _IOC_NR(PIDFD_GET_INFO))
+ return pidfd_info(file, cmd, arg);
+
+ ns_common = pidfd_get_namespace(pidfd_pid(file), cmd, arg);
+ if (IS_ERR(ns_common))
+ return PTR_ERR(ns_common);
/* open_namespace() unconditionally consumes the reference */
return open_namespace(ns_common);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 3/3] pidfd: Add task path ioctls
2026-08-31 2:59 [PATCH v2 0/3] pidfd: add task path ioctls Chen Linxuan via B4 Relay
2026-08-31 2:59 ` [PATCH v2 1/3] fs: Introduce task path helpers Chen Linxuan via B4 Relay
2026-08-31 2:59 ` [PATCH v2 2/3] pidfd: Use scoped cleanup for task access Chen Linxuan via B4 Relay
@ 2026-08-31 2:59 ` Chen Linxuan via B4 Relay
2026-08-31 8:49 ` [PATCH v2 0/3] pidfd: add " Christian Brauner
2026-09-04 14:53 ` Florian Weimer
4 siblings, 0 replies; 7+ messages in thread
From: Chen Linxuan via B4 Relay @ 2026-08-31 2:59 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn
Cc: linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api, Chen Linxuan
From: Chen Linxuan <me@black-desk.cn>
Add PIDFD_GET_EXE, PIDFD_GET_CWD, and PIDFD_GET_ROOT to return
close-on-exec O_PATH file descriptors referencing the target task's
executable, working directory, and root directory.
The new ioctls use the same PTRACE_MODE_READ_FSCREDS permission check
and nonzero-argument rejection as the existing namespace ioctls. The
target is sampled while holding exec_update_lock so that the access
check and path read cannot race with execve().
This allows userspace to obtain stable path references from a pidfd
without requiring procfs.
Assisted-by: LLM
Signed-off-by: Chen Linxuan <me@black-desk.cn>
---
fs/pidfs.c | 38 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/pidfd.h | 7 +++++++
2 files changed, 45 insertions(+)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 49a1ab0c42f9..b21374f00002 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -4,6 +4,7 @@
#include <linux/exportfs.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/fs_struct.h>
#include <linux/cgroup.h>
#include <linux/magic.h>
#include <linux/mount.h>
@@ -510,6 +511,9 @@ static bool pidfs_ioctl_valid(unsigned int cmd)
case PIDFD_GET_UTS_NAMESPACE:
case PIDFD_GET_USER_NAMESPACE:
case PIDFD_GET_PID_NAMESPACE:
+ case PIDFD_GET_EXE:
+ case PIDFD_GET_CWD:
+ case PIDFD_GET_ROOT:
return true;
}
@@ -678,9 +682,31 @@ static struct ns_common *pidfd_get_namespace(struct pid *pid,
return ns_common;
}
+static int pidfd_get_task_path(struct pid *pid, unsigned int cmd,
+ unsigned long arg, struct path *path)
+{
+ CLASS(pidfd_task_locked, task)(pid, arg);
+
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ switch (cmd) {
+ case PIDFD_GET_EXE:
+ return get_task_exe_path(task, path);
+ case PIDFD_GET_CWD:
+ return get_task_pwd(task, path);
+ case PIDFD_GET_ROOT:
+ return get_task_root(task, path);
+ }
+
+ return -EINVAL;
+}
+
static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ns_common *ns_common = NULL;
+ struct path path __free(path_put) = {};
+ int error;
if (!pidfs_ioctl_valid(cmd))
return -ENOIOCTLCMD;
@@ -698,6 +724,18 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (_IOC_NR(cmd) == _IOC_NR(PIDFD_GET_INFO))
return pidfd_info(file, cmd, arg);
+ switch (cmd) {
+ case PIDFD_GET_EXE:
+ case PIDFD_GET_CWD:
+ case PIDFD_GET_ROOT:
+ error = pidfd_get_task_path(pidfd_pid(file), cmd, arg, &path);
+ if (error)
+ return error;
+
+ return FD_ADD(O_CLOEXEC,
+ dentry_open(&path, O_PATH, current_cred()));
+ }
+
ns_common = pidfd_get_namespace(pidfd_pid(file), cmd, arg);
if (IS_ERR(ns_common))
return PTR_ERR(ns_common);
diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h
index 0919246a1611..95ce1819f423 100644
--- a/include/uapi/linux/pidfd.h
+++ b/include/uapi/linux/pidfd.h
@@ -121,4 +121,11 @@ struct pidfd_info {
#define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
+/* Return an O_PATH file descriptor for the target task's executable. */
+#define PIDFD_GET_EXE _IO(PIDFS_IOCTL_MAGIC, 12)
+/* Return an O_PATH file descriptor for the target task's working directory. */
+#define PIDFD_GET_CWD _IO(PIDFS_IOCTL_MAGIC, 13)
+/* Return an O_PATH file descriptor for the target task's root directory. */
+#define PIDFD_GET_ROOT _IO(PIDFS_IOCTL_MAGIC, 14)
+
#endif /* _UAPI_LINUX_PIDFD_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v2 0/3] pidfd: add task path ioctls
2026-08-31 2:59 [PATCH v2 0/3] pidfd: add task path ioctls Chen Linxuan via B4 Relay
` (2 preceding siblings ...)
2026-08-31 2:59 ` [PATCH v2 3/3] pidfd: Add task path ioctls Chen Linxuan via B4 Relay
@ 2026-08-31 8:49 ` Christian Brauner
2026-09-02 6:47 ` Chen Linxuan
2026-09-04 14:53 ` Florian Weimer
4 siblings, 1 reply; 7+ messages in thread
From: Christian Brauner @ 2026-08-31 8:49 UTC (permalink / raw)
To: Chen Linxuan
Cc: Alexander Viro, Christian Brauner, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn,
linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api
On 2026-08-31 10:59 +0800, Chen Linxuan wrote:
> Obtaining a target task's executable, working directory, or root
> currently requires walking procfs symlinks such as /proc/<pid>/exe,
> /proc/<pid>/cwd, and /proc/<pid>/root. That makes the operation depend
> on procfs being mounted and visible to the caller, even when it already
> holds a pidfd for the target.
>
> This series adds PIDFD_GET_EXE, PIDFD_GET_CWD, and PIDFD_GET_ROOT. Each
> ioctl takes no argument and returns a close-on-exec O_PATH file
> descriptor referencing the corresponding task path. The new ioctls use
> the same ptrace permission check and nonzero-argument rejection as the
> existing pidfd namespace ioctls.
>
> The target task is sampled while holding its exec_update_lock. This
> keeps the access decision and the task-state read in the same exec
> critical section, preventing a concurrent execve() from changing the
> credentials or target state between the check and the use.
>
> The first patch factors out helpers for acquiring referenced task paths
> and reuses them in procfs and AppArmor. The second patch introduces
> scoped cleanup for privileged pidfd task access and separates namespace
> lookup from namespace fd creation. The final patch uses these pieces to
> implement the three new ioctls.
>
> Signed-off-by: Chen Linxuan <me@black-desk.cn>
> ---
I really have difficulties forming an opinion on this. So this sounds
very useful but it has implications.
Right now, pidfd ioctls are available even in situations where the task
in question would not be accessible via procfs, e.g., when procfs is
mounted with "hidepid" options or similar. So this would expand the
surface of operations you could potentially do.
But again, I do think it is actually useful. Adding Jann.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2 0/3] pidfd: add task path ioctls
2026-08-31 8:49 ` [PATCH v2 0/3] pidfd: add " Christian Brauner
@ 2026-09-02 6:47 ` Chen Linxuan
0 siblings, 0 replies; 7+ messages in thread
From: Chen Linxuan @ 2026-09-02 6:47 UTC (permalink / raw)
To: Christian Brauner
Cc: Chen Linxuan, Alexander Viro, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn,
linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api, Jann Horn
On Mon, Aug 31, 2026 at 4:49 PM Christian Brauner <brauner@kernel.org> wrote:
>
> On 2026-08-31 10:59 +0800, Chen Linxuan wrote:
> > Obtaining a target task's executable, working directory, or root
> > currently requires walking procfs symlinks such as /proc/<pid>/exe,
> > /proc/<pid>/cwd, and /proc/<pid>/root. That makes the operation depend
> > on procfs being mounted and visible to the caller, even when it already
> > holds a pidfd for the target.
> >
> > This series adds PIDFD_GET_EXE, PIDFD_GET_CWD, and PIDFD_GET_ROOT. Each
> > ioctl takes no argument and returns a close-on-exec O_PATH file
> > descriptor referencing the corresponding task path. The new ioctls use
> > the same ptrace permission check and nonzero-argument rejection as the
> > existing pidfd namespace ioctls.
> >
> > The target task is sampled while holding its exec_update_lock. This
> > keeps the access decision and the task-state read in the same exec
> > critical section, preventing a concurrent execve() from changing the
> > credentials or target state between the check and the use.
> >
> > The first patch factors out helpers for acquiring referenced task paths
> > and reuses them in procfs and AppArmor. The second patch introduces
> > scoped cleanup for privileged pidfd task access and separates namespace
> > lookup from namespace fd creation. The final patch uses these pieces to
> > implement the three new ioctls.
> >
> > Signed-off-by: Chen Linxuan <me@black-desk.cn>
> > ---
>
> I really have difficulties forming an opinion on this. So this sounds
> very useful but it has implications.
>
> Right now, pidfd ioctls are available even in situations where the task
> in question would not be accessible via procfs, e.g., when procfs is
> mounted with "hidepid" options or similar. So this would expand the
One point regarding hidepid: for callers that can pass
PTRACE_MODE_READ_FSCREDS, hidepid does not provide an additional
restriction. With hidepid=1 or hidepid=2, has_pid_permissions() falls
back to ptrace_may_access(..., PTRACE_MODE_READ_FSCREDS), and
hidepid=ptraceable uses that check directly. In addition, the
/proc/<pid>/{exe,cwd,root} links independently perform the same
PTRACE_MODE_READ_FSCREDS check in call_proc_get_link(), regardless of
the hidepid mode.
So for these specific path lookups, hidepid does not block a caller who
would already pass the check used by the proposed pidfd ioctls.
> surface of operations you could potentially do.
>
> But again, I do think it is actually useful. Adding Jann.
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 0/3] pidfd: add task path ioctls
2026-08-31 2:59 [PATCH v2 0/3] pidfd: add task path ioctls Chen Linxuan via B4 Relay
` (3 preceding siblings ...)
2026-08-31 8:49 ` [PATCH v2 0/3] pidfd: add " Christian Brauner
@ 2026-09-04 14:53 ` Florian Weimer
4 siblings, 0 replies; 7+ messages in thread
From: Florian Weimer @ 2026-09-04 14:53 UTC (permalink / raw)
To: Chen Linxuan via B4 Relay
Cc: Alexander Viro, Christian Brauner, Jan Kara, Andrew Morton,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Kees Cook, John Johansen,
Georgia Garcia, Paul Moore, James Morris, Serge E. Hallyn, me,
linux-fsdevel, linux-kernel, linux-mm, apparmor,
linux-security-module, linux-api
* Chen Linxuan via:
> This series adds PIDFD_GET_EXE, PIDFD_GET_CWD, and PIDFD_GET_ROOT. Each
> ioctl takes no argument and returns a close-on-exec O_PATH file
> descriptor referencing the corresponding task path. The new ioctls use
> the same ptrace permission check and nonzero-argument rejection as the
> existing pidfd namespace ioctls.
How would one upgrade the O_PATH descriptor to a full descriptor without
a mounted /proc?
Thanks,
Florian
^ permalink raw reply [flat|nested] 7+ messages in thread