Thread (38 messages) 38 messages, 4 authors, 2021-06-03

Re: [PATCH 5/5] fanotify: Add pidfd info record support to the fanotify API

From: Amir Goldstein <amir73il@gmail.com>
Date: 2021-05-20 13:44:04
Also in: linux-fsdevel

On Thu, May 20, 2021 at 11:17 AM Christian Brauner
[off-list ref] wrote:
On Thu, May 20, 2021 at 12:11:51PM +1000, Matthew Bobrowski wrote:
quoted
Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which
allows userspace applications to control whether a pidfd info record
containing a pidfd is to be returned with each event.

If FAN_REPORT_PIDFD is enabled for a notification group, an additional
struct fanotify_event_info_pidfd object will be supplied alongside the
generic struct fanotify_event_metadata within a single event. This
functionality is analogous to that of FAN_REPORT_FID in terms of how
the event structure is supplied to the 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 follow any struct fanotify_event_info_fid object.

Usage of FAN_REPORT_TID is not permitted with FAN_REPORT_PIDFD as the
pidfd API only supports the creation of pidfds for thread-group
leaders. Attempting to do so will result with a -EINVAL being returned
when calling fanotify_init(2).

If pidfd creation fails via pidfd_create(), the pidfd field within
struct fanotify_event_info_pidfd is set to FAN_NOPIDFD.

Signed-off-by: Matthew Bobrowski <repnop@google.com>
---
 fs/notify/fanotify/fanotify_user.c | 65 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  3 +-
 include/uapi/linux/fanotify.h      | 12 ++++++
 3 files changed, 74 insertions(+), 6 deletions(-)
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 1e15f3222eb2..bba61988f4a0 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -106,6 +106,8 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
 #define FANOTIFY_EVENT_ALIGN 4
 #define FANOTIFY_FID_INFO_HDR_LEN \
      (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)
 {
@@ -141,6 +143,9 @@ static int fanotify_event_info_len(unsigned int info_mode,
      if (fh_len)
              info_len += fanotify_fid_info_len(fh_len, dot_len);

+     if (info_mode & FAN_REPORT_PIDFD)
+             info_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
+
      return info_len;
 }
@@ -401,6 +406,29 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid,
      return info_len;
 }

+static int copy_pidfd_info_to_user(struct pid *pid,
+                                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_create(pid, 0);
+     if (info.pidfd < 0)
+             info.pidfd = FAN_NOPIDFD;
+
+     if (copy_to_user(buf, &info, info_len))
+             return -EFAULT;
Hm, well this kinda sucks. The caller can end up with a pidfd in their
fd table and when the copy_to_user() failed they won't know what fd it
Good catch!
But I prefer to solve it differently, because moving fd_install() to the
end of this function does not guarantee that copy_event_to_user()
won't return an error one day with dangling pidfd in fd table.

It might be simpler to do pidfd_create() next to create_fd() in
copy_event_to_user() and pass pidfd to copy_pidfd_info_to_user().
pidfd can be closed on error along with fd on out_close_fd label.

You also forgot to add CAP_SYS_ADMIN check before pidfd_create()
(even though fanotify_init() does check for that).

Anyway, something like:

        if (!capable(CAP_SYS_ADMIN) &&
            task_tgid(current) != event->pid)
                metadata.pid = 0;
+      else if (pidfd_mode)
+              pidfd = pidfd_create(pid, 0);

[...]

+       if (pidfd_mode)
+               ret = copy_pidfd_info_to_user(pidfd, buf, count);

        return metadata.event_len;

out_close_fd:
+        if (pidfd != FAN_NOPIDFD) {
...


And in any case, it wrong to call copy_pidfd_info_to_user() from
copy_info_to_user(). It needs to be called once from copy_event_to_user()
because copy_pidfd_info_to_user() may be called twice to report both
FAN_EVENT_INFO_TYPE_FID and FAN_EVENT_INFO_TYPE_DFID
records for the same event.

Thanks,
Amir.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help