Skip to content

Commit 8649c32

Browse files
sargunChristian Brauner
authored andcommitted
pid: Implement pidfd_getfd syscall
This syscall allows for the retrieval of file descriptors from other processes, based on their pidfd. This is possible using ptrace, and injection of parasitic code to inject code which leverages SCM_RIGHTS to move file descriptors between a tracee and a tracer. Unfortunately, ptrace comes with a high cost of requiring the process to be stopped, and breaks debuggers. This does not require stopping the process under manipulation. One reason to use this is to allow sandboxers to take actions on file descriptors on the behalf of another process. For example, this can be combined with seccomp-bpf's user notification to do on-demand fd extraction and take privileged actions. One such privileged action is binding a socket to a privileged port. /* prototype */ /* flags is currently reserved and should be set to 0 */ int sys_pidfd_getfd(int pidfd, int fd, unsigned int flags); /* testing */ Ran self-test suite on x86_64 Signed-off-by: Sargun Dhillon <[email protected]> Acked-by: Christian Brauner <[email protected]> Reviewed-by: Arnd Bergmann <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 5e876fb commit 8649c32

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

kernel/pid.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,93 @@ void __init pid_idr_init(void)
578578
init_pid_ns.pid_cachep = KMEM_CACHE(pid,
579579
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
580580
}
581+
582+
static struct file *__pidfd_fget(struct task_struct *task, int fd)
583+
{
584+
struct file *file;
585+
int ret;
586+
587+
ret = mutex_lock_killable(&task->signal->cred_guard_mutex);
588+
if (ret)
589+
return ERR_PTR(ret);
590+
591+
if (ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
592+
file = fget_task(task, fd);
593+
else
594+
file = ERR_PTR(-EPERM);
595+
596+
mutex_unlock(&task->signal->cred_guard_mutex);
597+
598+
return file ?: ERR_PTR(-EBADF);
599+
}
600+
601+
static int pidfd_getfd(struct pid *pid, int fd)
602+
{
603+
struct task_struct *task;
604+
struct file *file;
605+
int ret;
606+
607+
task = get_pid_task(pid, PIDTYPE_PID);
608+
if (!task)
609+
return -ESRCH;
610+
611+
file = __pidfd_fget(task, fd);
612+
put_task_struct(task);
613+
if (IS_ERR(file))
614+
return PTR_ERR(file);
615+
616+
ret = security_file_receive(file);
617+
if (ret) {
618+
fput(file);
619+
return ret;
620+
}
621+
622+
ret = get_unused_fd_flags(O_CLOEXEC);
623+
if (ret < 0)
624+
fput(file);
625+
else
626+
fd_install(ret, file);
627+
628+
return ret;
629+
}
630+
631+
/**
632+
* sys_pidfd_getfd() - Get a file descriptor from another process
633+
*
634+
* @pidfd: the pidfd file descriptor of the process
635+
* @fd: the file descriptor number to get
636+
* @flags: flags on how to get the fd (reserved)
637+
*
638+
* This syscall gets a copy of a file descriptor from another process
639+
* based on the pidfd, and file descriptor number. It requires that
640+
* the calling process has the ability to ptrace the process represented
641+
* by the pidfd. The process which is having its file descriptor copied
642+
* is otherwise unaffected.
643+
*
644+
* Return: On success, a cloexec file descriptor is returned.
645+
* On error, a negative errno number will be returned.
646+
*/
647+
SYSCALL_DEFINE3(pidfd_getfd, int, pidfd, int, fd,
648+
unsigned int, flags)
649+
{
650+
struct pid *pid;
651+
struct fd f;
652+
int ret;
653+
654+
/* flags is currently unused - make sure it's unset */
655+
if (flags)
656+
return -EINVAL;
657+
658+
f = fdget(pidfd);
659+
if (!f.file)
660+
return -EBADF;
661+
662+
pid = pidfd_pid(f.file);
663+
if (IS_ERR(pid))
664+
ret = PTR_ERR(pid);
665+
else
666+
ret = pidfd_getfd(pid, fd);
667+
668+
fdput(f);
669+
return ret;
670+
}

0 commit comments

Comments
 (0)