mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
Merge tag 'fsnotify_for_v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull fsnotify updates from Jan Kara: - a couple of small cleanups and fixes - implement fanotify watchdog that reports processes that fail to respond to fanotify permission events in a given time * tag 'fsnotify_for_v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: fanotify: add watchdog for permission events fanotify: Validate the return value of mnt_ns_from_dentry() before dereferencing fsnotify: fix "rewriten"->"rewritten"
This commit is contained in:
@@ -441,7 +441,9 @@ struct fanotify_perm_event {
|
||||
size_t count;
|
||||
u32 response; /* userspace answer to the event */
|
||||
unsigned short state; /* state of the event */
|
||||
unsigned short watchdog_cnt; /* already scanned by watchdog? */
|
||||
int fd; /* fd we passed to userspace for this event */
|
||||
pid_t recv_pid; /* pid of task receiving the event */
|
||||
union {
|
||||
struct fanotify_response_info_header hdr;
|
||||
struct fanotify_response_info_audit_rule audit_rule;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
/* configurable via /proc/sys/fs/fanotify/ */
|
||||
static int fanotify_max_queued_events __read_mostly;
|
||||
static int perm_group_timeout __read_mostly;
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
|
||||
@@ -85,6 +86,14 @@ static const struct ctl_table fanotify_table[] = {
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO
|
||||
},
|
||||
{
|
||||
.procname = "watchdog_timeout",
|
||||
.data = &perm_group_timeout,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
},
|
||||
};
|
||||
|
||||
static void __init fanotify_sysctls_init(void)
|
||||
@@ -95,6 +104,91 @@ static void __init fanotify_sysctls_init(void)
|
||||
#define fanotify_sysctls_init() do { } while (0)
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static LIST_HEAD(perm_group_list);
|
||||
static DEFINE_SPINLOCK(perm_group_lock);
|
||||
static void perm_group_watchdog(struct work_struct *work);
|
||||
static DECLARE_DELAYED_WORK(perm_group_work, perm_group_watchdog);
|
||||
|
||||
static void perm_group_watchdog_schedule(void)
|
||||
{
|
||||
schedule_delayed_work(&perm_group_work, secs_to_jiffies(perm_group_timeout));
|
||||
}
|
||||
|
||||
static void perm_group_watchdog(struct work_struct *work)
|
||||
{
|
||||
struct fsnotify_group *group;
|
||||
struct fanotify_perm_event *event;
|
||||
struct task_struct *task;
|
||||
pid_t failed_pid = 0;
|
||||
|
||||
guard(spinlock)(&perm_group_lock);
|
||||
if (list_empty(&perm_group_list))
|
||||
return;
|
||||
|
||||
list_for_each_entry(group, &perm_group_list,
|
||||
fanotify_data.perm_grp_list) {
|
||||
/*
|
||||
* Ok to test without lock, racing with an addition is
|
||||
* fine, will deal with it next round
|
||||
*/
|
||||
if (list_empty(&group->fanotify_data.access_list))
|
||||
continue;
|
||||
|
||||
spin_lock(&group->notification_lock);
|
||||
list_for_each_entry(event, &group->fanotify_data.access_list,
|
||||
fae.fse.list) {
|
||||
if (likely(event->watchdog_cnt == 0)) {
|
||||
event->watchdog_cnt = 1;
|
||||
} else if (event->watchdog_cnt == 1) {
|
||||
/* Report on event only once */
|
||||
event->watchdog_cnt = 2;
|
||||
|
||||
/* Do not report same pid repeatedly */
|
||||
if (event->recv_pid == failed_pid)
|
||||
continue;
|
||||
|
||||
failed_pid = event->recv_pid;
|
||||
rcu_read_lock();
|
||||
task = find_task_by_pid_ns(event->recv_pid,
|
||||
&init_pid_ns);
|
||||
pr_warn_ratelimited(
|
||||
"PID %u (%s) failed to respond to fanotify queue for more than %d seconds\n",
|
||||
event->recv_pid,
|
||||
task ? task->comm : NULL,
|
||||
perm_group_timeout);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
spin_unlock(&group->notification_lock);
|
||||
}
|
||||
perm_group_watchdog_schedule();
|
||||
}
|
||||
|
||||
static void fanotify_perm_watchdog_group_remove(struct fsnotify_group *group)
|
||||
{
|
||||
if (!list_empty(&group->fanotify_data.perm_grp_list)) {
|
||||
/* Perm event watchdog can no longer scan this group. */
|
||||
spin_lock(&perm_group_lock);
|
||||
list_del_init(&group->fanotify_data.perm_grp_list);
|
||||
spin_unlock(&perm_group_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void fanotify_perm_watchdog_group_add(struct fsnotify_group *group)
|
||||
{
|
||||
if (!perm_group_timeout)
|
||||
return;
|
||||
|
||||
spin_lock(&perm_group_lock);
|
||||
if (list_empty(&group->fanotify_data.perm_grp_list)) {
|
||||
/* Add to perm_group_list for monitoring by watchdog. */
|
||||
if (list_empty(&perm_group_list))
|
||||
perm_group_watchdog_schedule();
|
||||
list_add_tail(&group->fanotify_data.perm_grp_list, &perm_group_list);
|
||||
}
|
||||
spin_unlock(&perm_group_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* All flags that may be specified in parameter event_f_flags of fanotify_init.
|
||||
*
|
||||
@@ -953,6 +1047,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
|
||||
spin_lock(&group->notification_lock);
|
||||
list_add_tail(&event->fse.list,
|
||||
&group->fanotify_data.access_list);
|
||||
FANOTIFY_PERM(event)->recv_pid = current->pid;
|
||||
spin_unlock(&group->notification_lock);
|
||||
}
|
||||
}
|
||||
@@ -1012,6 +1107,8 @@ static int fanotify_release(struct inode *ignored, struct file *file)
|
||||
*/
|
||||
fsnotify_group_stop_queueing(group);
|
||||
|
||||
fanotify_perm_watchdog_group_remove(group);
|
||||
|
||||
/*
|
||||
* Process all permission events on access_list and notification queue
|
||||
* and simulate reply from userspace.
|
||||
@@ -1465,6 +1562,10 @@ out:
|
||||
fsnotify_group_unlock(group);
|
||||
|
||||
fsnotify_put_mark(fsn_mark);
|
||||
|
||||
if (!ret && (mask & FANOTIFY_PERM_EVENTS))
|
||||
fanotify_perm_watchdog_group_add(group);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1625,6 +1726,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
|
||||
group->fanotify_data.f_flags = event_f_flags;
|
||||
init_waitqueue_head(&group->fanotify_data.access_waitq);
|
||||
INIT_LIST_HEAD(&group->fanotify_data.access_list);
|
||||
INIT_LIST_HEAD(&group->fanotify_data.perm_grp_list);
|
||||
switch (class) {
|
||||
case FAN_CLASS_NOTIF:
|
||||
group->priority = FSNOTIFY_PRIO_NORMAL;
|
||||
@@ -1999,7 +2101,10 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
|
||||
user_ns = path.mnt->mnt_sb->s_user_ns;
|
||||
obj = path.mnt->mnt_sb;
|
||||
} else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) {
|
||||
ret = -EINVAL;
|
||||
mntns = mnt_ns_from_dentry(path.dentry);
|
||||
if (!mntns)
|
||||
goto path_put_and_out;
|
||||
user_ns = mntns->user_ns;
|
||||
obj = mntns;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* Copyright 2006 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Copyright (C) 2009 Eric Paris <Red Hat Inc>
|
||||
* inotify was largely rewriten to make use of the fsnotify infrastructure
|
||||
* inotify was largely rewritten to make use of the fsnotify infrastructure
|
||||
*/
|
||||
|
||||
#include <linux/dcache.h> /* d_unlinked */
|
||||
|
||||
@@ -273,6 +273,8 @@ struct fsnotify_group {
|
||||
int f_flags; /* event_f_flags from fanotify_init() */
|
||||
struct ucounts *ucounts;
|
||||
mempool_t error_events_pool;
|
||||
/* chained on perm_group_list */
|
||||
struct list_head perm_grp_list;
|
||||
} fanotify_data;
|
||||
#endif /* CONFIG_FANOTIFY */
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user