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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, May 20, 2021 at 11:17 AM Christian Brauner
<christian.brauner@xxxxxxxxxx> wrote:
>
> On Thu, May 20, 2021 at 12:11:51PM +1000, Matthew Bobrowski wrote:
> > 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@xxxxxxxxxx>
> > ---
> >  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.



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux