From patchwork Fri Dec 20 23:28:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11306521 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 23091139A for ; Fri, 20 Dec 2019 23:28:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F2C7C2082E for ; Fri, 20 Dec 2019 23:28:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="uK3AhW1c" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726674AbfLTX2E (ORCPT ); Fri, 20 Dec 2019 18:28:04 -0500 Received: from mail-io1-f66.google.com ([209.85.166.66]:43731 "EHLO mail-io1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726648AbfLTX2D (ORCPT ); Fri, 20 Dec 2019 18:28:03 -0500 Received: by mail-io1-f66.google.com with SMTP id n21so9469451ioo.10 for ; Fri, 20 Dec 2019 15:28:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=CX/EMM8AnWfaI4rNz03pFAf5OWqiTpv8JaYTV9Mz/lc=; b=uK3AhW1cGSB2r++u32XTz9HtiQf9DqoAJjZTRWoiXrjhVWY6c5A2GADwJdSXWsp2/X FsGsLqdPVE3e+/7ZjXCDprcZpUcC7yZ4UWf6Jr6Czg9nUCF0j6YtxlOzYegNvAokHGMa IvrQIWpnkjoATVBm197EtU7IuXBoy83PBzK4c= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=CX/EMM8AnWfaI4rNz03pFAf5OWqiTpv8JaYTV9Mz/lc=; b=jip96FGr52inF9vbWMBzq+GPMCpXhINwVtZCsRcp1/2Q8L0ZDOiRFYyXBqGNZyKIPg jI/S0Zp1Aor+BhxqOYFLqHsM8hywXPFK8ZxvDlA3BPPMI4mpfHZwzMZ25I5FtTQ5Gxqc AVJT1VRIDHpB3kSibTsCdxdpLmHAX6qOy0WYanpOh0WFof6YbGQ94nZmYQXzjcqjlKeN 36a2S99nc4fxAKKcIn4ovdLSJdxeL21z5cV7AWhyJ+GeXlfe5MZ7CiV2nYPf/9spj9Df yiJYzX1SXr/2WOt/gG/sUC+g8Dmke8DSwExKO72e0qgywpqxyI1QGPJ8JCVy5fmd7F4z KEMw== X-Gm-Message-State: APjAAAWo6uE1pBQ0hB1kf80hEhqR5vlO+glCd/so3y/KN75n5eELlpv+ +f98VSgxB5m9g+zu5WYabJoKGw== X-Google-Smtp-Source: APXvYqxsP0bwleRoHecpmcs1c/KtuGVrDwDug1CwLOoHCIMnBUjjBhwWd5nxifsPFV+UuKhkDPnVNg== X-Received: by 2002:a5e:924c:: with SMTP id z12mr12049921iop.296.1576884482976; Fri, 20 Dec 2019 15:28:02 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id a12sm3941787ion.73.2019.12.20.15.28.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 20 Dec 2019 15:28:02 -0800 (PST) Date: Fri, 20 Dec 2019 23:28:01 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk, gpascutto@mozilla.com, ealvarez@mozilla.com, fweimer@redhat.com, jld@mozilla.com, arnd@arndb.de Subject: [PATCH v5 1/3] vfs, fdtable: Add get_task_file helper Message-ID: <20191220232758.GA20224@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This introduces a function which can be used to fetch a file, given an arbitrary task. As long as the user holds a reference (refcnt) to the task_struct it is safe to call, and will either return NULL on failure, or a pointer to the file, with a refcnt. Signed-off-by: Sargun Dhillon --- fs/file.c | 22 ++++++++++++++++++++-- include/linux/file.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/fs/file.c b/fs/file.c index 2f4fcf985079..0ceeb046f4f3 100644 --- a/fs/file.c +++ b/fs/file.c @@ -706,9 +706,9 @@ void do_close_on_exec(struct files_struct *files) spin_unlock(&files->file_lock); } -static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs) +static struct file *__fget_files(struct files_struct *files, unsigned int fd, + fmode_t mask, unsigned int refs) { - struct files_struct *files = current->files; struct file *file; rcu_read_lock(); @@ -729,6 +729,11 @@ static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs) return file; } +static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs) +{ + return __fget_files(current->files, fd, mask, refs); +} + struct file *fget_many(unsigned int fd, unsigned int refs) { return __fget(fd, FMODE_PATH, refs); @@ -746,6 +751,19 @@ struct file *fget_raw(unsigned int fd) } EXPORT_SYMBOL(fget_raw); +struct file *fget_task(struct task_struct *task, unsigned int fd) +{ + struct file *file = NULL; + + task_lock(task); + if (task->files) + file = __fget_files(task->files, fd, 0, 1); + + task_unlock(task); + + return file; +} + /* * Lightweight file lookup - no refcnt increment if fd table isn't shared. * diff --git a/include/linux/file.h b/include/linux/file.h index 3fcddff56bc4..c6c7b24ea9f7 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -16,6 +16,7 @@ extern void fput(struct file *); extern void fput_many(struct file *, unsigned int); struct file_operations; +struct task_struct; struct vfsmount; struct dentry; struct inode; @@ -47,6 +48,7 @@ static inline void fdput(struct fd fd) extern struct file *fget(unsigned int fd); extern struct file *fget_many(unsigned int fd, unsigned int refs); extern struct file *fget_raw(unsigned int fd); +extern struct file *fget_task(struct task_struct *task, unsigned int fd); extern unsigned long __fdget(unsigned int fd); extern unsigned long __fdget_raw(unsigned int fd); extern unsigned long __fdget_pos(unsigned int fd); From patchwork Fri Dec 20 23:28:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11306523 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A1E94921 for ; Fri, 20 Dec 2019 23:28:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 61A25218AC for ; Fri, 20 Dec 2019 23:28:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="UChnFr9Z" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726752AbfLTX2Q (ORCPT ); Fri, 20 Dec 2019 18:28:16 -0500 Received: from mail-io1-f65.google.com ([209.85.166.65]:43758 "EHLO mail-io1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726637AbfLTX2Q (ORCPT ); Fri, 20 Dec 2019 18:28:16 -0500 Received: by mail-io1-f65.google.com with SMTP id n21so9469806ioo.10 for ; Fri, 20 Dec 2019 15:28:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=kxHJTJjVCyhCiL727OkEZRZ43OQtsu2M3mcaBbYHQdc=; b=UChnFr9ZV27wTdAuMUPmfv43PiMUhXrdziHHrL+VNiSNSjzKdwMDrqxPXXuFHUP8Eq 64nUoCssNpp3PFlSwO39jWdyY5O/jiMF8ORIZw3rRgKOYQNGAtEyCQpt82s+sHN2c/ue ai5qPz5cDl/cOLJ24XKPf6Pm1n8tdDPbY+xgc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=kxHJTJjVCyhCiL727OkEZRZ43OQtsu2M3mcaBbYHQdc=; b=nWniRdDu4axQhNi/TWJO1WNtjV1K/JK+B8JQe88jAGfUKQb6iI9xtqE+m2GNEOle9b zgmZKgMuhDZSLpawKZ2onK1mYyj5Ar42wVuLBJsoHu1ogOYtu90NgyRmH/rcOQN1QqQ/ 0P8Z0JnWw3EWX7XzaVws3OBxiArCKifgQ3rsJ9WnXunFONpPmA07KI3M/iATjS32HkD2 BQwS5eqtmgid8ajVhGVBkMm4+WcasXrdK2+2nyOOPmNBNHoQkE/vd4xd/smP37775ZD9 GWafCuZhu33UeFnxXiM01wi1vjZc6Qh8LUqd5Lu/V3umix99pMHJcTOqi8zOzM6BlHUs JYkA== X-Gm-Message-State: APjAAAUZvIAaRoBWR7PuaoUqgdUbUF/NjG+bYFpvIjTeH+l+ODTsx+hB oMzeRWpbNnw3ghitd+ca6cZQ2A== X-Google-Smtp-Source: APXvYqxD2FoyucWeZRo6/bolFQQTOfeKLw+vXC5BtUQBBHIEZZNtgoiDpa+5REsgnEZCfgshkHrzYg== X-Received: by 2002:a6b:b297:: with SMTP id b145mr12410260iof.19.1576884495205; Fri, 20 Dec 2019 15:28:15 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id s10sm3914224ioc.4.2019.12.20.15.28.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 20 Dec 2019 15:28:14 -0800 (PST) Date: Fri, 20 Dec 2019 23:28:13 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk, gpascutto@mozilla.com, ealvarez@mozilla.com, fweimer@redhat.com, jld@mozilla.com, arnd@arndb.de Subject: [PATCH v5 2/3] pid: Introduce pidfd_getfd syscall Message-ID: <20191220232810.GA20233@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org 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 along with using 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. For example, it can be used to bind a socket to a privileged port. /* prototype */ /* * pidfd_getfd_options is an extensible struct which can have options * added to it. If options is NULL, size, and it will be ignored be * ignored, otherwise, size should be set to sizeof(*options). If * option is newer than the current kernel version, E2BIG will be * returned. */ struct pidfd_getfd_options {}; long pidfd_getfd(int pidfd, int fd, unsigned int flags, struct pidfd_getfd_options *options, size_t size); /* testing */ Ran self-test suite on x86_64 Signed-off-by: Sargun Dhillon Reported-by: kbuild test robot --- MAINTAINERS | 1 + arch/alpha/kernel/syscalls/syscall.tbl | 1 + arch/arm/tools/syscall.tbl | 1 + arch/arm64/include/asm/unistd.h | 2 +- arch/arm64/include/asm/unistd32.h | 2 + arch/ia64/kernel/syscalls/syscall.tbl | 1 + arch/m68k/kernel/syscalls/syscall.tbl | 1 + arch/microblaze/kernel/syscalls/syscall.tbl | 1 + arch/mips/kernel/syscalls/syscall_n32.tbl | 1 + arch/mips/kernel/syscalls/syscall_n64.tbl | 1 + arch/mips/kernel/syscalls/syscall_o32.tbl | 1 + arch/parisc/kernel/syscalls/syscall.tbl | 1 + arch/powerpc/kernel/syscalls/syscall.tbl | 1 + arch/s390/kernel/syscalls/syscall.tbl | 1 + arch/sh/kernel/syscalls/syscall.tbl | 1 + arch/sparc/kernel/syscalls/syscall.tbl | 1 + arch/x86/entry/syscalls/syscall_32.tbl | 1 + arch/x86/entry/syscalls/syscall_64.tbl | 1 + arch/xtensa/kernel/syscalls/syscall.tbl | 1 + include/linux/syscalls.h | 4 + include/uapi/asm-generic/unistd.h | 3 +- include/uapi/linux/pidfd.h | 10 ++ kernel/pid.c | 115 ++++++++++++++++++++ 23 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 include/uapi/linux/pidfd.h diff --git a/MAINTAINERS b/MAINTAINERS index cc0a4a8ae06a..bc370ff59dbf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13014,6 +13014,7 @@ M: Christian Brauner L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git +F: include/uapi/linux/pidfd.h F: samples/pidfd/ F: tools/testing/selftests/pidfd/ F: tools/testing/selftests/clone3/ diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl index 8e13b0b2928d..d1cac0d657b7 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl @@ -475,3 +475,4 @@ 543 common fspick sys_fspick 544 common pidfd_open sys_pidfd_open # 545 reserved for clone3 +548 common pidfd_getfd sys_pidfd diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index 6da7dc4d79cc..ba045e2f3a60 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -449,3 +449,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 2629a68b8724..b722e47377a5 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -38,7 +38,7 @@ #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) -#define __NR_compat_syscalls 436 +#define __NR_compat_syscalls 439 #endif #define __ARCH_WANT_SYS_CLONE diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index 94ab29cf4f00..a8da97a2de41 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -879,6 +879,8 @@ __SYSCALL(__NR_fspick, sys_fspick) __SYSCALL(__NR_pidfd_open, sys_pidfd_open) #define __NR_clone3 435 __SYSCALL(__NR_clone3, sys_clone3) +#define __NR_pidfd_getfd 438 +__SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) /* * Please add new compat syscalls above this comment and update diff --git a/arch/ia64/kernel/syscalls/syscall.tbl b/arch/ia64/kernel/syscalls/syscall.tbl index 36d5faf4c86c..2b11adfc860c 100644 --- a/arch/ia64/kernel/syscalls/syscall.tbl +++ b/arch/ia64/kernel/syscalls/syscall.tbl @@ -356,3 +356,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl index a88a285a0e5f..44e879e98459 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl @@ -435,3 +435,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl index 09b0cd7dab0a..7afa00125cc4 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl @@ -441,3 +441,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index e7c5ab38e403..856d5ba34461 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -374,3 +374,4 @@ 433 n32 fspick sys_fspick 434 n32 pidfd_open sys_pidfd_open 435 n32 clone3 __sys_clone3 +438 n32 pidfd_getfd sys_pidfd_getfd diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index 13cd66581f3b..2db6075352f3 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -350,3 +350,4 @@ 433 n64 fspick sys_fspick 434 n64 pidfd_open sys_pidfd_open 435 n64 clone3 __sys_clone3 +438 n64 pidfd_getfd sys_pidfd_getfd diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index 353539ea4140..e9f9d4a9b105 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -423,3 +423,4 @@ 433 o32 fspick sys_fspick 434 o32 pidfd_open sys_pidfd_open 435 o32 clone3 __sys_clone3 +438 o32 pidfd_getfd sys_pidfd_getfd diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index 285ff516150c..c58c7eb144ca 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -433,3 +433,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3_wrapper +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl index 43f736ed47f2..707609bfe3ea 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl @@ -517,3 +517,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 nospu clone3 ppc_clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl index 3054e9c035a3..185cd624face 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl @@ -438,3 +438,4 @@ 433 common fspick sys_fspick sys_fspick 434 common pidfd_open sys_pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 sys_clone3 +438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl index b5ed26c4c005..88f90895aad8 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl @@ -438,3 +438,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl index 8c8cc7537fb2..218df6a2326e 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl @@ -481,3 +481,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index 15908eb9b17e..9c3101b65e0f 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -440,3 +440,4 @@ 433 i386 fspick sys_fspick __ia32_sys_fspick 434 i386 pidfd_open sys_pidfd_open __ia32_sys_pidfd_open 435 i386 clone3 sys_clone3 __ia32_sys_clone3 +438 i386 pidfd_getfd sys_pidfd_getfd __ia32_sys_pidfd_getfd diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index c29976eca4a8..cef85db75a62 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -357,6 +357,7 @@ 433 common fspick __x64_sys_fspick 434 common pidfd_open __x64_sys_pidfd_open 435 common clone3 __x64_sys_clone3/ptregs +438 common pidfd_getfd __x64_sys_pidfd_getfd # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl index 25f4de729a6d..ae15183def12 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl @@ -406,3 +406,4 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 2960dedcfde8..62fe706329d1 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -69,6 +69,7 @@ struct rseq; union bpf_attr; struct io_uring_params; struct clone_args; +struct pidfd_getfd_options; #include #include @@ -1000,6 +1001,9 @@ asmlinkage long sys_fspick(int dfd, const char __user *path, unsigned int flags) asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, siginfo_t __user *info, unsigned int flags); +asmlinkage long sys_pidfd_getfd(int pidfd, int fd, + struct pidfd_getfd_options __user *options, + size_t, usize); /* * Architecture-specific system calls diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 1fc8faa6e973..f358488366f6 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -850,9 +850,10 @@ __SYSCALL(__NR_pidfd_open, sys_pidfd_open) #define __NR_clone3 435 __SYSCALL(__NR_clone3, sys_clone3) #endif +#define __NR_pidfd_getfd 438 #undef __NR_syscalls -#define __NR_syscalls 436 +#define __NR_syscalls 439 /* * 32 bit systems traditionally used different diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h new file mode 100644 index 000000000000..0a3fc922661d --- /dev/null +++ b/include/uapi/linux/pidfd.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_PIDFD_H +#define _UAPI_LINUX_PIDFD_H + +struct pidfd_getfd_options {}; + +#define PIDFD_GETFD_OPTIONS_SIZE_VER0 0 +#define PIDFD_GETFD_OPTIONS_SIZE_LATEST PIDFD_GETFD_OPTIONS_SIZE_VER0 + +#endif /* _UAPI_LINUX_PIDFD_H */ diff --git a/kernel/pid.c b/kernel/pid.c index 2278e249141d..2a9cb4be383f 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -42,6 +42,7 @@ #include #include #include +#include struct pid init_struct_pid = { .count = REFCOUNT_INIT(1), @@ -578,3 +579,117 @@ void __init pid_idr_init(void) init_pid_ns.pid_cachep = KMEM_CACHE(pid, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT); } + +static struct file *__pidfd_getfd_fget_task(struct task_struct *task, u32 fd) +{ + struct file *file; + int ret; + + ret = mutex_lock_killable(&task->signal->cred_guard_mutex); + if (ret) + return ERR_PTR(ret); + + if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS)) { + file = ERR_PTR(-EPERM); + goto out; + } + + file = fget_task(task, fd); + if (!file) + file = ERR_PTR(-EBADF); + +out: + mutex_unlock(&task->signal->cred_guard_mutex); + return file; +} + +static long pidfd_getfd(struct pid *pid, u32 fd) +{ + struct task_struct *task; + struct file *file; + int ret, retfd; + + task = get_pid_task(pid, PIDTYPE_PID); + if (!task) + return -ESRCH; + + file = __pidfd_getfd_fget_task(task, fd); + put_task_struct(task); + if (IS_ERR(file)) + return PTR_ERR(file); + + retfd = get_unused_fd_flags(O_CLOEXEC); + if (retfd < 0) { + ret = retfd; + goto out; + } + + /* + * security_file_receive must come last since it may have side effects + * and cannot be reversed. + */ + ret = security_file_receive(file); + if (ret) + goto out_put_fd; + + fd_install(retfd, file); + return retfd; + +out_put_fd: + put_unused_fd(retfd); +out: + fput(file); + return ret; +} + +/** + * sys_pidfd_getfd() - Get a file descriptor from another process + * + * @pidfd: file descriptor of the process + * @fd: the file descriptor number to get + * @options: options on how to get the fd + * @usize: the size of options + * + * This syscall requires that the process has the ability to ptrace the + * process represented by the pidfd. It will return a duplicated version + * of the file descriptor on success. The process who which is having + * its file descriptor taken is otherwise unaffected. If options is NULL + * it is ignored along with usize. + * + * Return: On success, a file descriptor with cloexec is returned. + * On error, a negative errno number will be returned. + */ +SYSCALL_DEFINE4(pidfd_getfd, int, pidfd, int, fd, + struct pidfd_getfd_options __user *, options, size_t, usize) +{ + struct pid *pid; + struct fd f; + int ret; + + BUILD_BUG_ON(sizeof(struct pidfd_getfd_options) != PIDFD_GETFD_OPTIONS_SIZE_LATEST); + + /* + * options is currently unused, verify it's unset or if it is set, + * ensure that size is 0. + * + * In the future, this will need to adopt copy_struct_from_user. + */ + if (options && usize > PIDFD_GETFD_OPTIONS_SIZE_VER0) + return -E2BIG; + + f = fdget(pidfd); + if (!f.file) + return -EBADF; + + pid = pidfd_pid(f.file); + if (IS_ERR(pid)) { + ret = PTR_ERR(pid); + goto out; + } + + ret = pidfd_getfd(pid, fd); + +out: + fdput(f); + return ret; +} From patchwork Fri Dec 20 23:28:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sargun Dhillon X-Patchwork-Id: 11306525 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B1EB8921 for ; Fri, 20 Dec 2019 23:28:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7C52C21D7E for ; Fri, 20 Dec 2019 23:28:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=sargun.me header.i=@sargun.me header.b="JJ80/QRs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726670AbfLTX21 (ORCPT ); Fri, 20 Dec 2019 18:28:27 -0500 Received: from mail-io1-f67.google.com ([209.85.166.67]:46919 "EHLO mail-io1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726741AbfLTX21 (ORCPT ); Fri, 20 Dec 2019 18:28:27 -0500 Received: by mail-io1-f67.google.com with SMTP id t26so11026981ioi.13 for ; Fri, 20 Dec 2019 15:28:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sargun.me; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=ny/1+b/coBMqT4XFcKfzetECP8mHU6SfHjIxB2tIfuo=; b=JJ80/QRsaKZ49ZUuazuy11OThgF1rsybPSWO4Bl42XJFlCZ9pa84YjSyxcYlY48Z2H 0MchM2KPhLpK+WZD6QGLfqItuw3MuLsqc9IrWRG5RvMaJKlhfdHQa/p7aBT2NLb8eWET DLwFL8YISQtT7/o0rLPQlx4dl2FnthBPknN7g= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=ny/1+b/coBMqT4XFcKfzetECP8mHU6SfHjIxB2tIfuo=; b=FRkBQ/+/9qwylsvjPD0ZZrfEZFBbqCxYdQTmrvmByGnQXQdpSM+enwS4e4rqyxx6vI +lYES/6mcc060CcF0fLPt9Um6jORGlcS/zkhltRKOdt65qpEOfo+so9phUQ4KKltuJDw IvGNr6I3mS96pd2kBmng3iXJw5F7xQdFY5fKhKVy+cQkW9TYSeHei0AuFx3LA4u3jyE/ kaCE9quoF1HB1GUVnky7uKvtBE+SOREfJcvm3y1qE16V6jDHH4teRLKmfKTMrTsssAtv 3tMF/gwazmKK/r39ortDisJiJ+hkkxE4DRayd7s/ry/BEAxVeAr9SpD8NdvHjOlY5Hby j1kQ== X-Gm-Message-State: APjAAAVciqodw5bGgQM/hIOfKRo5IElJZAGRsuzDYs2AI1aP4wLNFhsN cNavBghMV918oaaPXa64Q8SfxA== X-Google-Smtp-Source: APXvYqy3bAGnwYfNbgzqQqAmxsNE47um0yrVgqnsdghb/mbUfWPZNbO4KDu2JI8gOA08Mg0O/6bSMg== X-Received: by 2002:a02:85e8:: with SMTP id d95mr14368360jai.92.1576884506238; Fri, 20 Dec 2019 15:28:26 -0800 (PST) Received: from ircssh-2.c.rugged-nimbus-611.internal (80.60.198.104.bc.googleusercontent.com. [104.198.60.80]) by smtp.gmail.com with ESMTPSA id c8sm5543254ilh.58.2019.12.20.15.28.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 20 Dec 2019 15:28:25 -0800 (PST) Date: Fri, 20 Dec 2019 23:28:24 +0000 From: Sargun Dhillon To: linux-kernel@vger.kernel.org, containers@lists.linux-foundation.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: tycho@tycho.ws, jannh@google.com, cyphar@cyphar.com, christian.brauner@ubuntu.com, oleg@redhat.com, luto@amacapital.net, viro@zeniv.linux.org.uk, gpascutto@mozilla.com, ealvarez@mozilla.com, fweimer@redhat.com, jld@mozilla.com, arnd@arndb.de Subject: [PATCH v5 3/3] test: Add test for pidfd getfd Message-ID: <20191220232822.GA20242@ircssh-2.c.rugged-nimbus-611.internal> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds four tests: * Fetch FD, and then compare via kcmp * Read data from FD to make sure it works * Make sure getfd can be blocked by blocking ptrace_may_access * Making sure fetching bad FDs fails * Make sure trying to pass too large of an options struct returns E2BIG Signed-off-by: Sargun Dhillon --- tools/testing/selftests/pidfd/.gitignore | 1 + tools/testing/selftests/pidfd/Makefile | 2 +- .../selftests/pidfd/pidfd_getfd_test.c | 262 ++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/pidfd/pidfd_getfd_test.c diff --git a/tools/testing/selftests/pidfd/.gitignore b/tools/testing/selftests/pidfd/.gitignore index 8d069490e17b..3a779c084d96 100644 --- a/tools/testing/selftests/pidfd/.gitignore +++ b/tools/testing/selftests/pidfd/.gitignore @@ -2,3 +2,4 @@ pidfd_open_test pidfd_poll_test pidfd_test pidfd_wait +pidfd_getfd_test diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile index 43db1b98e845..75a545861375 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only CFLAGS += -g -I../../../../usr/include/ -pthread -TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test pidfd_poll_test pidfd_wait +TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test pidfd_poll_test pidfd_wait pidfd_getfd_test include ../lib.mk diff --git a/tools/testing/selftests/pidfd/pidfd_getfd_test.c b/tools/testing/selftests/pidfd/pidfd_getfd_test.c new file mode 100644 index 000000000000..e53dacad8d8c --- /dev/null +++ b/tools/testing/selftests/pidfd/pidfd_getfd_test.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pidfd.h" +#include "../kselftest.h" + +#define WELL_KNOWN_CHILD_FD 100 +#define UNKNOWN_FD 111 +#define SECRET_MESSAGE "secret" + +static int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, + unsigned long idx2) +{ + return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2); +} + +static int pidfd_getfd(int pidfd, int fd) +{ + struct pidfd_getfd_options options = {}; + + return syscall(__NR_pidfd_getfd, pidfd, fd, &options, sizeof(options)); +} + +static int child(bool disable_ptrace, int sk) +{ + char buf[1024]; + int ret, fd; + + ret = prctl(PR_SET_PDEATHSIG, SIGKILL); + if (ret) + ksft_exit_fail_msg("%s: Child could not set DEATHSIG\n", + strerror(errno)); + + fd = syscall(SYS_memfd_create, "test", 0); + if (fd < 0) + ksft_exit_fail_msg("%s: Child could not create memfd\n", + strerror(errno)); + + ret = write(fd, SECRET_MESSAGE, sizeof(SECRET_MESSAGE)); + if (ret < 0) + ksft_exit_fail_msg("%s: Child could not write secret message\n", + strerror(errno)); + + ret = dup2(fd, WELL_KNOWN_CHILD_FD); + if (ret < 0) + ksft_exit_fail_msg("%s: Could not dup fd into well-known FD\n", + strerror(errno)); + + ret = close(fd); + if (ret < 0) + ksft_exit_fail_msg("%s: Child could close old fd\n", + strerror(errno)); + + if (disable_ptrace) { + ret = prctl(PR_SET_DUMPABLE, 0); + if (ret < 0) + ksft_exit_fail_msg("%s: Child failed to disable ptrace\n", + strerror(errno)); + } + ret = send(sk, "L", 1, 0); + if (ret < 0) + ksft_exit_fail_msg("%s: Child failed to send launched message\n", + strerror(errno)); + if (ret == 0) + ksft_exit_fail_msg("Failed to send launch message; other side is closed\n"); + + close(sk); + pause(); + + return EXIT_SUCCESS; +} + +static int start_child(bool disable_ptrace, pid_t *childpid) +{ + int pidfd, ret, sk_pair[2]; + char buf[1]; + + if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) + ksft_exit_fail_msg("%s: failed to create socketpair\n", + strerror(errno)); + *childpid = fork(); + if (*childpid < 0) + ksft_exit_fail_msg("%s: failed to fork a child process\n", + strerror(errno)); + + if (*childpid == 0) + exit(child(disable_ptrace, sk_pair[1])); + + close(sk_pair[1]); + + pidfd = sys_pidfd_open(*childpid, 0); + if (pidfd < 0) + ksft_exit_fail_msg("%s: failed to pidfd_open\n", + strerror(errno)); + + ret = recv(sk_pair[0], &buf, 1, 0); + if (ret < 0) + ksft_exit_fail_msg("%s: failed read from launch socket\n", + strerror(errno)); + if (ret == 0) + ksft_exit_fail_msg("Failed to read from launch socket, child failed\n"); + + return pidfd; +} + +static void test_kcmp_and_fetch_fd(void) +{ + char buf[sizeof(SECRET_MESSAGE)]; + int fd, pidfd, ret; + pid_t child_pid; + + pidfd = start_child(false, &child_pid); + + fd = pidfd_getfd(pidfd, WELL_KNOWN_CHILD_FD); + if (fd < 0) + ksft_exit_fail_msg("%s: getfd failed\n", strerror(errno)); + + ret = kcmp(getpid(), child_pid, KCMP_FILE, fd, WELL_KNOWN_CHILD_FD); + if (ret != 0) + ksft_exit_fail_msg("Our FD not equal to child FD\n"); + + ksft_test_result_pass("kcmp\n"); + + ret = lseek(fd, 0, SEEK_SET); + if (ret < 0) + ksft_exit_fail_msg("%s: seek failed\n", strerror(errno)); + if (ret != 0) + ksft_exit_fail_msg("%d: unexpected seek position\n", ret); + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) + ksft_exit_fail_msg("%s: failed to read secret message\n", + strerror(errno)); + + if (strncmp(SECRET_MESSAGE, buf, sizeof(buf)) != 0) + ksft_exit_fail_msg("%s: Secret message not correct\n", buf); + + ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); + close(pidfd); + if (ret < 0) + ksft_exit_fail_msg("%s: failed to send kill to child\n", + strerror(errno)); + + ksft_test_result_pass("fetch_and_read\n"); +} + +static void test_no_ptrace(void) +{ + int fd, pidfd, ret, uid; + pid_t child_pid; + + /* turn into nobody if we're root, to avoid CAP_SYS_PTRACE */ + uid = getuid(); + if (uid == 0) + seteuid(USHRT_MAX); + + pidfd = start_child(true, &child_pid); + + fd = pidfd_getfd(pidfd, WELL_KNOWN_CHILD_FD); + if (fd != -1) + ksft_exit_fail_msg("%s: getfd succeeded when ptrace blocked\n", + strerror(errno)); + if (errno != EPERM) + ksft_exit_fail_msg("%s: getfd did not get EPERM\n", + strerror(errno)); + + ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); + close(pidfd); + if (ret < 0) + ksft_exit_fail_msg("%s: failed to send kill to child\n", + strerror(errno)); + + if (uid == 0) + seteuid(0); + + ksft_test_result_pass("no_ptrace\n"); +} + +static void test_unknown_fd(void) +{ + int fd, pidfd, ret; + pid_t child_pid; + + pidfd = start_child(false, &child_pid); + + fd = pidfd_getfd(pidfd, UNKNOWN_FD); + if (fd != -1) + ksft_exit_fail_msg("%s: getfd succeeded when fetching unknown FD\n", + strerror(errno)); + if (errno != EBADF) + ksft_exit_fail_msg("%s: getfd did not get EBADF\n", + strerror(errno)); + + ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); + close(pidfd); + if (ret < 0) + ksft_exit_fail_msg("%s: failed to send kill to child\n", + strerror(errno)); + + ksft_test_result_pass("unknown_fd\n"); +} + +static void test_e2big(void) +{ + struct pidfd_getfd_options *options; + int ret, allocation_size; + + allocation_size = sizeof(*options) + 1; + options = malloc(allocation_size); + if (!options) + ksft_exit_fail_msg("%s: Unable to allocate memory\n", + strerror(errno)); + + errno = 0; + ret = syscall(__NR_pidfd_getfd, 0, 0, options, allocation_size + 1); + if (ret != -1) + ksft_exit_fail_msg("getfd succeeded with invalid options\n"); + if (errno != E2BIG) + ksft_exit_fail_msg("%s: getfd did not get E2BIG\n", + strerror(errno)); + free(options); + + ksft_test_result_pass("e2big\n"); +} + +int main(int argc, char **argv) +{ + char buf[sizeof(SECRET_MESSAGE)]; + int ret, status, fd, pidfd; + pid_t child_pid; + + ksft_print_header(); + ksft_set_plan(5); + + test_kcmp_and_fetch_fd(); + test_unknown_fd(); + test_no_ptrace(); + test_e2big(); + + return ksft_exit_pass(); +}