Skip to content

Commit 522249f

Browse files
amir73iljankara
authored andcommitted
fanotify: allow reporting errors on failure to open fd
When working in "fd mode", fanotify_read() needs to open an fd from a dentry to report event->fd to userspace. Opening an fd from dentry can fail for several reasons. For example, when tasks are gone and we try to open their /proc files or we try to open a WRONLY file like in sysfs or when trying to open a file that was deleted on the remote network server. Add a new flag FAN_REPORT_FD_ERROR for fanotify_init(). For a group with FAN_REPORT_FD_ERROR, we will send the event with the error instead of the open fd, otherwise userspace may not get the error at all. For an overflow event, we report -EBADF to avoid confusing FAN_NOFD with -EPERM. Similarly for pidfd open errors we report either -ESRCH or the open error instead of FAN_NOPIDFD and FAN_EPIDFD. In any case, userspace will not know which file failed to open, so add a debug print for further investigation. Reported-by: Krishna Vivek Vitta <kvitta@microsoft.com> Link: https://lore.kernel.org/linux-fsdevel/SI2P153MB07182F3424619EDDD1F393EED46D2@SI2P153MB0718.APCP153.PROD.OUTLOOK.COM/ Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20241003142922.111539-1-amir73il@gmail.com
1 parent 1cda52f commit 522249f

File tree

3 files changed

+50
-37
lines changed

3 files changed

+50
-37
lines changed

fs/notify/fanotify/fanotify_user.c

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,6 @@ static int create_fd(struct fsnotify_group *group, const struct path *path,
266266
group->fanotify_data.f_flags | __FMODE_NONOTIFY,
267267
current_cred());
268268
if (IS_ERR(new_file)) {
269-
/*
270-
* we still send an event even if we can't open the file. this
271-
* can happen when say tasks are gone and we try to open their
272-
* /proc files or we try to open a WRONLY file like in sysfs
273-
* we just send the errno to userspace since there isn't much
274-
* else we can do.
275-
*/
276269
put_unused_fd(client_fd);
277270
client_fd = PTR_ERR(new_file);
278271
} else {
@@ -663,7 +656,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
663656
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
664657
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
665658
struct file *f = NULL, *pidfd_file = NULL;
666-
int ret, pidfd = FAN_NOPIDFD, fd = FAN_NOFD;
659+
int ret, pidfd = -ESRCH, fd = -EBADF;
667660

668661
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
669662

@@ -691,10 +684,39 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
691684
if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
692685
path && path->mnt && path->dentry) {
693686
fd = create_fd(group, path, &f);
694-
if (fd < 0)
695-
return fd;
687+
/*
688+
* Opening an fd from dentry can fail for several reasons.
689+
* For example, when tasks are gone and we try to open their
690+
* /proc files or we try to open a WRONLY file like in sysfs
691+
* or when trying to open a file that was deleted on the
692+
* remote network server.
693+
*
694+
* For a group with FAN_REPORT_FD_ERROR, we will send the
695+
* event with the error instead of the open fd, otherwise
696+
* Userspace may not get the error at all.
697+
* In any case, userspace will not know which file failed to
698+
* open, so add a debug print for further investigation.
699+
*/
700+
if (fd < 0) {
701+
pr_debug("fanotify: create_fd(%pd2) failed err=%d\n",
702+
path->dentry, fd);
703+
if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR)) {
704+
/*
705+
* Historically, we've handled EOPENSTALE in a
706+
* special way and silently dropped such
707+
* events. Now we have to keep it to maintain
708+
* backward compatibility...
709+
*/
710+
if (fd == -EOPENSTALE)
711+
fd = 0;
712+
return fd;
713+
}
714+
}
696715
}
697-
metadata.fd = fd;
716+
if (FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR))
717+
metadata.fd = fd;
718+
else
719+
metadata.fd = fd >= 0 ? fd : FAN_NOFD;
698720

699721
if (pidfd_mode) {
700722
/*
@@ -709,18 +731,16 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
709731
* The PIDTYPE_TGID check for an event->pid is performed
710732
* preemptively in an attempt to catch out cases where the event
711733
* listener reads events after the event generating process has
712-
* already terminated. Report FAN_NOPIDFD to the event listener
713-
* in those cases, with all other pidfd creation errors being
714-
* reported as FAN_EPIDFD.
734+
* already terminated. Depending on flag FAN_REPORT_FD_ERROR,
735+
* report either -ESRCH or FAN_NOPIDFD to the event listener in
736+
* those cases with all other pidfd creation errors reported as
737+
* the error code itself or as FAN_EPIDFD.
715738
*/
716-
if (metadata.pid == 0 ||
717-
!pid_has_task(event->pid, PIDTYPE_TGID)) {
718-
pidfd = FAN_NOPIDFD;
719-
} else {
739+
if (metadata.pid && pid_has_task(event->pid, PIDTYPE_TGID))
720740
pidfd = pidfd_prepare(event->pid, 0, &pidfd_file);
721-
if (pidfd < 0)
722-
pidfd = FAN_EPIDFD;
723-
}
741+
742+
if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR) && pidfd < 0)
743+
pidfd = pidfd == -ESRCH ? FAN_NOPIDFD : FAN_EPIDFD;
724744
}
725745

726746
ret = -EFAULT;
@@ -737,9 +757,6 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
737757
buf += FAN_EVENT_METADATA_LEN;
738758
count -= FAN_EVENT_METADATA_LEN;
739759

740-
if (fanotify_is_perm_event(event->mask))
741-
FANOTIFY_PERM(event)->fd = fd;
742-
743760
if (info_mode) {
744761
ret = copy_info_records_to_user(event, info, info_mode, pidfd,
745762
buf, count);
@@ -753,15 +770,18 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
753770
if (pidfd_file)
754771
fd_install(pidfd, pidfd_file);
755772

773+
if (fanotify_is_perm_event(event->mask))
774+
FANOTIFY_PERM(event)->fd = fd;
775+
756776
return metadata.event_len;
757777

758778
out_close_fd:
759-
if (fd != FAN_NOFD) {
779+
if (f) {
760780
put_unused_fd(fd);
761781
fput(f);
762782
}
763783

764-
if (pidfd >= 0) {
784+
if (pidfd_file) {
765785
put_unused_fd(pidfd);
766786
fput(pidfd_file);
767787
}
@@ -828,15 +848,6 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
828848
}
829849

830850
ret = copy_event_to_user(group, event, buf, count);
831-
if (unlikely(ret == -EOPENSTALE)) {
832-
/*
833-
* We cannot report events with stale fd so drop it.
834-
* Setting ret to 0 will continue the event loop and
835-
* do the right thing if there are no more events to
836-
* read (i.e. return bytes read, -EAGAIN or wait).
837-
*/
838-
ret = 0;
839-
}
840851

841852
/*
842853
* Permission events get queued to wait for response. Other
@@ -845,7 +856,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
845856
if (!fanotify_is_perm_event(event->mask)) {
846857
fsnotify_destroy_event(group, &event->fse);
847858
} else {
848-
if (ret <= 0) {
859+
if (ret <= 0 || FANOTIFY_PERM(event)->fd < 0) {
849860
spin_lock(&group->notification_lock);
850861
finish_permission_event(group,
851862
FANOTIFY_PERM(event), FAN_DENY, NULL);
@@ -1954,7 +1965,7 @@ static int __init fanotify_user_setup(void)
19541965
FANOTIFY_DEFAULT_MAX_USER_MARKS);
19551966

19561967
BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
1957-
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12);
1968+
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13);
19581969
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
19591970

19601971
fanotify_mark_cache = KMEM_CACHE(fanotify_mark,

include/linux/fanotify.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \
3737
FAN_REPORT_TID | \
3838
FAN_REPORT_PIDFD | \
39+
FAN_REPORT_FD_ERROR | \
3940
FAN_UNLIMITED_QUEUE | \
4041
FAN_UNLIMITED_MARKS)
4142

include/uapi/linux/fanotify.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */
6161
#define FAN_REPORT_NAME 0x00000800 /* Report events with name */
6262
#define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */
63+
#define FAN_REPORT_FD_ERROR 0x00002000 /* event->fd can report error */
6364

6465
/* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */
6566
#define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME)

0 commit comments

Comments
 (0)