Skip to contentSkip to conversation

fanotify: Adding pidfd support to the fanotify API

PATCH In review Matthew Bobrowski 4 participants 35 messages Apr 15, 2021 – Apr 28, 2021

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover fanotify: Adding pidfd support to the fanotify API

Hey Jan/Amir/Christian,

This is the RFC patch series for adding pidfd support to the fanotify API.

tl;dr rather than returning a pid within struct fanotify_event_metadata, a pidfd is returned instead when fanotify has been explicitly told to do so via the new FAN_REPORT_PIDFD flag.

The main driver behind returning a pidfd within an event instead of a pid is that it permits userspace applications to better detect if they've possibly lost a TOCTOU race. A common paradigm among userspace applications that make use of the fanotify API is to crawl /proc/<pid> upon receiving an event. Userspace applications do this in order to further ascertain contextual meta-information about the process that was responsible for generating the filesystem event. On high pressure systems, where pid recycling isn't uncommon, it's no longer considered as a reliable approach to directly sift through /proc/<pid> and have userspace applications use the information contained within /proc/<pid> as it could, and does, lead to program execution inaccuracies.

Now when a pidfd is returned in an event, a userspace application can:

a) Obtain the pid responsible for generating the filesystem event from the pidfds fdinfo.

b) Detect whether the userspace application lost the procfs access race by sending a 0 signal on the pidfd and checking the return value. A -ESRCH is indicative of the userspace application losing the race, meaning that the pid has been recycled.

Matthew Bobrowski (2):
pidfd_create(): remove static qualifier and declare pidfd_create() in
linux/pid.h
fanotify: Add pidfd support to the fanotify API

 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/linux/pid.h                |  1 +
 include/uapi/linux/fanotify.h      |  2 ++
 kernel/pid.c                       |  2 +-
 5 files changed, 35 insertions(+), 5 deletions(-)
Signature · 3 lines
2.31.1.368.gbe11c130af-goog

/M
#1 lore raw

PATCH 1/2 pidfd_create(): remove static qualifier and declare pidfd_create() in linux/pid.h

With the idea to have fanotify(7) return pidfds within a struct fanotify_event_metadata, pidfd_create()'s scope is to increased so that it can be called from other subsystems within the Linux kernel. The current static qualifier from its definition is to be removed and a new function declaration for pidfd_create() is to be added to the linux/pid.h header file.

---
 include/linux/pid.h | 1 +
 kernel/pid.c        | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
include/linux/pid.h +1 -0
index fa10acb8d6a4..af308e15f174 100644
@@ -78,6 +78,7 @@ struct file;
 
 extern struct pid *pidfd_pid(const struct file *file);
 struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags);
+int pidfd_create(struct pid *pid, unsigned int flags);
 
 static inline struct pid *get_pid(struct pid *pid)
 {
kernel/pid.c +1 -1
index ebdf9c60cd0b..91c4b6891c15 100644
@@ -553,7 +553,7 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
  * Return: On success, a cloexec pidfd is returned.
  *         On error, a negative errno number will be returned.
  */
-static int pidfd_create(struct pid *pid, unsigned int flags)
+int pidfd_create(struct pid *pid, unsigned int flags)
 {
 	int fd;
 
Signature · 3 lines
2.31.1.368.gbe11c130af-goog

/M
maintainer
#2 lore raw

Re: [PATCH 1/2] pidfd_create(): remove static qualifier and declare pidfd_create() in linux/pid.h

Quoted 8 lines · Matthew Bobrowski

With the idea to have fanotify(7) return pidfds within a struct fanotify_event_metadata, pidfd_create()'s scope is to increased so that it can be called from other subsystems within the Linux kernel. The current static qualifier from its definition is to be removed and a new function declaration for pidfd_create() is to be added to the linux/pid.h header file.

I'm fine with this but it would be good to get explicit Christian's ack on this patch (I know he's already agreed with the intent in general).

Honza

Quoted 34 lines
---
 include/linux/pid.h | 1 +
 kernel/pid.c        | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
include/linux/pid.h +1 -0
index fa10acb8d6a4..af308e15f174 100644
@@ -78,6 +78,7 @@ struct file;
 
 extern struct pid *pidfd_pid(const struct file *file);
 struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags);
+int pidfd_create(struct pid *pid, unsigned int flags);
 
 static inline struct pid *get_pid(struct pid *pid)
 {
kernel/pid.c +1 -1
index ebdf9c60cd0b..91c4b6891c15 100644
@@ -553,7 +553,7 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
  * Return: On success, a cloexec pidfd is returned.
  *         On error, a negative errno number will be returned.
  */
-static int pidfd_create(struct pid *pid, unsigned int flags)
+int pidfd_create(struct pid *pid, unsigned int flags)
 {
 	int fd;
 
Signature · 3 lines
2.31.1.368.gbe11c130af-goog

/M
Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#3 lore raw

Re: [PATCH 1/2] pidfd_create(): remove static qualifier and declare pidfd_create() in linux/pid.h

In reply to Matthew Bobrowski
Quoted 30 lines · Matthew Bobrowski

With the idea to have fanotify(7) return pidfds within a struct fanotify_event_metadata, pidfd_create()'s scope is to increased so that it can be called from other subsystems within the Linux kernel. The current static qualifier from its definition is to be removed and a new function declaration for pidfd_create() is to be added to the linux/pid.h header file.

---
 include/linux/pid.h | 1 +
 kernel/pid.c        | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
include/linux/pid.h +1 -0
index fa10acb8d6a4..af308e15f174 100644
@@ -78,6 +78,7 @@ struct file;
 
 extern struct pid *pidfd_pid(const struct file *file);
 struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags);
+int pidfd_create(struct pid *pid, unsigned int flags);
 
 static inline struct pid *get_pid(struct pid *pid)
 {
kernel/pid.c +0 -0
index ebdf9c60cd0b..91c4b6891c15 100644

kernel/pid.c struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags) · line 557

Quoted 6 lines
kernel/pid.c +1 -1
@@ -553,7 +553,7 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
  * Return: On success, a cloexec pidfd is returned.
  *         On error, a negative errno number will be returned.
  */
-static int pidfd_create(struct pid *pid, unsigned int flags)
 
+int pidfd_create(struct pid *pid, unsigned int flags)

Can you please add a comment to the kernel doc mentioning that this helper is _not_ intended to be exported to modules? I don't want drivers to get the idea that it's ok to start returning pidfds from everywhere just yet.

And I think we should add sm like

	if (flags & ~(O_NONBLOCK | O_CLOEXEC | O_RDWR))
		return -EINVAL;

in pidfd_open() so future callers don't accidently create pidfds with random flags we don't support.

Christian

#4 lore raw

Re: [PATCH 1/2] pidfd_create(): remove static qualifier and declare pidfd_create() in linux/pid.h

Quoted 36 lines · Christian Brauner
Quoted 34 lines · Matthew Bobrowski

With the idea to have fanotify(7) return pidfds within a struct fanotify_event_metadata, pidfd_create()'s scope is to increased so that it can be called from other subsystems within the Linux kernel. The current static qualifier from its definition is to be removed and a new function declaration for pidfd_create() is to be added to the linux/pid.h header file.

---
 include/linux/pid.h | 1 +
 kernel/pid.c        | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
include/linux/pid.h +1 -0
index fa10acb8d6a4..af308e15f174 100644
@@ -78,6 +78,7 @@ struct file;
 
 extern struct pid *pidfd_pid(const struct file *file);
 struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags);
+int pidfd_create(struct pid *pid, unsigned int flags);
 
 static inline struct pid *get_pid(struct pid *pid)
 {
kernel/pid.c +0 -1
index ebdf9c60cd0b..91c4b6891c15 100644
@@ -553,7 +553,7 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
  * Return: On success, a cloexec pidfd is returned.
  *         On error, a negative errno number will be returned.
  */
-static int pidfd_create(struct pid *pid, unsigned int flags)
Quoted 6 lines

kernel/pid.c struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags) · line 556

Quoted 1 line
kernel/pid.c +1 -0
+int pidfd_create(struct pid *pid, unsigned int flags)

Can you please add a comment to the kernel doc mentioning that this helper is _not_ intended to be exported to modules? I don't want drivers to get the idea that it's ok to start returning pidfds from everywhere just yet.

Sure, this makes sense to me.

Quoted 7 lines

And I think we should add sm like

	if (flags & ~(O_NONBLOCK | O_CLOEXEC | O_RDWR))
		return -EINVAL;

in pidfd_open() so future callers don't accidently create pidfds with random flags we don't support.

In the context of exporting pidfd_create() to the rest of the kernel, presumably we should be adding this flag check to pidfd_create() and not pidfd_open(), right? I gather that's what you actually meant.

/M

#5 lore raw

PATCH 2/2 fanotify: Add pidfd support to the fanotify API

In reply to Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +30 -3
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {
+		/*
+		 * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+		 * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+		 * blindly lifted without pidfds for threads actually being
+		 * supported.
+		 */
+		WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+		pidfd = pidfd_create(event->pid, 0);
+		if (unlikely(pidfd < 0))
+			metadata.pid = FAN_NOPIDFD;
+		else
+			metadata.pid = pidfd;
+	} else {
+		metadata.pid = pid_vnr(event->pid);
+	}
 
 	if (path && path->mnt && path->dentry) {
 		fd = create_fd(group, path, &f);
@@ -941,6 +959,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 #endif
 		return -EINVAL;
 
+	/*
+	 * A pidfd can only be returned for a thread-group leader; thus
+	 * FAN_REPORT_TID and FAN_REPORT_PIDFD need to be mutually
+	 * exclusive. Once the pidfd API supports the creation of pidfds on
+	 * individual threads, then we can look at removing this conditional.
+	 */
+	if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
+		return -EINVAL;
+
 	if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
 		return -EINVAL;
 
@@ -1312,7 +1339,7 @@ SYSCALL32_DEFINE6(fanotify_mark,
  */
 static int __init fanotify_user_setup(void)
 {
-	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
+	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
 	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
 	fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
include/linux/fanotify.h +1 -1
index 3e9c56ee651f..894740a6f4e0 100644
@@ -21,7 +21,7 @@
 #define FANOTIFY_FID_BITS	(FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
 
 #define FANOTIFY_INIT_FLAGS	(FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
-				 FAN_REPORT_TID | \
+				 FAN_REPORT_TID | FAN_REPORT_PIDFD | \
 				 FAN_CLOEXEC | FAN_NONBLOCK | \
 				 FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
 
include/uapi/linux/fanotify.h +2 -0
index fbf9c5c7dd59..369392644d4e 100644
@@ -55,6 +55,7 @@
 #define FAN_REPORT_FID		0x00000200	/* Report unique file id */
 #define FAN_REPORT_DIR_FID	0x00000400	/* Report unique directory id */
 #define FAN_REPORT_NAME		0x00000800	/* Report events with name */
+#define FAN_REPORT_PIDFD	0x00001000	/* Return a pidfd for event->pid */
 
 /* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */
 #define FAN_REPORT_DFID_NAME	(FAN_REPORT_DIR_FID | FAN_REPORT_NAME)
@@ -160,6 +161,7 @@ struct fanotify_response {
 
 /* No fd set in event */
 #define FAN_NOFD	-1
+#define FAN_NOPIDFD	FAN_NOFD
 
 /* Helper functions to deal with fanotify_event_metadata buffers */
 #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
Signature · 3 lines
2.31.1.368.gbe11c130af-goog

/M
#6 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 12 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

Quoted 2 lines

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1).

Hi Matthew,

All in all looks good, just a few small nits.

Quoted 5 lines

Falling back and providing a pid instead of a
pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

I don't think this should have been even "considered" so I see little value in this paragraph in commit message.

Quoted 26 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +1 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        struct fanotify_info *info = fanotify_event_info(event);
        unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
        struct file *f = NULL;
-       int ret, fd = FAN_NOFD;
+       int ret, pidfd, fd = FAN_NOFD;
        int info_type = 0;
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        metadata.vers = FANOTIFY_METADATA_VERSION;
        metadata.reserved = 0;
        metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-       metadata.pid = pid_vnr(event->pid);

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 352

Quoted 10 lines
fs/notify/fanotify/fanotify_user.c +10 -0
+
+       if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+               pid_has_task(event->pid, PIDTYPE_TGID)) {
+               /*
+                * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+                * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+                * blindly lifted without pidfds for threads actually being
+                * supported.
+                */
+               WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));

Better WARN_ON_ONCE() the outcome of this error is not terrible. Also in the comment above I would not refer to this warning as "panic".

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 361

Quoted 9 lines
fs/notify/fanotify/fanotify_user.c +9 -0
+
+               pidfd = pidfd_create(event->pid, 0);
+               if (unlikely(pidfd < 0))
+                       metadata.pid = FAN_NOPIDFD;
+               else
+                       metadata.pid = pidfd;
+       } else {
+               metadata.pid = pid_vnr(event->pid);
+       }

You should rebase your work on:
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git fsnotify and resolve conflicts with "unprivileged listener" code.

Need to make sure that pidfd is not reported to an unprivileged listener even if group was initialized by a privileged process. This is a conscious conservative choice that we made for reporting pid info to unprivileged listener that can be revisited in the future.

Quoted 33 lines
fs/notify/fanotify/fanotify_user.c +10 -1
        if (path && path->mnt && path->dentry) {
                fd = create_fd(group, path, &f);
@@ -941,6 +959,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 #endif
                return -EINVAL;
 
+       /*
+        * A pidfd can only be returned for a thread-group leader; thus
+        * FAN_REPORT_TID and FAN_REPORT_PIDFD need to be mutually
+        * exclusive. Once the pidfd API supports the creation of pidfds on
+        * individual threads, then we can look at removing this conditional.
+        */
+       if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
+               return -EINVAL;
+
        if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
                return -EINVAL;
 
@@ -1312,7 +1339,7 @@ SYSCALL32_DEFINE6(fanotify_mark,
  */
 static int __init fanotify_user_setup(void)
 {
-       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
+       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
        fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
include/linux/fanotify.h +0 -0
index 3e9c56ee651f..894740a6f4e0 100644

include/linux/fanotify.h line 27

Quoted 8 lines
include/linux/fanotify.h +1 -1
@@ -21,7 +21,7 @@
 #define FANOTIFY_FID_BITS      (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
 
 #define FANOTIFY_INIT_FLAGS    (FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
-                                FAN_REPORT_TID | \
+                                FAN_REPORT_TID | FAN_REPORT_PIDFD | \
                                 FAN_CLOEXEC | FAN_NONBLOCK | \
                                 FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
 

FAN_REPORT_PIDFD should be added to FANOTIFY_ADMIN_INIT_FLAGS from fsnotify branch.

Thanks,
Amir.

#7 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 13 lines · Amir Goldstein
Quoted 12 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

Quoted 6 lines
Quoted 2 lines

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1).

Hi Matthew,

All in all looks good, just a few small nits.

Thanks for feedback Amir! :)

Quoted 8 lines
Quoted 5 lines

Falling back and providing a pid instead of a
pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

I don't think this should have been even "considered" so I see little value in this paragraph in commit message.

Fair point. I will discard this sentence for all subsequent iterations of this patch series. I guess the idea was that this patch series was meant to be labeled as being "RFC", so some extra thoughts had been noted.

Quoted 30 lines
Quoted 30 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +6 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        struct fanotify_info *info = fanotify_event_info(event);
        unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
        struct file *f = NULL;
-       int ret, fd = FAN_NOFD;
+       int ret, pidfd, fd = FAN_NOFD;
        int info_type = 0;
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        metadata.vers = FANOTIFY_METADATA_VERSION;
        metadata.reserved = 0;
        metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-       metadata.pid = pid_vnr(event->pid);
+
+       if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+               pid_has_task(event->pid, PIDTYPE_TGID)) {
+               /*
+                * Given FAN_REPORT_PIDFD is to be mutually exclusive with
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 352

Quoted 5 lines
fs/notify/fanotify/fanotify_user.c +5 -0
+                * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+                * blindly lifted without pidfds for threads actually being
+                * supported.
+                */
+               WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));

Better WARN_ON_ONCE() the outcome of this error is not terrible. Also in the comment above I would not refer to this warning as "panic".

ACK.

Quoted 10 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 361

Quoted 9 lines
fs/notify/fanotify/fanotify_user.c +9 -0
+
+               pidfd = pidfd_create(event->pid, 0);
+               if (unlikely(pidfd < 0))
+                       metadata.pid = FAN_NOPIDFD;
+               else
+                       metadata.pid = pidfd;
+       } else {
+               metadata.pid = pid_vnr(event->pid);
+       }
Quoted 3 lines

You should rebase your work on:
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git fsnotify and resolve conflicts with "unprivileged listener" code.

ACK.

Quoted 4 lines

Need to make sure that pidfd is not reported to an unprivileged listener even if group was initialized by a privileged process. This is a conscious conservative choice that we made for reporting pid info to unprivileged listener that can be revisited in the future.

OK, I see. In that case, I guess I can add the FAN_REPORT_PIDFD check above the current conditional [0]:

...
if (!capable(CAP_SYS_ADMIN) && task_tgid(current) != event->pid)

        metadata.pid = 0;

...

That way, AFAIK even if it is an unprivileged listener the pid info will be overwritten as intended.

Quoted 35 lines
Quoted 35 lines
fs/notify/fanotify/fanotify_user.c +10 -1
        if (path && path->mnt && path->dentry) {
                fd = create_fd(group, path, &f);
@@ -941,6 +959,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 #endif
                return -EINVAL;
 
+       /*
+        * A pidfd can only be returned for a thread-group leader; thus
+        * FAN_REPORT_TID and FAN_REPORT_PIDFD need to be mutually
+        * exclusive. Once the pidfd API supports the creation of pidfds on
+        * individual threads, then we can look at removing this conditional.
+        */
+       if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
+               return -EINVAL;
+
        if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
                return -EINVAL;
 
@@ -1312,7 +1339,7 @@ SYSCALL32_DEFINE6(fanotify_mark,
  */
 static int __init fanotify_user_setup(void)
 {
-       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
+       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
        fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
include/linux/fanotify.h +0 -0
index 3e9c56ee651f..894740a6f4e0 100644
@@ -21,7 +21,7 @@
 #define FANOTIFY_FID_BITS      (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
 
Quoted 9 lines

include/linux/fanotify.h line 27

Quoted 6 lines
include/linux/fanotify.h +1 -1
 #define FANOTIFY_INIT_FLAGS    (FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
-                                FAN_REPORT_TID | \
+                                FAN_REPORT_TID | FAN_REPORT_PIDFD | \
                                 FAN_CLOEXEC | FAN_NONBLOCK | \
                                 FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
 

FAN_REPORT_PIDFD should be added to FANOTIFY_ADMIN_INIT_FLAGS from fsnotify branch.

ACK.

Before sending any other version of this patch series through I will see what Jan and Christian have to say.

[0]
https://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git/tree/fs/notify/fanotify/fanotify_user.c?h=fsnotify&id=7cea2a3c505e87a9d6afc78be4a7f7be636a73a7#n427

/M

#8 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 103 lines · Matthew Bobrowski
Quoted 13 lines · Amir Goldstein
Quoted 12 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

Quoted 6 lines
Quoted 2 lines

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1).

Hi Matthew,

All in all looks good, just a few small nits.

Thanks for feedback Amir! :)

Quoted 8 lines
Quoted 5 lines

Falling back and providing a pid instead of a
pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

I don't think this should have been even "considered" so I see little value in this paragraph in commit message.

Fair point. I will discard this sentence for all subsequent iterations of this patch series. I guess the idea was that this patch series was meant to be labeled as being "RFC", so some extra thoughts had been noted.

Quoted 30 lines
Quoted 30 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +6 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        struct fanotify_info *info = fanotify_event_info(event);
        unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
        struct file *f = NULL;
-       int ret, fd = FAN_NOFD;
+       int ret, pidfd, fd = FAN_NOFD;
        int info_type = 0;
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        metadata.vers = FANOTIFY_METADATA_VERSION;
        metadata.reserved = 0;
        metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-       metadata.pid = pid_vnr(event->pid);
+
+       if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+               pid_has_task(event->pid, PIDTYPE_TGID)) {
+               /*
+                * Given FAN_REPORT_PIDFD is to be mutually exclusive with
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 352

Quoted 5 lines
fs/notify/fanotify/fanotify_user.c +5 -0
+                * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+                * blindly lifted without pidfds for threads actually being
+                * supported.
+                */
+               WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));

Better WARN_ON_ONCE() the outcome of this error is not terrible. Also in the comment above I would not refer to this warning as "panic".

ACK.

Quoted 10 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 361

Quoted 9 lines
fs/notify/fanotify/fanotify_user.c +9 -0
+
+               pidfd = pidfd_create(event->pid, 0);
+               if (unlikely(pidfd < 0))
+                       metadata.pid = FAN_NOPIDFD;
+               else
+                       metadata.pid = pidfd;
+       } else {
+               metadata.pid = pid_vnr(event->pid);
+       }
Quoted 3 lines

You should rebase your work on:
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git fsnotify and resolve conflicts with "unprivileged listener" code.

ACK.

Quoted 4 lines

Need to make sure that pidfd is not reported to an unprivileged listener even if group was initialized by a privileged process. This is a conscious conservative choice that we made for reporting pid info to unprivileged listener that can be revisited in the future.

OK, I see. In that case, I guess I can add the FAN_REPORT_PIDFD check above the current conditional [0]:

Quoted 8 lines

...
if (!capable(CAP_SYS_ADMIN) && task_tgid(current) != event->pid)

        metadata.pid = 0;

...

That way, AFAIK even if it is an unprivileged listener the pid info will be overwritten as intended.

Situation is a bit more subtle than that.
If you override event->pid with zero and zero is interpreted as pidfd that would not be consistent with uapi documentation.
You need to make sure that event->pid is FAN_NOPIDFD in case (!capable(CAP_SYS_ADMIN) &&
FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD))
Hopefully, you can do that while keeping the special cases to minimum...

Quoted 45 lines
Quoted 35 lines
Quoted 35 lines
fs/notify/fanotify/fanotify_user.c +10 -1
        if (path && path->mnt && path->dentry) {
                fd = create_fd(group, path, &f);
@@ -941,6 +959,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 #endif
                return -EINVAL;
 
+       /*
+        * A pidfd can only be returned for a thread-group leader; thus
+        * FAN_REPORT_TID and FAN_REPORT_PIDFD need to be mutually
+        * exclusive. Once the pidfd API supports the creation of pidfds on
+        * individual threads, then we can look at removing this conditional.
+        */
+       if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
+               return -EINVAL;
+
        if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
                return -EINVAL;
 
@@ -1312,7 +1339,7 @@ SYSCALL32_DEFINE6(fanotify_mark,
  */
 static int __init fanotify_user_setup(void)
 {
-       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
+       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
        fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
include/linux/fanotify.h +0 -0
index 3e9c56ee651f..894740a6f4e0 100644
@@ -21,7 +21,7 @@
 #define FANOTIFY_FID_BITS      (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
 
Quoted 9 lines

include/linux/fanotify.h line 27

Quoted 6 lines
include/linux/fanotify.h +1 -1
 #define FANOTIFY_INIT_FLAGS    (FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
-                                FAN_REPORT_TID | \
+                                FAN_REPORT_TID | FAN_REPORT_PIDFD | \
                                 FAN_CLOEXEC | FAN_NONBLOCK | \
                                 FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
 

FAN_REPORT_PIDFD should be added to FANOTIFY_ADMIN_INIT_FLAGS from fsnotify branch.

Quoted 5 lines

ACK.

Before sending any other version of this patch series through I will see what Jan and Christian have to say.

That makes sense.

Thanks,
Amir.

#9 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 29 lines · Amir Goldstein
Quoted 28 lines · Matthew Bobrowski
Quoted 9 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 361

Quoted 8 lines
fs/notify/fanotify/fanotify_user.c +8 -0
+               pidfd = pidfd_create(event->pid, 0);
+               if (unlikely(pidfd < 0))
+                       metadata.pid = FAN_NOPIDFD;
+               else
+                       metadata.pid = pidfd;
+       } else {
+               metadata.pid = pid_vnr(event->pid);
+       }
Quoted 3 lines

You should rebase your work on:
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git fsnotify and resolve conflicts with "unprivileged listener" code.

ACK.

Quoted 4 lines

Need to make sure that pidfd is not reported to an unprivileged listener even if group was initialized by a privileged process. This is a conscious conservative choice that we made for reporting pid info to unprivileged listener that can be revisited in the future.

OK, I see. In that case, I guess I can add the FAN_REPORT_PIDFD check above the current conditional [0]:

...
if (!capable(CAP_SYS_ADMIN) && task_tgid(current) != event->pid)

        metadata.pid = 0;

...

Quoted 7 lines
Quoted 3 lines

That way, AFAIK even if it is an unprivileged listener the pid info will be overwritten as intended.

Situation is a bit more subtle than that.
If you override event->pid with zero and zero is interpreted as pidfd that would not be consistent with uapi documentation.

Ah, yes, of course! I had totally overlooked this. Also, speaking of UAPI documentation, I'll have it prepared along with the LTP tests once I get the ACK for this particular concept from Jan and Christian.

Quoted 4 lines

You need to make sure that event->pid is FAN_NOPIDFD in case (!capable(CAP_SYS_ADMIN) &&
FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD))
Hopefully, you can do that while keeping the special cases to minimum...

I don't foresee any issues with doing this at all.

/M

#10 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

In reply to Amir Goldstein
Quoted 79 lines · Amir Goldstein
Quoted 12 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

Quoted 2 lines

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1).

Hi Matthew,

All in all looks good, just a few small nits.

Quoted 5 lines

Falling back and providing a pid instead of a
pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

I don't think this should have been even "considered" so I see little value in this paragraph in commit message.

Quoted 26 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +1 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        struct fanotify_info *info = fanotify_event_info(event);
        unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
        struct file *f = NULL;
-       int ret, fd = FAN_NOFD;
+       int ret, pidfd, fd = FAN_NOFD;
        int info_type = 0;
 
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
        metadata.vers = FANOTIFY_METADATA_VERSION;
        metadata.reserved = 0;
        metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-       metadata.pid = pid_vnr(event->pid);

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 352

Quoted 10 lines
fs/notify/fanotify/fanotify_user.c +10 -0
+
+       if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+               pid_has_task(event->pid, PIDTYPE_TGID)) {
+               /*
+                * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+                * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+                * blindly lifted without pidfds for threads actually being
+                * supported.
+                */
+               WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));

Better WARN_ON_ONCE() the outcome of this error is not terrible. Also in the comment above I would not refer to this warning as "panic".

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 361

Quoted 9 lines
fs/notify/fanotify/fanotify_user.c +9 -0
+
+               pidfd = pidfd_create(event->pid, 0);
+               if (unlikely(pidfd < 0))
+                       metadata.pid = FAN_NOPIDFD;
+               else
+                       metadata.pid = pidfd;
+       } else {
+               metadata.pid = pid_vnr(event->pid);
+       }
Quoted 6 lines

You should rebase your work on:
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git fsnotify and resolve conflicts with "unprivileged listener" code.

Need to make sure that pidfd is not reported to an unprivileged listener even if group was initialized by a privileged process.

I agree.

maintainer
#11 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

In reply to Matthew Bobrowski
Quoted 11 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

Quoted 8 lines

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

Overall this looks OK to me. Just one style nit & one question below in addition to what Amir wrote.

Quoted 20 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +1 -1
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 345

Quoted 7 lines
fs/notify/fanotify/fanotify_user.c +3 -1
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {

Please align the rest of the condition to the opening brace. I.e., like:

	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
	    pid_has_task(event->pid, PIDTYPE_TGID)) {

BTW, why is the pid_has_task() check here? And why is it OK to fall back to returning pid if pid_has_task() is false?

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#12 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 19 lines · Jan Kara
Quoted 18 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

Quoted 4 lines
Quoted 1 line

Overall this looks OK to me. Just one style nit & one question below in addition to what Amir wrote.

Thanks for the quick review Jan!

Quoted 30 lines
Quoted 20 lines
---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +1 -1
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 345

Quoted 7 lines
fs/notify/fanotify/fanotify_user.c +3 -1
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {

Please align the rest of the condition to the opening brace. I.e., like:

Quoted 2 lines
	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
	    pid_has_task(event->pid, PIDTYPE_TGID)) {

ACK.

Quoted 1 line

BTW, why is the pid_has_task() check here?

My thought was that we add a means of ensuring that event->pid holds a reference to a thread-group leader as pidfds aren't supported for individual threads just yet. The same check is implemented in pidfd_open(), so I thought to make the preliminary checks consistent.

Actually, now that I've writeten that, perhaps the pid_has_task() check can be rolled up into pidfd_create()?

Quoted 2 lines

And why is it OK to fall back to returning pid if pid_has_task() is false?

Ah, I see, it's not OK. Good catch Jan! I will need to fix this up in the follow up series.

/M

#13 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

In reply to Matthew Bobrowski
Quoted 49 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +7 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {
+		/*
+		 * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+		 * FAN_REPORT_TID, panic here if the mutual exclusion is ever

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 10 lines
fs/notify/fanotify/fanotify_user.c +10 -0
+		 * blindly lifted without pidfds for threads actually being
+		 * supported.
+		 */
+		WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+		pidfd = pidfd_create(event->pid, 0);
+		if (unlikely(pidfd < 0))
+			metadata.pid = FAN_NOPIDFD;
+		else
+			metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

Christian

#14 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 59 lines · Christian Brauner
Quoted 57 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +15 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
      struct fanotify_info *info = fanotify_event_info(event);
      unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
      struct file *f = NULL;
-     int ret, fd = FAN_NOFD;
+     int ret, pidfd, fd = FAN_NOFD;
      int info_type = 0;
 
      pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
      metadata.vers = FANOTIFY_METADATA_VERSION;
      metadata.reserved = 0;
      metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-     metadata.pid = pid_vnr(event->pid);
+
+     if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+             pid_has_task(event->pid, PIDTYPE_TGID)) {
+             /*
+              * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+              * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+              * blindly lifted without pidfds for threads actually being
+              * supported.
+              */
+             WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+             pidfd = pidfd_create(event->pid, 0);
+             if (unlikely(pidfd < 0))
+                     metadata.pid = FAN_NOPIDFD;
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 2 lines
fs/notify/fanotify/fanotify_user.c +2 -0
+             else
+                     metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

struct fanotify_event_metadata is fully booked.
We could use a variable length record, but IMO that's an overkill for pidfd.

If you are concerned about users wrongly interpreting pidfd as pid and getting the the wrong process we could use invalid negative values for pidfd, e.g. metadata.pid = ~pidfd.
This has the quality that 0 can mean both FAN_NOPIDFD and no pid. UAPI to this would be abstracted with
#define FAN_EVENT_PIDFD(event) (~((event)->pid))

I am not convinced that this helps more than it hurts users, but abstracting the accessor to pidfd could be a good idea anyway.

Thanks,
Amir.

#15 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 80 lines · Amir Goldstein
Quoted 59 lines · Christian Brauner
Quoted 57 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +15 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
      struct fanotify_info *info = fanotify_event_info(event);
      unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
      struct file *f = NULL;
-     int ret, fd = FAN_NOFD;
+     int ret, pidfd, fd = FAN_NOFD;
      int info_type = 0;
 
      pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
      metadata.vers = FANOTIFY_METADATA_VERSION;
      metadata.reserved = 0;
      metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-     metadata.pid = pid_vnr(event->pid);
+
+     if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+             pid_has_task(event->pid, PIDTYPE_TGID)) {
+             /*
+              * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+              * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+              * blindly lifted without pidfds for threads actually being
+              * supported.
+              */
+             WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+             pidfd = pidfd_create(event->pid, 0);
+             if (unlikely(pidfd < 0))
+                     metadata.pid = FAN_NOPIDFD;
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 2 lines
fs/notify/fanotify/fanotify_user.c +2 -0
+             else
+                     metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

struct fanotify_event_metadata is fully booked.
We could use a variable length record, but IMO that's an overkill for pidfd.

If you are concerned about users wrongly interpreting pidfd as pid and getting the the wrong process we could use invalid negative values for pidfd, e.g. metadata.pid = ~pidfd.
This has the quality that 0 can mean both FAN_NOPIDFD and no pid. UAPI to this would be abstracted with
#define FAN_EVENT_PIDFD(event) (~((event)->pid))

Quoted 2 lines

I am not convinced that this helps more than it hurts users, but abstracting the accessor to pidfd could be a good idea anyway.

Abstracting the accessor may be a good idea but let's not do the negative values thing that seems clunky to me.

maintainer
#16 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

In reply to Christian Brauner
Quoted 58 lines · Christian Brauner
Quoted 57 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +15 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {
+		/*
+		 * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+		 * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+		 * blindly lifted without pidfds for threads actually being
+		 * supported.
+		 */
+		WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+		pidfd = pidfd_create(event->pid, 0);
+		if (unlikely(pidfd < 0))
+			metadata.pid = FAN_NOPIDFD;
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 2 lines
fs/notify/fanotify/fanotify_user.c +2 -0
+		else
+			metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

I'm not a huge fan of overloading fields either but in this particular case I'm fine with that because:

a) storage size & type matches
b) it describes exactly the same information, just in a different way

It is not possible to store the pidfd elsewhere in fanotify_event_metadata. But it is certainly possible to use extended event info to return pidfd instead - similarly to how we return e.g. handle + fsid for some notification groups. It just means somewhat longer events and more complicated parsing of structured events in userspace. But as I write above, in this case I don't think it is worth it - only if we think that returning both pid and pidfd could ever be useful.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#17 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 74 lines · Jan Kara
Quoted 58 lines · Christian Brauner
Quoted 57 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +15 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {
+		/*
+		 * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+		 * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+		 * blindly lifted without pidfds for threads actually being
+		 * supported.
+		 */
+		WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+		pidfd = pidfd_create(event->pid, 0);
+		if (unlikely(pidfd < 0))
+			metadata.pid = FAN_NOPIDFD;
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 2 lines
fs/notify/fanotify/fanotify_user.c +2 -0
+		else
+			metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

I'm not a huge fan of overloading fields either but in this particular case I'm fine with that because:

a) storage size & type matches
b) it describes exactly the same information, just in a different way

Quoted 7 lines

It is not possible to store the pidfd elsewhere in fanotify_event_metadata. But it is certainly possible to use extended event info to return pidfd instead - similarly to how we return e.g. handle + fsid for some notification groups. It just means somewhat longer events and more complicated parsing of structured events in userspace. But as I write above, in this case I don't think it is worth it - only if we think that returning both pid and pidfd could ever be useful.

Yeah, I don't hink users need to do that. After all they can parse the PID out of the /proc/self/fdinfo/<pidfd> file.

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Extension should then be fairly straightforward (64bit aligned increments)?

Christian

#18 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 83 lines · Christian Brauner
Quoted 74 lines · Jan Kara
Quoted 58 lines · Christian Brauner
Quoted 57 lines · Matthew Bobrowski

Introduce a new flag FAN_REPORT_PIDFD for fanotify_init(2) which allows userspace applications to control whether a pidfd is to be returned instead of a pid for struct fanotify_event_metadata.pid.

FAN_REPORT_PIDFD is mutually exclusive with FAN_REPORT_TID as the pidfd API is currently restricted to only support pidfd generation for thread-group leaders. Attempting to set them both when calling fanotify_init(2) will result in -EINVAL being returned to the caller. As the pidfd API evolves and support is added for tids, this is something that could be relaxed in the future.

If pidfd creation fails, the pid in struct fanotify_event_metadata is set to FAN_NOPIDFD(-1). Falling back and providing a pid instead of a pidfd on pidfd creation failures was considered, although this could possibly lead to confusion and unpredictability within userspace applications as distinguishing between whether an actual pidfd or pid was returned could be difficult, so it's best to be explicit.

---
 fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++---
 include/linux/fanotify.h           |  2 +-
 include/uapi/linux/fanotify.h      |  2 ++
 3 files changed, 33 insertions(+), 4 deletions(-)
fs/notify/fanotify/fanotify_user.c +15 -2
index 9e0c1afac8bd..fd8ae88796a8 100644
@@ -329,7 +329,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	struct fanotify_info *info = fanotify_event_info(event);
 	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
 	struct file *f = NULL;
-	int ret, fd = FAN_NOFD;
+	int ret, pidfd, fd = FAN_NOFD;
 	int info_type = 0;
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@@ -340,7 +340,25 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
 	metadata.vers = FANOTIFY_METADATA_VERSION;
 	metadata.reserved = 0;
 	metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
-	metadata.pid = pid_vnr(event->pid);
+
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_PIDFD) &&
+		pid_has_task(event->pid, PIDTYPE_TGID)) {
+		/*
+		 * Given FAN_REPORT_PIDFD is to be mutually exclusive with
+		 * FAN_REPORT_TID, panic here if the mutual exclusion is ever
+		 * blindly lifted without pidfds for threads actually being
+		 * supported.
+		 */
+		WARN_ON(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
+
+		pidfd = pidfd_create(event->pid, 0);
+		if (unlikely(pidfd < 0))
+			metadata.pid = FAN_NOPIDFD;
Quoted 8 lines

fs/notify/fanotify/fanotify_user.c static ssize_t copy_event_to_user(struct fsnotify_group *group, · line 358

Quoted 2 lines
fs/notify/fanotify/fanotify_user.c +2 -0
+		else
+			metadata.pid = pidfd;

I'm not a fan of overloading fields (Yes, we did this for the _legacy_ clone() syscall for CLONE_PIDFD/CLONE_PARENT_SETTID but in general it's never a good idea if there are other options, imho.). Could/should we consider the possibility of adding a new pidfd field to struct fanotify_event_metadata?

I'm not a huge fan of overloading fields either but in this particular case I'm fine with that because:

a) storage size & type matches
b) it describes exactly the same information, just in a different way

Quoted 7 lines

It is not possible to store the pidfd elsewhere in fanotify_event_metadata. But it is certainly possible to use extended event info to return pidfd instead - similarly to how we return e.g. handle + fsid for some notification groups. It just means somewhat longer events and more complicated parsing of structured events in userspace. But as I write above, in this case I don't think it is worth it - only if we think that returning both pid and pidfd could ever be useful.

Quoted 2 lines

Yeah, I don't hink users need to do that. After all they can parse the PID out of the /proc/self/fdinfo/<pidfd> file.

I agree. We can do this little PID extraction dance in userspace, it's not a big deal at this point IMO.

Quoted 23 lines

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

/M

maintainer
#19 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#20 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 48 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 11 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

But there is a middle path option.
The event metadata can be self described without extending it:

 struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;
-       __u8 reserved;
+#define FANOTIFY_METADATA_FLAG_PIDFD   1
+       __u8 flags;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;

Thanks,
Amir.

maintainer
#21 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 67 lines · Amir Goldstein
Quoted 48 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 11 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

But there is a middle path option.
The event metadata can be self described without extending it:

 struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;

Review

Quoted 6 lines
(quoted diff) +2 -1
-       __u8 reserved;
+#define FANOTIFY_METADATA_FLAG_PIDFD   1
+       __u8 flags;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;

Well, yes, but do we want another way to describe what fanotify_event_metadata actually contains? I don't think parsing extended event information is that bad to make changes like this worth it...

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#22 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 76 lines · Jan Kara
Quoted 67 lines · Amir Goldstein
Quoted 48 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 11 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

But there is a middle path option.
The event metadata can be self described without extending it:

 struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;

Review

Quoted 6 lines
(quoted diff) +2 -1
-       __u8 reserved;
+#define FANOTIFY_METADATA_FLAG_PIDFD   1
+       __u8 flags;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;
Quoted 3 lines

Well, yes, but do we want another way to describe what fanotify_event_metadata actually contains? I don't think parsing extended event information is that bad to make changes like this worth it...

Fine by me.
But in that case, we should report pidfd in addition to pid.

There is an advantage in doing that -
For the use case of filtering out events generated by self_pid of the listener, there is no need to follow pidfd in order to check if event->pid == self_pid.

Thanks,
Amir.

maintainer
#23 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 77 lines · Amir Goldstein
Quoted 76 lines · Jan Kara
Quoted 73 lines · Amir Goldstein
Quoted 48 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 11 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

But there is a middle path option.
The event metadata can be self described without extending it:

 struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;
(quoted diff) +2 -1
-       __u8 reserved;
+#define FANOTIFY_METADATA_FLAG_PIDFD   1
+       __u8 flags;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;
Quoted 6 lines
Quoted 3 lines

Well, yes, but do we want another way to describe what fanotify_event_metadata actually contains? I don't think parsing extended event information is that bad to make changes like this worth it...

Fine by me.
But in that case, we should report pidfd in addition to pid.

Agreed.

Quoted 4 lines

There is an advantage in doing that -
For the use case of filtering out events generated by self_pid of the listener, there is no need to follow pidfd in order to check if event->pid == self_pid.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#24 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 78 lines · Jan Kara
Quoted 77 lines · Amir Goldstein
Quoted 76 lines · Jan Kara
Quoted 73 lines · Amir Goldstein
Quoted 48 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 11 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

But there is a middle path option.
The event metadata can be self described without extending it:

 struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;
(quoted diff) +2 -1
-       __u8 reserved;
+#define FANOTIFY_METADATA_FLAG_PIDFD   1
+       __u8 flags;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

Well, yes, but do we want another way to describe what fanotify_event_metadata actually contains? I don't think parsing extended event information is that bad to make changes like this worth it...

Fine by me.
But in that case, we should report pidfd in addition to pid.

Agreed.

Sounds good to me.

Christian

#25 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

In reply to Jan Kara
Quoted 47 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 10 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

This approach makes sense to me.

Jan/Amir, just to be clear, we've agreed to go ahead with the extended struct approach whereby specifying the FAN_REPORT_PIDFD flag will result in an event which includes an additional struct (i.e. fanotify_event_info_pid) alongside the generic existing fanotify_event_metadata (also ensuring that pid has been provided). Events will be provided to userspace applications just like when specifying FAN_REPORT_FID, correct?

/M

#26 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 60 lines · Matthew Bobrowski
Quoted 47 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

Quoted 10 lines

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

Quoted 6 lines

This approach makes sense to me.

Jan/Amir, just to be clear, we've agreed to go ahead with the extended struct approach whereby specifying the FAN_REPORT_PIDFD flag will result in an event which includes an additional struct (i.e. fanotify_event_info_pid) alongside the generic existing

struct fanotify_event_info_pidfd?

Quoted 4 lines

fanotify_event_metadata (also ensuring that pid has been provided). Events will be provided to userspace applications just like when specifying FAN_REPORT_FID, correct?

Yes.

Thanks,
Amir.

#27 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 61 lines · Amir Goldstein
Quoted 60 lines · Matthew Bobrowski
Quoted 57 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

Quoted 8 lines
Quoted 6 lines

This approach makes sense to me.

Jan/Amir, just to be clear, we've agreed to go ahead with the extended struct approach whereby specifying the FAN_REPORT_PIDFD flag will result in an event which includes an additional struct (i.e. fanotify_event_info_pid) alongside the generic existing

struct fanotify_event_info_pidfd?

Well, yeah? I mean, my line of thought was that we'd also need to include struct fanotify_event_info_header alongside the event to provide more meta-information about the additional event you'd expect to receive when FAN_REPORT_PIDFD is provided, so we'd end up with something like:

struct fanotify_event_info_pidfd {
       struct fanotify_event_info_header hdr;
       __s32 pidfd;
}

Unless this of course is overbaking it and there's no need to do this?

/M

#28 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 78 lines · Matthew Bobrowski
Quoted 61 lines · Amir Goldstein
Quoted 60 lines · Matthew Bobrowski
Quoted 57 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

Quoted 8 lines
Quoted 6 lines

This approach makes sense to me.

Jan/Amir, just to be clear, we've agreed to go ahead with the extended struct approach whereby specifying the FAN_REPORT_PIDFD flag will result in an event which includes an additional struct (i.e. fanotify_event_info_pid) alongside the generic existing

struct fanotify_event_info_pidfd?

Well, yeah? I mean, my line of thought was that we'd also need to include struct fanotify_event_info_header alongside the event to provide more meta-information about the additional event you'd expect to receive when FAN_REPORT_PIDFD is provided, so we'd end up with something like:

Quoted 7 lines
struct fanotify_event_info_pidfd {
       struct fanotify_event_info_header hdr;
       __s32 pidfd;
}

Unless this of course is overbaking it and there's no need to do this?

We need this. I was just pointing out that you wrote fanotify_event_info_pid must have been a typo.

Thanks,
Amir.

#29 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 84 lines · Amir Goldstein
Quoted 83 lines · Matthew Bobrowski
Quoted 61 lines · Amir Goldstein
Quoted 60 lines · Matthew Bobrowski
Quoted 57 lines · Jan Kara
Quoted 27 lines · Matthew Bobrowski
Quoted 23 lines · Christian Brauner

A general question about struct fanotify_event_metadata and its extensibility model:
looking through the code it seems that this struct is read via fanotify_rad(). So the user is expected to supply a buffer with at least

#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

bytes. In addition you can return the info to the user about how many bytes the kernel has written from fanotify_read().

So afaict extending fanotify_event_metadata should be _fairly_ straightforward, right? It would essentially the complement to copy_struct_from_user() which Aleksa and I added (1 or 2 years ago) which deals with user->kernel and you're dealing with kernel->user:
- If the user supplied a buffer smaller than the minimum known struct size -> reject.
- If the user supplied a buffer < smaller than what the current kernel supports -> copy only what userspace knows about, and return the size userspace knows about.
- If the user supplied a buffer that is larger than what the current kernel knows about -> copy only what the kernel knows about, zero the rest, and return the kernel size.

Quoted 2 lines

Extension should then be fairly straightforward (64bit aligned increments)?

Quoted 8 lines

You'd think that it's fairly straightforward, but I have a feeling that the whole fanotify_event_metadata extensibility discussion and the current limitation to do so revolves around whether it can be achieved in a way which can guarantee that no userspace applications would break. I think the answer to this is that there's no guarantee because of <<reasons>>, so the decision to extend fanotify's feature set was done via other means i.e. introduction of additional structures.

There's no real problem extending fanotify_event_metadata. We already have multiple extended version of that structure in use (see e.g. FAN_REPORT_FID flag and its effect, extended versions of the structure in include/uapi/linux/fanotify.h). The key for backward compatibility is to create extended struct only when explicitely requested by a flag when creating notification group - and that would be the case here - FAN_REPORT_PIDFD or how you called it. It is just that extending the structure means adding 8 bytes to each event and parsing extended structure is more cumbersome than just fetching s32 from a well known location.

On the other hand extended structure is self-describing (i.e., you can tell the meaning of all the fields just from the event you receive) while reusing 'pid' field means that you have to know how the notification group was created (whether FAN_REPORT_PIDFD was used or not) to be able to interpret the contents of the event. Actually I think the self-describing feature of fanotify event stream is useful (e.g. when application manages multiple fanotify groups or when fanotify group descriptors are passed among processes) so now I'm more leaning towards using the extended structure instead of reusing 'pid' as Christian suggests. I'm sorry for the confusion.

Quoted 8 lines
Quoted 6 lines

This approach makes sense to me.

Jan/Amir, just to be clear, we've agreed to go ahead with the extended struct approach whereby specifying the FAN_REPORT_PIDFD flag will result in an event which includes an additional struct (i.e. fanotify_event_info_pid) alongside the generic existing

struct fanotify_event_info_pidfd?

Well, yeah? I mean, my line of thought was that we'd also need to include struct fanotify_event_info_header alongside the event to provide more meta-information about the additional event you'd expect to receive when FAN_REPORT_PIDFD is provided, so we'd end up with something like:

struct fanotify_event_info_pidfd {
       struct fanotify_event_info_header hdr;
       __s32 pidfd;
}
Quoted 5 lines
Quoted 2 lines

Unless this of course is overbaking it and there's no need to do this?

We need this. I was just pointing out that you wrote fanotify_event_info_pid must have been a typo.

Oh, right, that sure was a typo! :)

Amir, I was just thinking about this a little over the weekend and I don't think we discussed how to handle the FAN_REPORT_PIDFD | FAN_REPORT_FID and friends case? My immediate thought is to make FAN_REPORT_PIDFD mutually exclusive with FAN_REPORT_FID and friends, but then again receiving a pidfd along with FID events may be also useful for some? What are your thoughts on this? If we don't go ahead with mutual exclusion, then this multiple event types alongside struct fanotify_event_metadata starts getting a little clunky, don't you think?

/M

#30 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 10 lines

Amir, I was just thinking about this a little over the weekend and I don't think we discussed how to handle the FAN_REPORT_PIDFD | FAN_REPORT_FID and friends case? My immediate thought is to make FAN_REPORT_PIDFD mutually exclusive with FAN_REPORT_FID and friends, but then again receiving a pidfd along with FID events may be also useful for some? What are your thoughts on this? If we don't go ahead with mutual exclusion, then this multiple event types alongside struct fanotify_event_metadata starts getting a little clunky, don't you think?

The current format of an fanotify event already supports multiple info records:

[fanotify_event_metadata] [[fanotify_event_info_header][event record #1]] [[fanotify_event_info_header][event record #2]]...

(meta)->event_len is the total event length including all info records.

For example, FAN_REPORT_FID | FAN_REPORT_DFID_MAME produces (for some events) two info records, one FAN_EVENT_INFO_TYPE_FID record and one FAN_EVENT_INFO_TYPE_DFID_NAME record.

So I see no problem with combination of FAN_REPORT_FID and FAN_REPORT_PIDFD.

Thanks,
Amir.

#31 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 17 lines · Amir Goldstein
Quoted 10 lines

Amir, I was just thinking about this a little over the weekend and I don't think we discussed how to handle the FAN_REPORT_PIDFD | FAN_REPORT_FID and friends case? My immediate thought is to make FAN_REPORT_PIDFD mutually exclusive with FAN_REPORT_FID and friends, but then again receiving a pidfd along with FID events may be also useful for some? What are your thoughts on this? If we don't go ahead with mutual exclusion, then this multiple event types alongside struct fanotify_event_metadata starts getting a little clunky, don't you think?

The current format of an fanotify event already supports multiple info records:

[fanotify_event_metadata] [[fanotify_event_info_header][event record #1]] [[fanotify_event_info_header][event record #2]]...

Quoted 5 lines

(meta)->event_len is the total event length including all info records.

For example, FAN_REPORT_FID | FAN_REPORT_DFID_MAME produces (for some events) two info records, one FAN_EVENT_INFO_TYPE_FID record and one FAN_EVENT_INFO_TYPE_DFID_NAME record.

Ah, that's right! I now remember reviewing some patches associated with the FID change series which mentioned the possibility of receiving multiple FID info records. As the implementation currently stands, AFAIK there's not possibility for fanotify to ever return more than two info records, right?

Quoted 2 lines

So I see no problem with combination of FAN_REPORT_FID and FAN_REPORT_PIDFD.

OK.

Is there any preference in terms of whether the new FAN_REPORT_PIDFD info records precede or come after FAN_REPORT_FID/FAN_REPORT_DFID_NAME info records in FAN_REPORT_FID or FAN_REPORT_FID | FAN_REPORT_DFID_NAME configurations?

/M

#32 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 25 lines · Matthew Bobrowski
Quoted 17 lines · Amir Goldstein
Quoted 10 lines

Amir, I was just thinking about this a little over the weekend and I don't think we discussed how to handle the FAN_REPORT_PIDFD | FAN_REPORT_FID and friends case? My immediate thought is to make FAN_REPORT_PIDFD mutually exclusive with FAN_REPORT_FID and friends, but then again receiving a pidfd along with FID events may be also useful for some? What are your thoughts on this? If we don't go ahead with mutual exclusion, then this multiple event types alongside struct fanotify_event_metadata starts getting a little clunky, don't you think?

The current format of an fanotify event already supports multiple info records:

[fanotify_event_metadata] [[fanotify_event_info_header][event record #1]] [[fanotify_event_info_header][event record #2]]...

Quoted 5 lines

(meta)->event_len is the total event length including all info records.

For example, FAN_REPORT_FID | FAN_REPORT_DFID_MAME produces (for some events) two info records, one FAN_EVENT_INFO_TYPE_FID record and one FAN_EVENT_INFO_TYPE_DFID_NAME record.

Quoted 6 lines

Ah, that's right! I now remember reviewing some patches associated with the FID change series which mentioned the possibility of receiving multiple FID info records. As the implementation currently stands, AFAIK there's not possibility for fanotify to ever return more than two info records, right?

Right.
Record types FAN_EVENT_INFO_TYPE_DFID_NAME and
FAN_EVENT_INFO_TYPE_DFID are mutually exclusive.

Quoted 10 lines
Quoted 2 lines

So I see no problem with combination of FAN_REPORT_FID and FAN_REPORT_PIDFD.

OK.

Is there any preference in terms of whether the new FAN_REPORT_PIDFD info records precede or come after FAN_REPORT_FID/FAN_REPORT_DFID_NAME info records in FAN_REPORT_FID or FAN_REPORT_FID | FAN_REPORT_DFID_NAME configurations?

Doesn't matter.
Your typical application would first filter by pid/pidfd and only if process matches the filters would it care to examine the event fid info, correct? So you go first :)

Thanks,
Amir.

#33 lore raw

Re: [PATCH 2/2] fanotify: Add pidfd support to the fanotify API

Quoted 32 lines · Amir Goldstein
Quoted 31 lines · Matthew Bobrowski
Quoted 17 lines · Amir Goldstein
Quoted 10 lines

Amir, I was just thinking about this a little over the weekend and I don't think we discussed how to handle the FAN_REPORT_PIDFD | FAN_REPORT_FID and friends case? My immediate thought is to make FAN_REPORT_PIDFD mutually exclusive with FAN_REPORT_FID and friends, but then again receiving a pidfd along with FID events may be also useful for some? What are your thoughts on this? If we don't go ahead with mutual exclusion, then this multiple event types alongside struct fanotify_event_metadata starts getting a little clunky, don't you think?

The current format of an fanotify event already supports multiple info records:

[fanotify_event_metadata] [[fanotify_event_info_header][event record #1]] [[fanotify_event_info_header][event record #2]]...

Quoted 5 lines

(meta)->event_len is the total event length including all info records.

For example, FAN_REPORT_FID | FAN_REPORT_DFID_MAME produces (for some events) two info records, one FAN_EVENT_INFO_TYPE_FID record and one FAN_EVENT_INFO_TYPE_DFID_NAME record.

Ah, that's right! I now remember reviewing some patches associated with the FID change series which mentioned the possibility of receiving multiple FID info records. As the implementation currently stands, AFAIK there's not possibility for fanotify to ever return more than two info records, right?

Quoted 6 lines
Quoted 4 lines

Is there any preference in terms of whether the new FAN_REPORT_PIDFD info records precede or come after FAN_REPORT_FID/FAN_REPORT_DFID_NAME info records in FAN_REPORT_FID or FAN_REPORT_FID | FAN_REPORT_DFID_NAME configurations?

Doesn't matter.

OK, fair.

Quoted 2 lines

Your typical application would first filter by pid/pidfd and only if process matches the filters would it care to examine the event fid info, correct?

Not necessarily, but you're right, the ordering doesn't really matter too much.

/M

#34 lore raw
In reply to Matthew Bobrowski
Quoted 24 lines · Matthew Bobrowski

Hey Jan/Amir/Christian,

This is the RFC patch series for adding pidfd support to the fanotify API.

tl;dr rather than returning a pid within struct fanotify_event_metadata, a pidfd is returned instead when fanotify has been explicitly told to do so via the new FAN_REPORT_PIDFD flag.

The main driver behind returning a pidfd within an event instead of a pid is that it permits userspace applications to better detect if they've possibly lost a TOCTOU race. A common paradigm among userspace applications that make use of the fanotify API is to crawl /proc/<pid> upon receiving an event. Userspace applications do this in order to further ascertain contextual meta-information about the process that was responsible for generating the filesystem event. On high pressure systems, where pid recycling isn't uncommon, it's no longer considered as a reliable approach to directly sift through /proc/<pid> and have userspace applications use the information contained within /proc/<pid> as it could, and does, lead to program execution inaccuracies.

Now when a pidfd is returned in an event, a userspace application can:

Quoted 7 lines

a) Obtain the pid responsible for generating the filesystem event from the pidfds fdinfo.

b) Detect whether the userspace application lost the procfs access race by sending a 0 signal on the pidfd and checking the return value. A -ESRCH is indicative of the userspace application losing the race, meaning that the pid has been recycled.

Thank you for working on this Matthew.

I'm explicitly adding Jann and Oleg to the thread since both have been involved in the original design.

Oleg, Jann, I know this isn't necessarily your area of expertise but if you could share your thoughts about returning pidfds that haven't been opened via pidfd_open() or CLONE_PIDFD that would be great. I've been conservative about this so far but I find it hard to resist use-cases such as this.

Christian