History

pidfs: allow retrieval of namespace file descriptors

For users that hold a reference to a pidfd procfs might not even be available nor is it desirable to parse through procfs just for the sake of getting namespace file descriptors for a process. Make it possible to directly retrieve namespace file descriptors from a pidfd. Pidfds already can be used with setns() to change a set of namespaces atomically.

Christian Brauner committed 2 years ago

Commit 5b08bd41 parent 460695a2 files · +104 −0Browse files

Unified · Split

2 files
  1. fs/pidfs.c +90 −0
  2. include/uapi/linux/pidfd.h +14 −0
fs/pidfs.c+90−0
@@ -11,10 +11,16 @@ · 10 unmodified lines
1111 #include <linux/proc_fs.h>
1212 #include <linux/proc_ns.h>
1313 #include <linux/pseudo_fs.h>
14+#include <linux/ptrace.h>
1415 #include <linux/seq_file.h>
1516 #include <uapi/linux/pidfd.h>
17+#include <linux/ipc_namespace.h>
18+#include <linux/time_namespace.h>
19+#include <linux/utsname.h>
20+#include <net/net_namespace.h>
1621
1722 #include "internal.h"
23+#include "mount.h"
1824
1925 #ifdef CONFIG_PROC_FS
2026 /**
@@ -108,11 +114,95 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts) · 87 unmodified lines
108114 return poll_flags;
109115 }
110116
117+static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
118+{
119+ struct task_struct *task __free(put_task) = NULL;
120+ struct nsproxy *nsp __free(put_nsproxy) = NULL;
121+ struct pid *pid = pidfd_pid(file);
122+ struct ns_common *ns_common;
123+
124+ if (arg)
125+ return -EINVAL;
126+
127+ task = get_pid_task(pid, PIDTYPE_PID);
128+ if (!task)
129+ return -ESRCH;
130+
131+ scoped_guard(task_lock, task) {
132+ nsp = task->nsproxy;
133+ if (nsp)
134+ get_nsproxy(nsp);
135+ }
136+ if (!nsp)
137+ return -ESRCH; /* just pretend it didn't exist */
138+
139+ /*
140+ * We're trying to open a file descriptor to the namespace so perform a
141+ * filesystem cred ptrace check. Also, we mirror nsfs behavior.
142+ */
143+ if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
144+ return -EACCES;
145+
146+ switch (cmd) {
147+ /* Namespaces that hang of nsproxy. */
148+ case PIDFD_GET_CGROUP_NAMESPACE:
149+ get_cgroup_ns(nsp->cgroup_ns);
150+ ns_common = to_ns_common(nsp->cgroup_ns);
151+ break;
152+ case PIDFD_GET_IPC_NAMESPACE:
153+ get_ipc_ns(nsp->ipc_ns);
154+ ns_common = to_ns_common(nsp->ipc_ns);
155+ break;
156+ case PIDFD_GET_MNT_NAMESPACE:
157+ get_mnt_ns(nsp->mnt_ns);
158+ ns_common = to_ns_common(nsp->mnt_ns);
159+ break;
160+ case PIDFD_GET_NET_NAMESPACE:
161+ ns_common = to_ns_common(nsp->net_ns);
162+ get_net_ns(ns_common);
163+ break;
164+ case PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE:
165+ get_pid_ns(nsp->pid_ns_for_children);
166+ ns_common = to_ns_common(nsp->pid_ns_for_children);
167+ break;
168+ case PIDFD_GET_TIME_NAMESPACE:
169+ get_time_ns(nsp->time_ns);
170+ ns_common = to_ns_common(nsp->time_ns);
171+ break;
172+ case PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE:
173+ get_time_ns(nsp->time_ns_for_children);
174+ ns_common = to_ns_common(nsp->time_ns_for_children);
175+ break;
176+ case PIDFD_GET_UTS_NAMESPACE:
177+ get_uts_ns(nsp->uts_ns);
178+ ns_common = to_ns_common(nsp->uts_ns);
179+ break;
180+ /* Namespaces that don't hang of nsproxy. */
181+ case PIDFD_GET_USER_NAMESPACE:
182+ rcu_read_lock();
183+ ns_common = to_ns_common(get_user_ns(task_cred_xxx(task, user_ns)));
184+ rcu_read_unlock();
185+ break;
186+ case PIDFD_GET_PID_NAMESPACE:
187+ rcu_read_lock();
188+ ns_common = to_ns_common(get_pid_ns(task_active_pid_ns(task)));
189+ rcu_read_unlock();
190+ break;
191+ default:
192+ return -ENOIOCTLCMD;
193+ }
194+
195+ /* open_namespace() unconditionally consumes the reference */
196+ return open_namespace(ns_common);
197+}
198+
111199 static const struct file_operations pidfs_file_operations = {
112200 .poll = pidfd_poll,
113201 #ifdef CONFIG_PROC_FS
114202 .show_fdinfo = pidfd_show_fdinfo,
115203 #endif
204+ .unlocked_ioctl = pidfd_ioctl,
205+ .compat_ioctl = compat_ptr_ioctl,
116206 };
117207
118208 struct pid *pidfd_pid(const struct file *file)
include/uapi/linux/pidfd.h+14−0
@@ -5,6 +5,7 @@ · 4 unmodified lines
55
66 #include <linux/types.h>
77 #include <linux/fcntl.h>
8+#include <linux/ioctl.h>
89
910 /* Flags for pidfd_open(). */
1011 #define PIDFD_NONBLOCK O_NONBLOCK
@@ -15,4 +16,17 @@ · 4 unmodified lines
1516 #define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1)
1617 #define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2)
1718
19+#define PIDFS_IOCTL_MAGIC 0xFF
20+
21+#define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1)
22+#define PIDFD_GET_IPC_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 2)
23+#define PIDFD_GET_MNT_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 3)
24+#define PIDFD_GET_NET_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 4)
25+#define PIDFD_GET_PID_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 5)
26+#define PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 6)
27+#define PIDFD_GET_TIME_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 7)
28+#define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8)
29+#define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9)
30+#define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
31+
1832 #endif /* _UAPI_LINUX_PIDFD_H */