Skip to content

Commit

Permalink
fanotify: flush outstanding perm requests on group destroy
Browse files Browse the repository at this point in the history
When an fanotify listener is closing it may cause a deadlock between the
listener and the original task doing an fs operation.  If the original task
is waiting for a permissions response it will be holding the srcu lock.  The
listener cannot clean up and exit until after that srcu lock is syncronized.
Thus deadlock.  The fix introduced here is to stop accepting new permissions
events when a listener is shutting down and to grant permission for all
outstanding events.  Thus the original task will eventually release the srcu
lock and the listener can complete shutdown.

Reported-by: Andreas Gruenbacher <agruen@suse.de>
Cc: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
  • Loading branch information
Eric Paris committed Aug 23, 2010
1 parent 84e1ab4 commit 2eebf58
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
27 changes: 27 additions & 0 deletions fs/notify/fanotify/fanotify_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ static int prepare_for_access_response(struct fsnotify_group *group,
re->fd = fd;

mutex_lock(&group->fanotify_data.access_mutex);

if (group->fanotify_data.bypass_perm) {
mutex_unlock(&group->fanotify_data.access_mutex);
kmem_cache_free(fanotify_response_event_cache, re);
event->response = FAN_ALLOW;
return 0;
}

list_add_tail(&re->list, &group->fanotify_data.access_list);
mutex_unlock(&group->fanotify_data.access_mutex);

Expand Down Expand Up @@ -364,9 +372,28 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t
static int fanotify_release(struct inode *ignored, struct file *file)
{
struct fsnotify_group *group = file->private_data;
struct fanotify_response_event *re, *lre;

pr_debug("%s: file=%p group=%p\n", __func__, file, group);

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
mutex_lock(&group->fanotify_data.access_mutex);

group->fanotify_data.bypass_perm = true;

list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) {
pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group,
re, re->event);

list_del_init(&re->list);
re->event->response = FAN_ALLOW;

kmem_cache_free(fanotify_response_event_cache, re);
}
mutex_unlock(&group->fanotify_data.access_mutex);

wake_up(&group->fanotify_data.access_waitq);
#endif
/* matches the fanotify_init->fsnotify_alloc_group */
fsnotify_put_group(group);

Expand Down
7 changes: 0 additions & 7 deletions include/linux/fanotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,4 @@ struct fanotify_response {
(long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
(long)(meta)->event_len <= (long)(len))

#ifdef __KERNEL__

struct fanotify_wait {
struct fsnotify_event *event;
__s32 fd;
};
#endif /* __KERNEL__ */
#endif /* _LINUX_FANOTIFY_H */
1 change: 1 addition & 0 deletions include/linux/fsnotify_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ struct fsnotify_group {
struct mutex access_mutex;
struct list_head access_list;
wait_queue_head_t access_waitq;
bool bypass_perm; /* protected by access_mutex */
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
int f_flags;
} fanotify_data;
Expand Down

0 comments on commit 2eebf58

Please sign in to comment.