Commit af579beb authored by Matthew Bobrowski's avatar Matthew Bobrowski Committed by Jan Kara

fanotify: add pidfd support to the fanotify API

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which
allows userspace applications to control whether a pidfd information
record containing a pidfd is to be returned alongside the generic
event metadata for each event.

If FAN_REPORT_PIDFD is enabled for a notification group, an additional
struct fanotify_event_info_pidfd object type will be supplied
alongside the generic struct fanotify_event_metadata for a single
event. This functionality is analogous to that of FAN_REPORT_FID in
terms of how the event structure is supplied to a userspace
application. Usage of FAN_REPORT_PIDFD with
FAN_REPORT_FID/FAN_REPORT_DFID_NAME is permitted, and in this case a
struct fanotify_event_info_pidfd object will likely follow any struct
fanotify_event_info_fid object.

Currently, the usage of the FAN_REPORT_TID flag is not permitted along
with FAN_REPORT_PIDFD as the pidfd API currently only supports the
creation of pidfds for thread-group leaders. Additionally, usage of
the FAN_REPORT_PIDFD flag is limited to privileged processes only
i.e. event listeners that are running with the CAP_SYS_ADMIN
capability. Attempting to supply the FAN_REPORT_TID initialization
flags with FAN_REPORT_PIDFD or creating a notification group without
CAP_SYS_ADMIN will result with -EINVAL being returned to the caller.

In the event of a pidfd creation error, there are two types of error
values that can be reported back to the listener. There is
FAN_NOPIDFD, which will be reported in cases where the process
responsible for generating the event has terminated prior to the event
listener being able to read the event. Then there is FAN_EPIDFD, which
will be reported when a more generic pidfd creation error has occurred
when fanotify calls pidfd_create().

Link: https://lore.kernel.org/r/5f9e09cff7ed62bfaa51c1369e0f7ea5f16a91aa.1628398044.git.repnop@google.comSigned-off-by: default avatarMatthew Bobrowski <repnop@google.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 0aca67bb
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/fanotify.h> #include <linux/fanotify.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/fdtable.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/anon_inodes.h> #include <linux/anon_inodes.h>
...@@ -106,6 +107,8 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly; ...@@ -106,6 +107,8 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
#define FANOTIFY_EVENT_ALIGN 4 #define FANOTIFY_EVENT_ALIGN 4
#define FANOTIFY_FID_INFO_HDR_LEN \ #define FANOTIFY_FID_INFO_HDR_LEN \
(sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle)) (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
#define FANOTIFY_PIDFD_INFO_HDR_LEN \
sizeof(struct fanotify_event_info_pidfd)
static int fanotify_fid_info_len(int fh_len, int name_len) static int fanotify_fid_info_len(int fh_len, int name_len)
{ {
...@@ -138,6 +141,9 @@ static int fanotify_event_info_len(unsigned int info_mode, ...@@ -138,6 +141,9 @@ static int fanotify_event_info_len(unsigned int info_mode,
dot_len = 1; dot_len = 1;
} }
if (info_mode & FAN_REPORT_PIDFD)
info_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
if (fh_len) if (fh_len)
info_len += fanotify_fid_info_len(fh_len, dot_len); info_len += fanotify_fid_info_len(fh_len, dot_len);
...@@ -401,13 +407,34 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, ...@@ -401,13 +407,34 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
return info_len; return info_len;
} }
static int copy_pidfd_info_to_user(int pidfd,
char __user *buf,
size_t count)
{
struct fanotify_event_info_pidfd info = { };
size_t info_len = FANOTIFY_PIDFD_INFO_HDR_LEN;
if (WARN_ON_ONCE(info_len > count))
return -EFAULT;
info.hdr.info_type = FAN_EVENT_INFO_TYPE_PIDFD;
info.hdr.len = info_len;
info.pidfd = pidfd;
if (copy_to_user(buf, &info, info_len))
return -EFAULT;
return info_len;
}
static int copy_info_records_to_user(struct fanotify_event *event, static int copy_info_records_to_user(struct fanotify_event *event,
struct fanotify_info *info, struct fanotify_info *info,
unsigned int info_mode, unsigned int info_mode, int pidfd,
char __user *buf, size_t count) char __user *buf, size_t count)
{ {
int ret, total_bytes = 0, info_type = 0; int ret, total_bytes = 0, info_type = 0;
unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS; unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS;
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
/* /*
* Event info records order is as follows: dir fid + name, child fid. * Event info records order is as follows: dir fid + name, child fid.
...@@ -478,6 +505,16 @@ static int copy_info_records_to_user(struct fanotify_event *event, ...@@ -478,6 +505,16 @@ static int copy_info_records_to_user(struct fanotify_event *event,
total_bytes += ret; total_bytes += ret;
} }
if (pidfd_mode) {
ret = copy_pidfd_info_to_user(pidfd, buf, count);
if (ret < 0)
return ret;
buf += ret;
count -= ret;
total_bytes += ret;
}
return total_bytes; return total_bytes;
} }
...@@ -489,8 +526,9 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -489,8 +526,9 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
struct path *path = fanotify_event_path(event); struct path *path = fanotify_event_path(event);
struct fanotify_info *info = fanotify_event_info(event); struct fanotify_info *info = fanotify_event_info(event);
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
struct file *f = NULL; struct file *f = NULL;
int ret, fd = FAN_NOFD; int ret, pidfd = FAN_NOPIDFD, fd = FAN_NOFD;
pr_debug("%s: group=%p event=%p\n", __func__, group, event); pr_debug("%s: group=%p event=%p\n", __func__, group, event);
...@@ -524,6 +562,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -524,6 +562,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
} }
metadata.fd = fd; metadata.fd = fd;
if (pidfd_mode) {
/*
* Complain if the FAN_REPORT_PIDFD and FAN_REPORT_TID mutual
* exclusion is ever lifted. At the time of incoporating pidfd
* support within fanotify, the pidfd API only supported the
* creation of pidfds for thread-group leaders.
*/
WARN_ON_ONCE(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
/*
* The PIDTYPE_TGID check for an event->pid is performed
* preemptively in an attempt to catch out cases where the event
* listener reads events after the event generating process has
* already terminated. Report FAN_NOPIDFD to the event listener
* in those cases, with all other pidfd creation errors being
* reported as FAN_EPIDFD.
*/
if (metadata.pid == 0 ||
!pid_has_task(event->pid, PIDTYPE_TGID)) {
pidfd = FAN_NOPIDFD;
} else {
pidfd = pidfd_create(event->pid, 0);
if (pidfd < 0)
pidfd = FAN_EPIDFD;
}
}
ret = -EFAULT; ret = -EFAULT;
/* /*
* Sanity check copy size in case get_one_event() and * Sanity check copy size in case get_one_event() and
...@@ -545,7 +610,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -545,7 +610,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
fd_install(fd, f); fd_install(fd, f);
if (info_mode) { if (info_mode) {
ret = copy_info_records_to_user(event, info, info_mode, ret = copy_info_records_to_user(event, info, info_mode, pidfd,
buf, count); buf, count);
if (ret < 0) if (ret < 0)
goto out_close_fd; goto out_close_fd;
...@@ -558,6 +623,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -558,6 +623,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
put_unused_fd(fd); put_unused_fd(fd);
fput(f); fput(f);
} }
if (pidfd >= 0)
close_fd(pidfd);
return ret; return ret;
} }
...@@ -1103,6 +1172,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) ...@@ -1103,6 +1172,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
#endif #endif
return -EINVAL; return -EINVAL;
/*
* A pidfd can only be returned for a thread-group leader; thus
* FAN_REPORT_PIDFD and FAN_REPORT_TID need to remain mutually
* exclusive.
*/
if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
return -EINVAL;
if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
return -EINVAL; return -EINVAL;
...@@ -1504,7 +1581,7 @@ static int __init fanotify_user_setup(void) ...@@ -1504,7 +1581,7 @@ static int __init fanotify_user_setup(void)
FANOTIFY_DEFAULT_MAX_USER_MARKS); FANOTIFY_DEFAULT_MAX_USER_MARKS);
BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10); BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9); BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
......
...@@ -27,7 +27,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */ ...@@ -27,7 +27,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
#define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME) #define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS) #define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD)
/* /*
* fanotify_init() flags that require CAP_SYS_ADMIN. * fanotify_init() flags that require CAP_SYS_ADMIN.
...@@ -37,6 +37,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */ ...@@ -37,6 +37,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
*/ */
#define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \ #define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \
FAN_REPORT_TID | \ FAN_REPORT_TID | \
FAN_REPORT_PIDFD | \
FAN_UNLIMITED_QUEUE | \ FAN_UNLIMITED_QUEUE | \
FAN_UNLIMITED_MARKS) FAN_UNLIMITED_MARKS)
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#define FAN_ENABLE_AUDIT 0x00000040 #define FAN_ENABLE_AUDIT 0x00000040
/* Flags to determine fanotify event format */ /* Flags to determine fanotify event format */
#define FAN_REPORT_PIDFD 0x00000080 /* Report pidfd for event->pid */
#define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */ #define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */
#define FAN_REPORT_FID 0x00000200 /* Report unique file id */ #define FAN_REPORT_FID 0x00000200 /* Report unique file id */
#define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */ #define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */
...@@ -123,6 +124,7 @@ struct fanotify_event_metadata { ...@@ -123,6 +124,7 @@ struct fanotify_event_metadata {
#define FAN_EVENT_INFO_TYPE_FID 1 #define FAN_EVENT_INFO_TYPE_FID 1
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2 #define FAN_EVENT_INFO_TYPE_DFID_NAME 2
#define FAN_EVENT_INFO_TYPE_DFID 3 #define FAN_EVENT_INFO_TYPE_DFID 3
#define FAN_EVENT_INFO_TYPE_PIDFD 4
/* Variable length info record following event metadata */ /* Variable length info record following event metadata */
struct fanotify_event_info_header { struct fanotify_event_info_header {
...@@ -148,6 +150,15 @@ struct fanotify_event_info_fid { ...@@ -148,6 +150,15 @@ struct fanotify_event_info_fid {
unsigned char handle[0]; unsigned char handle[0];
}; };
/*
* This structure is used for info records of type FAN_EVENT_INFO_TYPE_PIDFD.
* It holds a pidfd for the pid that was responsible for generating an event.
*/
struct fanotify_event_info_pidfd {
struct fanotify_event_info_header hdr;
__s32 pidfd;
};
struct fanotify_response { struct fanotify_response {
__s32 fd; __s32 fd;
__u32 response; __u32 response;
...@@ -160,6 +171,8 @@ struct fanotify_response { ...@@ -160,6 +171,8 @@ struct fanotify_response {
/* No fd set in event */ /* No fd set in event */
#define FAN_NOFD -1 #define FAN_NOFD -1
#define FAN_NOPIDFD FAN_NOFD
#define FAN_EPIDFD -2
/* Helper functions to deal with fanotify_event_metadata buffers */ /* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment