Received: by 2002:ac0:bc90:0:0:0:0:0 with SMTP id a16csp3394038img; Mon, 25 Mar 2019 09:23:09 -0700 (PDT) X-Google-Smtp-Source: APXvYqzalF1DP8qfKQd/POaGrry+GdPxC1Sieji6wAeVjZRp2qqqn0ZSYy2MUwJQYO3WiIHPrIVL X-Received: by 2002:a63:d1f:: with SMTP id c31mr1268862pgl.353.1553530989382; Mon, 25 Mar 2019 09:23:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1553530989; cv=none; d=google.com; s=arc-20160816; b=TnRTrdM+SF4eRueNvDZUZTz4tAJ+2MFFjOwUoFPLe7LMfwODpNdDLm2AA+s4fqm+Gn gpEYTWAY4bLHkvUmoxnrHhfZ7BogoiKGxMS4s8hpGqonQrBjCtXk1ZZDiY+dDjyQGc+Q iqSCGXLbLVyVYvGBIaaEr7oZHPmQg9hWCd6FWMP796wAbByZNGRyrihT1bjnyr9rtSVI QtBONcfGSmba9gXdezg7lA5wEs5OZ4pzQbvfPcSSzMXvFrooCYPIr8Bf2Fucfz2x8jU1 wFcD05ZC8a2cAbfXArhQO/jItwYCYjG+YM8Uce+DdSfSfKVL5yreqQwnchpVnIOWplae XFbg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=YcYRr3aHjxSmLDiO5dyizKGgYRee5X6BiecmOVGxx80=; b=XvS0kiZbq3AA//azSzatFZMUe42wQYJyPq6sZkeRIfEGrhumB8CevRxyLXg/C1HOYl L1Rq+3qm+OWaAq05yFCVRaR4PAVdFZdc1hBxwqFhqexDH83Iejbt7b+GhkM6DT5zlPcU eWrwaI8RBx6RnHkK+udOQxlefYLShNWrgRvgeW3n154fyWrqrchjU8nyI6Jv3c3NTDzr FRZk8z7VSXn4wGtHajkxmfXLpCSxVu1c2dwp0F5odoOG0qwdI6J95uGJqibxVvFsYSLz FRTUWsZnm65+2caF5KcPTeehzWymGxmwQCn/xEwgpw2BXMYwssD1hdIP37HMj5WpTpX1 y5cw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@brauner.io header.s=google header.b=PiIpOga8; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i10si13766195pfj.186.2019.03.25.09.22.54; Mon, 25 Mar 2019 09:23:09 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@brauner.io header.s=google header.b=PiIpOga8; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729911AbfCYQVk (ORCPT + 99 others); Mon, 25 Mar 2019 12:21:40 -0400 Received: from mail-ed1-f68.google.com ([209.85.208.68]:39557 "EHLO mail-ed1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726203AbfCYQVU (ORCPT ); Mon, 25 Mar 2019 12:21:20 -0400 Received: by mail-ed1-f68.google.com with SMTP id p20so7610642eds.6 for ; Mon, 25 Mar 2019 09:21:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brauner.io; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=YcYRr3aHjxSmLDiO5dyizKGgYRee5X6BiecmOVGxx80=; b=PiIpOga8rF1yLHEmWPOx8cqSdChEDxZnmr+mZCUudjlH7rKD6+8RQoON+Pr5c3WeZK dy6Wb0Faa6jNLLI1Jbx4zbXwGUxXZmvFclN/u9OHZ/Leuui64xfZS/UuEew0xF4BzTp7 XWsyiWS+TAszEkrldsT973HFG8MoB6HmB1eFvmjX4j+bJ4LlNigI1Zz6oZ+IzXOcBLH1 W3yo+tHm+DiM9nbK2MmkzYrsXq3VFgZMGblF87cknct3o2ViUetFZhq40OZGslKUFl3J Mk3klG0RumwhuhOqVwZxVcdKdQjOsBFiQihHT8UueKL6ot8NQA5pgRDh8Xt2GsK0oU6P zJ3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=YcYRr3aHjxSmLDiO5dyizKGgYRee5X6BiecmOVGxx80=; b=J4aAHK6r3Q4dpDOvNz2oMkh4OltDadlR9Vnj+KRsEl6pJ7hh3GEbniwB9JmExAM4Ym +1W+DMhvslHCgnq39ICW5n+jANxI7/xvDnEyoDT9JxxO6TWEocggjaljaaawgCParyrG VTZdRmUR+lhAPvntPwa8aJgrbAulb6bhw3UHY8fw93V2dDCPYdaF0uyU7jeTmIhsGKA9 i7DFaOOq1EQvk23VlDQSEIeW+egufrSzXQGC1ZjyM3B3aJEa4jhnfe+6YemjRHeCiZeD DXOY81ZvzfWsW74MgtcGYqDpm7KS45wrcChIZzV5NJgjDId4Nv6vOCB9nZt/A0f0ZUIy xAxA== X-Gm-Message-State: APjAAAVTRweS14WvyWXq+EI+vSZ5UQnLuMLPAgxVorghwLAp9rjJmaJ/ tOjjodncvsDA/Cv4AvH7xj8m0Q== X-Received: by 2002:a50:ac04:: with SMTP id v4mr17038948edc.255.1553530877258; Mon, 25 Mar 2019 09:21:17 -0700 (PDT) Received: from localhost.localdomain ([193.96.224.244]) by smtp.gmail.com with ESMTPSA id e45sm5027759edd.3.2019.03.25.09.21.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 25 Mar 2019 09:21:16 -0700 (PDT) From: Christian Brauner To: jannh@google.com, khlebnikov@yandex-team.ru, luto@kernel.org, dhowells@redhat.com, serge@hallyn.com, ebiederm@xmission.com, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org Cc: arnd@arndb.de, keescook@chromium.org, adobriyan@gmail.com, tglx@linutronix.de, mtk.manpages@gmail.com, bl0pbl33p@gmail.com, ldv@altlinux.org, akpm@linux-foundation.org, oleg@redhat.com, nagarathnam.muthusamy@oracle.com, cyphar@cyphar.com, viro@zeniv.linux.org.uk, joel@joelfernandes.org, dancol@google.com, Christian Brauner Subject: [PATCH 2/4] pid: add pidctl() Date: Mon, 25 Mar 2019 17:20:50 +0100 Message-Id: <20190325162052.28987-3-christian@brauner.io> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190325162052.28987-1-christian@brauner.io> References: <20190325162052.28987-1-christian@brauner.io> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The pidctl() syscalls builds on, extends, and improves translate_pid() [4]. I quote Konstantins original patchset first that has already been acked and picked up by Eric before and whose functionality is preserved in this syscall: "Each process have different pids, one for each pid namespace it belongs. When interaction happens within single pid-ns translation isn't required. More complicated scenarios needs special handling. For example: - reading pid-files or logs written inside container with pid namespace - writing logs with internal pids outside container for pushing them into - attaching with ptrace to tasks from different pid namespace Generally speaking, any cross pid-ns API with pids needs translation. Currently there are several interfaces that could be used here: Pid namespaces are identified by device and inode of /proc/[pid]/ns/pid. Pids for nested pid namespaces are shown in file /proc/[pid]/status. In some cases pid translation could be easily done using this information. Backward translation requires scanning all tasks and becomes really complicated for deeper namespace nesting. Unix socket automatically translates pid attached to SCM_CREDENTIALS. This requires CAP_SYS_ADMIN for sending arbitrary pids and entering into pid namespace, this expose process and could be insecure." The original patchset allowed two distinct operations implicitly: - discovering whether pid namespaces (pidns) have a parent-child relationship - translating a pid from a source pidns into a target pidns Both tasks are accomplished in the original patchset by passing a pid along. If the pid argument is passed as 1 the relationship between two pid namespaces can be discovered. The syscall will gain a lot clearer syntax and will be easier to use for userspace if the task it is asked to perform is passed through a command argument. Additionally, it allows us to remove an intrinsic race caused by using the pid argument as a way to discover the relationship between pid namespaces. This patch introduces three commands: /* PIDCMD_QUERY_PID */ PIDCMD_QUERY_PID allows to translate a pid between pid namespaces. Given a source pid namespace fd return the pid of the process in the target namespace: 1. pidctl(PIDCMD_QUERY_PID, pid, source_fd, -1, 0) - retrieve pidns identified by source_fd - retrieve struct pid identifed by pid in pidns identified by source_fd - retrieve callers pidns - return pid in callers pidns 2. pidctl(PIDCMD_QUERY_PID, pid, -1, target_fd, 0) - retrieve callers pidns - retrieve struct pid identifed by pid in callers pidns - retrieve pidns identified by target_fd - return pid in pidns identified by target_fd 3. pidctl(PIDCMD_QUERY_PID, 1, source_fd, -1, 0) - retrieve pidns identified by source_fd - retrieve struct pid identifed by init task in pidns identified by source_fd - retrieve callers pidns - return pid of init task of pidns identified by source_fd in callers pidns 4. pidctl(PIDCMD_QUERY_PID, pid, source_fd, target_fd, 0) - retrieve pidns identified by source_fd - retrieve struct pid identifed by pid in pidns identified by source_fd - retrieve pidns identified by target_fd - check whether struct pid can be found in pidns identified by target_fd - return pid in pidns identified by target_fd /* PIDCMD_QUERY_PIDNS */ PIDCMD_QUERY_PIDNS allows to determine the relationship between pid namespaces. In the original version of the pachset passing pid as 1 would allow to deterimine the relationship between the pid namespaces. This is inherhently racy. If pid 1 inside a pid namespace has died it would report false negatives. For example, if pid 1 inside of the target pid namespace already died, it would report that the target pid namespace cannot be reached from the source pid namespace because it couldn't find the pid inside of the target pid namespace and thus falsely report to the user that the two pid namespaces are not related. This problem is simple to avoid. In the new version we simply walk the list of ancestors and check whether the namespace are related to each other. By doing it this way we can reliably report what the relationship between two pid namespace file descriptors looks like. 1. pidctl(PIDCMD_QUERY_PIDNS, 0, ns_fd1, ns_fd1, 0) == 0 - pidns_of(ns_fd1) and pidns_of(ns_fd2) are unrelated to each other 2. pidctl(PIDCMD_QUERY_PIDNS, 0, ns_fd1, ns_fd2, 0) == 1 - pidns_of(ns_fd1) == pidns_of(ns_fd2) 3. pidctl(PIDCMD_QUERY_PIDNS, 0, ns_fd1, ns_fd2, 0) == 2 - pidns_of(ns_fd1) is ancestor of pidns_of(ns_fd2) 4. pidctl(PIDCMD_QUERY_PIDNS, 0, ns_fd1, ns_fd2, 0) == 3 - pidns_of(ns_fd2) is ancestor of pidns_of(ns_fd1) These two commands - PIDCMD_QUERY_PID and PIDCMD_QUERY_PIDNS - cover and improve the functionality expressed implicitly in translate_pid() before. /* PIDCMD_GET_PIDFD */ This command allows to retrieve file descriptors for processes and removes the dependency of pidfds and thereby the pidfd_send_signal() syscall on procfs. First, multiple people have expressed a desire to do this even when pidfd_send_signal() was merged. It is even recorded in the commit message for pidfd_send_signal() itself (cf. commit 3eb39f47934f9d5a3027fe00d906a45fe3a15fad): Q-06: (Andrew Morton) Is there a cleaner way of obtaining the fd? Another syscall perhaps. A-06: Userspace can already trivially retrieve file descriptors from procfs so this is something that we will need to support anyway. Hence, there's no immediate need to add another syscalls just to make pidfd_send_signal() not dependent on the presence of procfs. However, adding a syscalls to get such file descriptors is planned for a future patchset). Alexey made a similar request (cf. [2]). Additionally, Andy made an additional, idependent argument that we should go forward with non-proc-dirfd file descriptors for the sake of security and extensibility (cf. [3]). The pidfds are not associated with a specific pid namespaces but rather only with struct pid. What the pidctl() syscall enforces is that when a caller wants to retrieve a pidfd file descriptor for a pid in a given target pid namespace the caller needs to proof it has access to a pid namespace in which the pid he wants a pidfd for a) exists and b) that this pid namespace is an ancestor of the target pid namespace, i.e. that the pid can be translated into the target pid namespaces. 1. pidctl(PIDCMD_GET_PIDFD, pid, source_fd, -1, PIDCTL_CLOEXEC) - retrieve pidns identified by source_fd - retrieve struct pid identifed by pid in pidns identified by source_fd - retrieve callers pidns - return pidfd 2. pidctl(PIDCMD_GET_PIDFD, pid, -1, target_fd, PIDCTL_CLOEXEC) - retrieve callers pidns - retrieve struct pid identifed by pid in callers pidns - retrieve pidns identified by target_fd - return pidfd 3. pidctl(PIDCMD_GET_PIDFD, 1, source_fd, -1, PIDCTL_CLOEXEC) - retrieve pidns identified by source_fd - retrieve struct pid identifed by init task in pidns identified by source_fd - retrieve callers pidns - return pidfd 4. pidctl(PIDCMD_GET_PIDFD, pid, source_fd, target_fd, PIDCTL_CLOEXEC) - retrieve pidns identified by source_fd - retrieve struct pid identifed by pid in pidns identified by source_fd - retrieve pidns identified by target_fd - check whether struct pid can be found in pidns identified by target_fd - return pidfd These pidfds are allocated using anon_inode_getfd() and can be used with the pidfd_send_signal() syscall. They are not dirfds and as such have the advantage that we can make them pollable or readable in the future if we see a need to do so (which I'm pretty sure we will eventually). Currently they do not support any advanced operations. /* References */ [1]: https://lore.kernel.org/lkml/20181228233725.722tdfgijxcssg76@brauner.io/ [2]: https://lore.kernel.org/lkml/20190320203910.GA2842@avx2/ [3]: https://lore.kernel.org/lkml/CALCETrXO=V=+qEdLDVPf8eCgLZiB9bOTrUfe0V-U-tUZoeoRDA@mail.gmail.com/ [4]: https://lore.kernel.org/lkml/20181109034919.GA21681@altlinux.org/ Signed-off-by: Christian Brauner Signed-off-by: Konstantin Khlebnikov Signed-off-by: David Howells Acked-by: Serge Hallyn Cc: Arnd Bergmann Cc: "Eric W. Biederman" Cc: Kees Cook Cc: Alexey Dobriyan Cc: Thomas Gleixner Cc: Jann Horn Cc: Konstantin Khlebnikov Cc: Jonathan Kowalski Cc: "Dmitry V. Levin" Cc: Andy Lutomirsky Cc: Andrew Morton Cc: Oleg Nesterov Cc: Nagarathnam Muthusamy Cc: Aleksa Sarai Cc: Al Viro --- arch/x86/entry/syscalls/syscall_32.tbl | 1 + arch/x86/entry/syscalls/syscall_64.tbl | 1 + include/linux/pid.h | 2 + include/linux/pid_namespace.h | 8 ++ include/linux/syscalls.h | 2 + include/uapi/linux/wait.h | 17 +++ kernel/pid.c | 162 +++++++++++++++++++++++++ kernel/pid_namespace.c | 25 ++++ 8 files changed, 218 insertions(+) diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index 1f9607ed087c..afc5a8997140 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -433,3 +433,4 @@ 425 i386 io_uring_setup sys_io_uring_setup __ia32_sys_io_uring_setup 426 i386 io_uring_enter sys_io_uring_enter __ia32_sys_io_uring_enter 427 i386 io_uring_register sys_io_uring_register __ia32_sys_io_uring_register +428 i386 pidctl sys_pidctl __ia32_sys_pidctl diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 92ee0b4378d4..8ef25d9a003d 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -349,6 +349,7 @@ 425 common io_uring_setup __x64_sys_io_uring_setup 426 common io_uring_enter __x64_sys_io_uring_enter 427 common io_uring_register __x64_sys_io_uring_register +428 common pidctl __x64_sys_pidctl # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/include/linux/pid.h b/include/linux/pid.h index b6f4ba16065a..3c8ef5a199ca 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -66,6 +66,8 @@ struct pid extern struct pid init_struct_pid; +extern const struct file_operations pidfd_fops; + static inline struct pid *get_pid(struct pid *pid) { if (pid) diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 49538b172483..c5aae8151287 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -64,6 +64,8 @@ extern struct pid_namespace *copy_pid_ns(unsigned long flags, extern void zap_pid_ns_processes(struct pid_namespace *pid_ns); extern int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd); extern void put_pid_ns(struct pid_namespace *ns); +extern int pidnscmp(struct pid_namespace *ancestor, + struct pid_namespace *descendant); #else /* !CONFIG_PID_NS */ #include @@ -94,6 +96,12 @@ static inline int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd) { return 0; } + +static inline int pidnscmp(struct pid_namespace *ancestor, + struct pid_namespace *descendant) +{ + return 0; +} #endif /* CONFIG_PID_NS */ extern struct pid_namespace *task_active_pid_ns(struct task_struct *tsk); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e446806a561f..a4c8c59f7c8f 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -929,6 +929,8 @@ asmlinkage long sys_clock_adjtime32(clockid_t which_clock, struct old_timex32 __user *tx); asmlinkage long sys_syncfs(int fd); asmlinkage long sys_setns(int fd, int nstype); +asmlinkage long sys_pidctl(unsigned int cmd, pid_t pid, int source, int target, + unsigned int flags); asmlinkage long sys_sendmmsg(int fd, struct mmsghdr __user *msg, unsigned int vlen, unsigned flags); asmlinkage long sys_process_vm_readv(pid_t pid, diff --git a/include/uapi/linux/wait.h b/include/uapi/linux/wait.h index ac49a220cf2a..4f3dfb528b20 100644 --- a/include/uapi/linux/wait.h +++ b/include/uapi/linux/wait.h @@ -18,5 +18,22 @@ #define P_PID 1 #define P_PGID 2 +/* Commands to pass to pidctl() */ +enum pidcmd { + PIDCMD_QUERY_PID = 0, /* Get pid in target pid namespace */ + PIDCMD_QUERY_PIDNS = 1, /* Determine relationship between pid namespaces */ + PIDCMD_GET_PIDFD = 2, /* Get pidfd for a process */ +}; + +/* Return values of PIDCMD_QUERY_PIDNS */ +enum pidcmd_query_pidns { + PIDNS_UNRELATED = 0, /* The pid namespaces are unrelated */ + PIDNS_EQUAL = 1, /* The pid namespaces are equal */ + PIDNS_SOURCE_IS_ANCESTOR = 2, /* Source pid namespace is ancestor of target pid namespace */ + PIDNS_TARGET_IS_ANCESTOR = 3, /* Target pid namespace is ancestor of source pid namespace */ +}; + +/* Flags to pass to pidctl() */ +#define PIDCTL_CLOEXEC 1 #endif /* _UAPI_LINUX_WAIT_H */ diff --git a/kernel/pid.c b/kernel/pid.c index 20881598bdfa..b3702bd38bdd 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -26,6 +26,7 @@ * */ +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include struct pid init_struct_pid = { .count = ATOMIC_INIT(1), @@ -451,6 +453,166 @@ struct pid *find_ge_pid(int nr, struct pid_namespace *ns) return idr_get_next(&ns->idr, &nr); } +static int pidfd_release(struct inode *inode, struct file *file) +{ + struct pid *pid = file->private_data; + + if (pid) { + file->private_data = NULL; + put_pid(pid); + } + + return 0; +} + +const struct file_operations pidfd_fops = { + .release = pidfd_release, + .llseek = no_llseek, +}; + +static int pidfd_create_fd(struct pid *pid, unsigned int o_flags) +{ + int fd; + + fd = anon_inode_getfd("pidfd", &pidfd_fops, get_pid(pid), + O_RDWR | o_flags); + if (fd < 0) + put_pid(pid); + + return fd; +} + +static struct pid_namespace *get_pid_ns_by_fd(int fd) +{ + struct pid_namespace *pidns = ERR_PTR(-EINVAL); + + if (fd >= 0) { +#ifdef CONFIG_PID_NS + struct ns_common *ns; + struct file *file = proc_ns_fget(fd); + if (IS_ERR(file)) + return ERR_CAST(file); + + ns = get_proc_ns(file_inode(file)); + if (ns->ops->type == CLONE_NEWPID) + pidns = get_pid_ns( + container_of(ns, struct pid_namespace, ns)); + + fput(file); +#endif + } else { + pidns = task_active_pid_ns(current); + } + + return pidns; +} + +static int pidns_related(struct pid_namespace *source, + struct pid_namespace *target) +{ + int query; + + query = pidnscmp(source, target); + switch (query) { + case 0: + return PIDNS_EQUAL; + case 1: + return PIDNS_SOURCE_IS_ANCESTOR; + } + + query = pidnscmp(target, source); + if (query == 1) + return PIDNS_TARGET_IS_ANCESTOR; + + return PIDNS_UNRELATED; +} + +/* + * pidctl - perform operations on pids + * @cmd: command to execute + * @pid: pid for translation + * @source: pid-ns file descriptor or -1 for active namespace + * @target: pid-ns file descriptor or -1 for active namesapce + * @flags: flags to pass + * + * If cmd is PIDCMD_QUERY_PID translates pid between pid namespaces + * identified by @source and @target. Returns pid if process has pid in + * @target, 0 if process has no pid in @target, -ESRCH if process does + * not have a pid in @source. + * + * If cmd is PIDCMD_QUERY_PIDNS determines the relations between two pid + * namespaces. Returns 2 if @source is an ancestor pid namespace + * of @target, 1 if @source and @target refer to the same pid namespace, + * 3 if @target is an ancestor pid namespace of @source, 0 if they have + * no parent-child relationship in either direction. + * + * If cmd is PIDCMD_GET_PIDFD returns pidfd for process in @target pid + * namespace. Returns pidfd if process has pid in @target, -ESRCH if + * process does not have a pid in @source, -ENOENT if process does not have a + * pid in @target pid namespace. + * + */ +SYSCALL_DEFINE5(pidctl, unsigned int, cmd, pid_t, pid, int, source, int, target, + unsigned int, flags) +{ + struct pid_namespace *source_ns = NULL, *target_ns = NULL; + struct pid *struct_pid; + pid_t result; + + switch (cmd) { + case PIDCMD_QUERY_PIDNS: + if (pid != 0) + return -EINVAL; + pid = 1; + /* fall through */ + case PIDCMD_QUERY_PID: + if (flags != 0) + return -EINVAL; + break; + case PIDCMD_GET_PIDFD: + if (flags & ~PIDCTL_CLOEXEC) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + source_ns = get_pid_ns_by_fd(source); + result = PTR_ERR(source_ns); + if (IS_ERR(source_ns)) + goto err_source; + + target_ns = get_pid_ns_by_fd(target); + result = PTR_ERR(target_ns); + if (IS_ERR(target_ns)) + goto err_target; + + if (cmd == PIDCMD_QUERY_PIDNS) { + result = pidns_related(source_ns, target_ns); + } else { + rcu_read_lock(); + struct_pid = find_pid_ns(pid, source_ns); + result = struct_pid ? pid_nr_ns(struct_pid, target_ns) : -ESRCH; + rcu_read_unlock(); + + if (cmd == PIDCMD_GET_PIDFD) { + int cloexec = (flags & PIDCTL_CLOEXEC) ? O_CLOEXEC : 0; + if (result > 0) + result = pidfd_create_fd(struct_pid, cloexec); + else if (result == 0) + result = -ENOENT; + } + } + + if (target) + put_pid_ns(target_ns); +err_target: + if (source) + put_pid_ns(source_ns); +err_source: + return result; +} + void __init pid_idr_init(void) { /* Verify no one has done anything silly: */ diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index aa6e72fb7c08..1c863fb3d55a 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -429,6 +429,31 @@ static struct ns_common *pidns_get_parent(struct ns_common *ns) return &get_pid_ns(pid_ns)->ns; } +/** + * pidnscmp - Determine if @ancestor is ancestor of @descendant + * @ancestor: pidns suspected to be the ancestor of @descendant + * @descendant: pidns suspected to be the descendant of @ancestor + * + * Returns -1 if @ancestor is not an ancestor of @descendant, + * 0 if @ancestor is the same pidns as @descendant, 1 if @ancestor + * is an ancestor of @descendant. + */ +int pidnscmp(struct pid_namespace *ancestor, struct pid_namespace *descendant) +{ + if (ancestor == descendant) + return 0; + + for (;;) { + if (!descendant) + return -1; + if (descendant == ancestor) + break; + descendant = descendant->parent; + } + + return 1; +} + static struct user_namespace *pidns_owner(struct ns_common *ns) { return to_pid_ns(ns)->user_ns; -- 2.21.0