From 40873284d7106fc0f0f4d2deae74b38fb18342cc Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 8 Jan 2019 14:02:44 +0100 Subject: [PATCH] fanotify: Track permission event state Track whether permission event got already reported to userspace and whether userspace already answered to the permission event. Protect stores to this field together with updates to ->response field by group->notification_lock. This will allow aborting wait for reply to permission event from userspace. Reviewed-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify.c | 6 +++--- fs/notify/fanotify/fanotify.h | 10 +++++++++- fs/notify/fanotify/fanotify_user.c | 35 ++++++++++++++++++++++++++++------- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 4ff84bc..812c975 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -85,7 +85,8 @@ static int fanotify_get_response(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); - wait_event(group->fanotify_data.access_waitq, event->response); + wait_event(group->fanotify_data.access_waitq, + event->state == FAN_EVENT_ANSWERED); /* userspace responded, convert to something usable */ switch (event->response & ~FAN_AUDIT) { @@ -101,8 +102,6 @@ static int fanotify_get_response(struct fsnotify_group *group, if (event->response & FAN_AUDIT) audit_fanotify(event->response & ~FAN_AUDIT); - event->response = 0; - pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, group, event, ret); @@ -275,6 +274,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, goto out; event = &pevent->fae; pevent->response = 0; + pevent->state = FAN_EVENT_INIT; goto init; } event = kmem_cache_alloc(fanotify_event_cachep, gfp); diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index e84d68c..480f2819 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -8,6 +8,13 @@ extern struct kmem_cache *fanotify_mark_cache; extern struct kmem_cache *fanotify_event_cachep; extern struct kmem_cache *fanotify_perm_event_cachep; +/* Possible states of the permission event */ +enum { + FAN_EVENT_INIT, + FAN_EVENT_REPORTED, + FAN_EVENT_ANSWERED +}; + /* * 3 dwords are sufficient for most local fs (64bit ino, 32bit generation). * For 32bit arch, fid increases the size of fanotify_event by 12 bytes and @@ -109,7 +116,8 @@ static inline void *fanotify_event_fh(struct fanotify_event *event) */ struct fanotify_perm_event { struct fanotify_event fae; - int response; /* userspace answer to question */ + unsigned short response; /* userspace answer to the event */ + unsigned short state; /* state of the event */ int fd; /* fd we passed to userspace for this event */ }; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a73ada4..3c272f6 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -64,7 +64,8 @@ static int fanotify_event_info_len(struct fanotify_event *event) /* * Get an fsnotify notification event if one exists and is small * enough to fit in "count". Return an error pointer if the count - * is not large enough. + * is not large enough. When permission event is dequeued, its state is + * updated accordingly. */ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, size_t count) @@ -88,6 +89,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, goto out; } fsn_event = fsnotify_remove_first_event(group); + if (fanotify_is_perm_event(FANOTIFY_E(fsn_event)->mask)) + FANOTIFY_PE(fsn_event)->state = FAN_EVENT_REPORTED; out: spin_unlock(&group->notification_lock); return fsn_event; @@ -135,6 +138,21 @@ static int create_fd(struct fsnotify_group *group, return client_fd; } +/* + * Finish processing of permission event by setting it to ANSWERED state and + * drop group->notification_lock. + */ +static void finish_permission_event(struct fsnotify_group *group, + struct fanotify_perm_event *event, + unsigned int response) + __releases(&group->notification_lock) +{ + assert_spin_locked(&group->notification_lock); + event->response = response; + event->state = FAN_EVENT_ANSWERED; + spin_unlock(&group->notification_lock); +} + static int process_access_response(struct fsnotify_group *group, struct fanotify_response *response_struct) { @@ -170,8 +188,7 @@ static int process_access_response(struct fsnotify_group *group, continue; list_del_init(&event->fae.fse.list); - event->response = response; - spin_unlock(&group->notification_lock); + finish_permission_event(group, event, response); wake_up(&group->fanotify_data.access_waitq); return 0; } @@ -354,7 +371,9 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, fsnotify_destroy_event(group, kevent); } else { if (ret <= 0) { - FANOTIFY_PE(kevent)->response = FAN_DENY; + spin_lock(&group->notification_lock); + finish_permission_event(group, + FANOTIFY_PE(kevent), FAN_DENY); wake_up(&group->fanotify_data.access_waitq); } else { spin_lock(&group->notification_lock); @@ -423,7 +442,8 @@ static int fanotify_release(struct inode *ignored, struct file *file) event = list_first_entry(&group->fanotify_data.access_list, struct fanotify_perm_event, fae.fse.list); list_del_init(&event->fae.fse.list); - event->response = FAN_ALLOW; + finish_permission_event(group, event, FAN_ALLOW); + spin_lock(&group->notification_lock); } /* @@ -436,10 +456,11 @@ static int fanotify_release(struct inode *ignored, struct file *file) if (!(FANOTIFY_E(fsn_event)->mask & FANOTIFY_PERM_EVENTS)) { spin_unlock(&group->notification_lock); fsnotify_destroy_event(group, fsn_event); - spin_lock(&group->notification_lock); } else { - FANOTIFY_PE(fsn_event)->response = FAN_ALLOW; + finish_permission_event(group, FANOTIFY_PE(fsn_event), + FAN_ALLOW); } + spin_lock(&group->notification_lock); } spin_unlock(&group->notification_lock); -- 2.7.4