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.
Unified · Split
| @@ -11,10 +11,16 @@ · 10 unmodified lines | |||
| 11 | #include <linux/proc_fs.h> | 11 | #include <linux/proc_fs.h> |
| 12 | #include <linux/proc_ns.h> | 12 | #include <linux/proc_ns.h> |
| 13 | #include <linux/pseudo_fs.h> | 13 | #include <linux/pseudo_fs.h> |
| 14 | #include <linux/ptrace.h> | ||
| 14 | #include <linux/seq_file.h> | 15 | #include <linux/seq_file.h> |
| 15 | #include <uapi/linux/pidfd.h> | 16 | #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> | ||
| 16 | 21 | ||
| 17 | #include "internal.h" | 22 | #include "internal.h" |
| 23 | #include "mount.h" | ||
| 18 | 24 | ||
| 19 | #ifdef CONFIG_PROC_FS | 25 | #ifdef CONFIG_PROC_FS |
| 20 | /** | 26 | /** |
| @@ -108,11 +114,95 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts) · 87 unmodified lines | |||
| 108 | return poll_flags; | 114 | return poll_flags; |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 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 | |||
| 111 | static const struct file_operations pidfs_file_operations = { | 199 | static const struct file_operations pidfs_file_operations = { |
| 112 | .poll = pidfd_poll, | 200 | .poll = pidfd_poll, |
| 113 | #ifdef CONFIG_PROC_FS | 201 | #ifdef CONFIG_PROC_FS |
| 114 | .show_fdinfo = pidfd_show_fdinfo, | 202 | .show_fdinfo = pidfd_show_fdinfo, |
| 115 | #endif | 203 | #endif |
| 204 | .unlocked_ioctl = pidfd_ioctl, | ||
| 205 | .compat_ioctl = compat_ptr_ioctl, | ||
| 116 | }; | 206 | }; |
| 117 | 207 | ||
| 118 | struct pid *pidfd_pid(const struct file *file) | 208 | struct pid *pidfd_pid(const struct file *file) |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
| 7 | #include <linux/fcntl.h> | 7 | #include <linux/fcntl.h> |
| 8 | #include <linux/ioctl.h> | ||
| 8 | 9 | ||
| 9 | /* Flags for pidfd_open(). */ | 10 | /* Flags for pidfd_open(). */ |
| 10 | #define PIDFD_NONBLOCK O_NONBLOCK | 11 | #define PIDFD_NONBLOCK O_NONBLOCK |
| @@ -15,4 +16,17 @@ · 4 unmodified lines | |||
| 15 | #define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1) | 16 | #define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1) |
| 16 | #define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2) | 17 | #define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2) |
| 17 | 18 | ||
| 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 | |||
| 18 | #endif /* _UAPI_LINUX_PIDFD_H */ | 32 | #endif /* _UAPI_LINUX_PIDFD_H */ |