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

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

From: Matthew Bobrowski <hidden>
Date: 2021-05-21 09:22:23
Also in: linux-fsdevel

Hey Amir/Christian,

On Thu, May 20, 2021 at 04:43:48PM +0300, Amir Goldstein wrote:
On Thu, May 20, 2021 at 11:17 AM Christian Brauner
[off-list ref] wrote:
quoted
quoted
+#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!
Super awesome catch Christian, thanks pulling this up!
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.
I can see the angle you're approaching this from...
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).
I didn't really understand the need for this check here given that the
administrative bits are already being checked for in fanotify_init()
i.e. FAN_REPORT_PIDFD can never be set for an unprivileged listener;
thus never walking any of the pidfd_mode paths. Is this just a defense
in depth approach here, or is it something else that I'm missing?
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) {
...
The early call to pidfd_create() and clean up in copy_event_to_user()
makes most sense to me.
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.
Right, as mentioned in patch 4 of this series, copy_info_to_user() has
been repurposed to account for the double call into
copy_fid_info_to_user() when reporting FAN_EVENT_INFO_TYPE_FID and
FAN_EVENT_INFO_TYPE_DFID.

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