aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLino Sanfilippo <LinoSanfilippo@gmx.de>2010-11-19 04:58:07 -0500
committerEric Paris <eparis@redhat.com>2010-12-07 16:14:22 -0500
commit09e5f14e57c70f9d357862bb56e57026c51092a1 (patch)
treee6e9c38a15e4ea562dcb0ac600ae37831dd16b49 /fs
parent1734dee4e3a296cb72b4819fc2e7ef2440737dff (diff)
fanotify: on group destroy allow all waiters to bypass permission check
When fanotify_release() is called, there may still be processes waiting for access permission. Currently only processes for which an event has already been queued into the groups access list will be woken up. Processes for which no event has been queued will continue to sleep and thus cause a deadlock when fsnotify_put_group() is called. Furthermore there is a race allowing further processes to be waiting on the access wait queue after wake_up (if they arrive before clear_marks_by_group() is called). This patch corrects this by setting a flag to inform processes that the group is about to be destroyed and thus not to wait for access permission. [additional changelog from eparis] Lets think about the 4 relevant code paths from the PoV of the 'operator' 'listener' 'responder' and 'closer'. Where operator is the process doing an action (like open/read) which could require permission. Listener is the task (or in this case thread) slated with reading from the fanotify file descriptor. The 'responder' is the thread responsible for responding to access requests. 'Closer' is the thread attempting to close the fanotify file descriptor. The 'operator' is going to end up in: fanotify_handle_event() get_response_from_access() (THIS BLOCKS WAITING ON USERSPACE) The 'listener' interesting code path fanotify_read() copy_event_to_user() prepare_for_access_response() (THIS CREATES AN fanotify_response_event) The 'responder' code path: fanotify_write() process_access_response() (REMOVE A fanotify_response_event, SET RESPONSE, WAKE UP 'operator') The 'closer': fanotify_release() (SUPPOSED TO CLEAN UP THE REST OF THIS MESS) What we have today is that in the closer we remove all of the fanotify_response_events and set a bit so no more response events are ever created in prepare_for_access_response(). The bug is that we never wake all of the operators up and tell them to move along. You fix that in fanotify_get_response_from_access(). You also fix other operators which haven't gotten there yet. So I agree that's a good fix. [/additional changelog from eparis] [remove additional changes to minimize patch size] [move initialization so it was inside CONFIG_FANOTIFY_PERMISSION] Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de> Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify.c6
-rw-r--r--fs/notify/fanotify/fanotify_user.c5
2 files changed, 8 insertions, 3 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index b04f88eed09e..f35794b97e8e 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -92,7 +92,11 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group,
92 92
93 pr_debug("%s: group=%p event=%p\n", __func__, group, event); 93 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
94 94
95 wait_event(group->fanotify_data.access_waitq, event->response); 95 wait_event(group->fanotify_data.access_waitq, event->response ||
96 atomic_read(&group->fanotify_data.bypass_perm));
97
98 if (!event->response) /* bypass_perm set */
99 return 0;
96 100
97 /* userspace responded, convert to something usable */ 101 /* userspace responded, convert to something usable */
98 spin_lock(&event->lock); 102 spin_lock(&event->lock);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 480434c5ee5f..01fffe62a2d4 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -200,7 +200,7 @@ static int prepare_for_access_response(struct fsnotify_group *group,
200 200
201 mutex_lock(&group->fanotify_data.access_mutex); 201 mutex_lock(&group->fanotify_data.access_mutex);
202 202
203 if (group->fanotify_data.bypass_perm) { 203 if (atomic_read(&group->fanotify_data.bypass_perm)) {
204 mutex_unlock(&group->fanotify_data.access_mutex); 204 mutex_unlock(&group->fanotify_data.access_mutex);
205 kmem_cache_free(fanotify_response_event_cache, re); 205 kmem_cache_free(fanotify_response_event_cache, re);
206 event->response = FAN_ALLOW; 206 event->response = FAN_ALLOW;
@@ -390,7 +390,7 @@ static int fanotify_release(struct inode *ignored, struct file *file)
390 390
391 mutex_lock(&group->fanotify_data.access_mutex); 391 mutex_lock(&group->fanotify_data.access_mutex);
392 392
393 group->fanotify_data.bypass_perm = true; 393 atomic_inc(&group->fanotify_data.bypass_perm);
394 394
395 list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { 395 list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) {
396 pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, 396 pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group,
@@ -703,6 +703,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
703 mutex_init(&group->fanotify_data.access_mutex); 703 mutex_init(&group->fanotify_data.access_mutex);
704 init_waitqueue_head(&group->fanotify_data.access_waitq); 704 init_waitqueue_head(&group->fanotify_data.access_waitq);
705 INIT_LIST_HEAD(&group->fanotify_data.access_list); 705 INIT_LIST_HEAD(&group->fanotify_data.access_list);
706 atomic_set(&group->fanotify_data.bypass_perm, 0);
706#endif 707#endif
707 switch (flags & FAN_ALL_CLASS_BITS) { 708 switch (flags & FAN_ALL_CLASS_BITS) {
708 case FAN_CLASS_NOTIF: 709 case FAN_CLASS_NOTIF: