-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmc/WikACgkQnJ2qBz9k
 QNnZdwf9FfT95zhnNWk3ohNOh5BO0P/uTY2fNkQBDPLPY3Bi8nywPIjXYCDSOgX1
 SBV0rakkWp+rVO1/qkg5J1mUvBoefzT7O17rG0LfRw3zjHPX+XeO+e3Xf/kPmJHJ
 3fvN//VTZQ6uPcn8PWgLe8VVQqNXD3nlUrwz/JKaxyodsdm0ERej4QZjG6Cikotk
 aKuDPAnOiS37/lIFZGdJRca/rwJPwMekNt1SxVrnmin0/QfB/Uubba2+NNdQ+z3W
 SCA/26PK822T3ELB8BkfwpdINC17WUwDJlkC8qha/JRzDlxJC/ysr43fHn/7Adfb
 CthG8V4JDGm51jcC0qe0Yk2HV75U4A==
 =htHs
 -----END PGP SIGNATURE-----

Merge tag 'fsnotify_for_v6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull fsnotify updates from Jan Kara:
 "A couple of smaller random fsnotify fixes"

* tag 'fsnotify_for_v6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: Fix ordering of iput() and watched_objects decrement
  fsnotify: fix sending inotify event with unexpected filename
  fanotify: allow reporting errors on failure to open fd
  fsnotify, lsm: Decouple fsnotify from lsm
This commit is contained in:
Linus Torvalds 2024-11-21 09:55:45 -08:00
commit 2dde263d81
8 changed files with 77 additions and 59 deletions

View File

@ -15,7 +15,6 @@ config FANOTIFY
config FANOTIFY_ACCESS_PERMISSIONS config FANOTIFY_ACCESS_PERMISSIONS
bool "fanotify permissions checking" bool "fanotify permissions checking"
depends on FANOTIFY depends on FANOTIFY
depends on SECURITY
default n default n
help help
Say Y here is you want fanotify listeners to be able to make permissions Say Y here is you want fanotify listeners to be able to make permissions

View File

@ -265,13 +265,6 @@ static int create_fd(struct fsnotify_group *group, const struct path *path,
group->fanotify_data.f_flags | __FMODE_NONOTIFY, group->fanotify_data.f_flags | __FMODE_NONOTIFY,
current_cred()); current_cred());
if (IS_ERR(new_file)) { if (IS_ERR(new_file)) {
/*
* we still send an event even if we can't open the file. this
* can happen when say tasks are gone and we try to open their
* /proc files or we try to open a WRONLY file like in sysfs
* we just send the errno to userspace since there isn't much
* else we can do.
*/
put_unused_fd(client_fd); put_unused_fd(client_fd);
client_fd = PTR_ERR(new_file); client_fd = PTR_ERR(new_file);
} else { } else {
@ -662,7 +655,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD; unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
struct file *f = NULL, *pidfd_file = NULL; struct file *f = NULL, *pidfd_file = NULL;
int ret, pidfd = FAN_NOPIDFD, fd = FAN_NOFD; int ret, pidfd = -ESRCH, fd = -EBADF;
pr_debug("%s: group=%p event=%p\n", __func__, group, event); pr_debug("%s: group=%p event=%p\n", __func__, group, event);
@ -690,10 +683,39 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) && if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
path && path->mnt && path->dentry) { path && path->mnt && path->dentry) {
fd = create_fd(group, path, &f); fd = create_fd(group, path, &f);
if (fd < 0) /*
return fd; * 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.
*
* 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.
* In any case, userspace will not know which file failed to
* open, so add a debug print for further investigation.
*/
if (fd < 0) {
pr_debug("fanotify: create_fd(%pd2) failed err=%d\n",
path->dentry, fd);
if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR)) {
/*
* Historically, we've handled EOPENSTALE in a
* special way and silently dropped such
* events. Now we have to keep it to maintain
* backward compatibility...
*/
if (fd == -EOPENSTALE)
fd = 0;
return fd;
}
}
} }
metadata.fd = fd; if (FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR))
metadata.fd = fd;
else
metadata.fd = fd >= 0 ? fd : FAN_NOFD;
if (pidfd_mode) { if (pidfd_mode) {
/* /*
@ -708,18 +730,16 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
* The PIDTYPE_TGID check for an event->pid is performed * The PIDTYPE_TGID check for an event->pid is performed
* preemptively in an attempt to catch out cases where the event * preemptively in an attempt to catch out cases where the event
* listener reads events after the event generating process has * listener reads events after the event generating process has
* already terminated. Report FAN_NOPIDFD to the event listener * already terminated. Depending on flag FAN_REPORT_FD_ERROR,
* in those cases, with all other pidfd creation errors being * report either -ESRCH or FAN_NOPIDFD to the event listener in
* reported as FAN_EPIDFD. * those cases with all other pidfd creation errors reported as
* the error code itself or as FAN_EPIDFD.
*/ */
if (metadata.pid == 0 || if (metadata.pid && pid_has_task(event->pid, PIDTYPE_TGID))
!pid_has_task(event->pid, PIDTYPE_TGID)) {
pidfd = FAN_NOPIDFD;
} else {
pidfd = pidfd_prepare(event->pid, 0, &pidfd_file); pidfd = pidfd_prepare(event->pid, 0, &pidfd_file);
if (pidfd < 0)
pidfd = FAN_EPIDFD; if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR) && pidfd < 0)
} pidfd = pidfd == -ESRCH ? FAN_NOPIDFD : FAN_EPIDFD;
} }
ret = -EFAULT; ret = -EFAULT;
@ -736,9 +756,6 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
buf += FAN_EVENT_METADATA_LEN; buf += FAN_EVENT_METADATA_LEN;
count -= FAN_EVENT_METADATA_LEN; count -= FAN_EVENT_METADATA_LEN;
if (fanotify_is_perm_event(event->mask))
FANOTIFY_PERM(event)->fd = fd;
if (info_mode) { if (info_mode) {
ret = copy_info_records_to_user(event, info, info_mode, pidfd, ret = copy_info_records_to_user(event, info, info_mode, pidfd,
buf, count); buf, count);
@ -752,15 +769,18 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
if (pidfd_file) if (pidfd_file)
fd_install(pidfd, pidfd_file); fd_install(pidfd, pidfd_file);
if (fanotify_is_perm_event(event->mask))
FANOTIFY_PERM(event)->fd = fd;
return metadata.event_len; return metadata.event_len;
out_close_fd: out_close_fd:
if (fd != FAN_NOFD) { if (f) {
put_unused_fd(fd); put_unused_fd(fd);
fput(f); fput(f);
} }
if (pidfd >= 0) { if (pidfd_file) {
put_unused_fd(pidfd); put_unused_fd(pidfd);
fput(pidfd_file); fput(pidfd_file);
} }
@ -827,15 +847,6 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
} }
ret = copy_event_to_user(group, event, buf, count); ret = copy_event_to_user(group, event, buf, count);
if (unlikely(ret == -EOPENSTALE)) {
/*
* We cannot report events with stale fd so drop it.
* Setting ret to 0 will continue the event loop and
* do the right thing if there are no more events to
* read (i.e. return bytes read, -EAGAIN or wait).
*/
ret = 0;
}
/* /*
* Permission events get queued to wait for response. Other * Permission events get queued to wait for response. Other
@ -844,7 +855,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
if (!fanotify_is_perm_event(event->mask)) { if (!fanotify_is_perm_event(event->mask)) {
fsnotify_destroy_event(group, &event->fse); fsnotify_destroy_event(group, &event->fse);
} else { } else {
if (ret <= 0) { if (ret <= 0 || FANOTIFY_PERM(event)->fd < 0) {
spin_lock(&group->notification_lock); spin_lock(&group->notification_lock);
finish_permission_event(group, finish_permission_event(group,
FANOTIFY_PERM(event), FAN_DENY, NULL); FANOTIFY_PERM(event), FAN_DENY, NULL);
@ -1941,7 +1952,7 @@ static int __init fanotify_user_setup(void)
FANOTIFY_DEFAULT_MAX_USER_MARKS); FANOTIFY_DEFAULT_MAX_USER_MARKS);
BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12); BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11); BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
fanotify_mark_cache = KMEM_CACHE(fanotify_mark, fanotify_mark_cache = KMEM_CACHE(fanotify_mark,

View File

@ -333,16 +333,19 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
if (!inode_mark) if (!inode_mark)
return 0; return 0;
if (mask & FS_EVENT_ON_CHILD) { /*
/* * Some events can be sent on both parent dir and child marks (e.g.
* Some events can be sent on both parent dir and child marks * FS_ATTRIB). If both parent dir and child are watching, report the
* (e.g. FS_ATTRIB). If both parent dir and child are * event once to parent dir with name (if interested) and once to child
* watching, report the event once to parent dir with name (if * without name (if interested).
* interested) and once to child without name (if interested). *
* The child watcher is expecting an event without a file name * In any case regardless whether the parent is watching or not, the
* and without the FS_EVENT_ON_CHILD flag. * child watcher is expecting an event without the FS_EVENT_ON_CHILD
*/ * flag. The file name is expected if and only if this is a directory
mask &= ~FS_EVENT_ON_CHILD; * event.
*/
mask &= ~FS_EVENT_ON_CHILD;
if (!(mask & ALL_FSNOTIFY_DIRENT_EVENTS)) {
dir = NULL; dir = NULL;
name = NULL; name = NULL;
} }

View File

@ -138,8 +138,11 @@ static void fsnotify_get_sb_watched_objects(struct super_block *sb)
static void fsnotify_put_sb_watched_objects(struct super_block *sb) static void fsnotify_put_sb_watched_objects(struct super_block *sb)
{ {
if (atomic_long_dec_and_test(fsnotify_sb_watched_objects(sb))) atomic_long_t *watched_objects = fsnotify_sb_watched_objects(sb);
wake_up_var(fsnotify_sb_watched_objects(sb));
/* the superblock can go away after this decrement */
if (atomic_long_dec_and_test(watched_objects))
wake_up_var(watched_objects);
} }
static void fsnotify_get_inode_ref(struct inode *inode) static void fsnotify_get_inode_ref(struct inode *inode)
@ -150,8 +153,11 @@ static void fsnotify_get_inode_ref(struct inode *inode)
static void fsnotify_put_inode_ref(struct inode *inode) static void fsnotify_put_inode_ref(struct inode *inode)
{ {
fsnotify_put_sb_watched_objects(inode->i_sb); /* read ->i_sb before the inode can go away */
struct super_block *sb = inode->i_sb;
iput(inode); iput(inode);
fsnotify_put_sb_watched_objects(sb);
} }
/* /*

View File

@ -929,6 +929,10 @@ static int do_dentry_open(struct file *f,
if (error) if (error)
goto cleanup_all; goto cleanup_all;
error = fsnotify_open_perm(f);
if (error)
goto cleanup_all;
error = break_lease(file_inode(f), f->f_flags); error = break_lease(file_inode(f), f->f_flags);
if (error) if (error)
goto cleanup_all; goto cleanup_all;

View File

@ -36,6 +36,7 @@
#define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \ #define FANOTIFY_ADMIN_INIT_FLAGS (FANOTIFY_PERM_CLASSES | \
FAN_REPORT_TID | \ FAN_REPORT_TID | \
FAN_REPORT_PIDFD | \ FAN_REPORT_PIDFD | \
FAN_REPORT_FD_ERROR | \
FAN_UNLIMITED_QUEUE | \ FAN_UNLIMITED_QUEUE | \
FAN_UNLIMITED_MARKS) FAN_UNLIMITED_MARKS)

View File

@ -60,6 +60,7 @@
#define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */ #define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */
#define FAN_REPORT_NAME 0x00000800 /* Report events with name */ #define FAN_REPORT_NAME 0x00000800 /* Report events with name */
#define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */ #define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */
#define FAN_REPORT_FD_ERROR 0x00002000 /* event->fd can report error */
/* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */ /* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */
#define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME) #define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME)

View File

@ -19,7 +19,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kernel_read_file.h> #include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h> #include <linux/lsm_hooks.h>
#include <linux/fsnotify.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/personality.h> #include <linux/personality.h>
@ -3103,13 +3102,7 @@ int security_file_receive(struct file *file)
*/ */
int security_file_open(struct file *file) int security_file_open(struct file *file)
{ {
int ret; return call_int_hook(file_open, file);
ret = call_int_hook(file_open, file);
if (ret)
return ret;
return fsnotify_open_perm(file);
} }
/** /**