notify: unused event private race

inotify decides if private data it passed to get added to an event was
used by checking list_empty().  But it's possible that the event may
have been dequeued and the private event removed so it would look empty.

The fix is to use the return code from fsnotify_add_notify_event rather
than looking at the list.

Signed-off-by: Eric Paris <eparis@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Eric Paris 2009-08-16 21:51:44 -04:00 committed by Linus Torvalds
parent 0f66f96d21
commit eef3a116be
3 changed files with 13 additions and 14 deletions

View file

@ -62,13 +62,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
event_priv->wd = wd; event_priv->wd = wd;
ret = fsnotify_add_notify_event(group, event, fsn_event_priv); ret = fsnotify_add_notify_event(group, event, fsn_event_priv);
/* EEXIST is not an error */ if (ret) {
if (ret == -EEXIST)
ret = 0;
/* did event_priv get attached? */
if (list_empty(&fsn_event_priv->event_list))
inotify_free_event_priv(fsn_event_priv); inotify_free_event_priv(fsn_event_priv);
/* EEXIST says we tail matched, EOVERFLOW isn't something
* to report up the stack. */
if ((ret == -EEXIST) ||
(ret == -EOVERFLOW))
ret = 0;
}
/* /*
* If we hold the entry until after the event is on the queue * If we hold the entry until after the event is on the queue

View file

@ -386,6 +386,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
struct fsnotify_event *ignored_event; struct fsnotify_event *ignored_event;
struct inotify_event_private_data *event_priv; struct inotify_event_private_data *event_priv;
struct fsnotify_event_private_data *fsn_event_priv; struct fsnotify_event_private_data *fsn_event_priv;
int ret;
ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL, ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL,
FSNOTIFY_EVENT_NONE, NULL, 0, FSNOTIFY_EVENT_NONE, NULL, 0,
@ -404,10 +405,8 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
fsn_event_priv->group = group; fsn_event_priv->group = group;
event_priv->wd = ientry->wd; event_priv->wd = ientry->wd;
fsnotify_add_notify_event(group, ignored_event, fsn_event_priv); ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv);
if (ret)
/* did the private data get added? */
if (list_empty(&fsn_event_priv->event_list))
inotify_free_event_priv(fsn_event_priv); inotify_free_event_priv(fsn_event_priv);
skip_send_ignore: skip_send_ignore:

View file

@ -171,9 +171,7 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
struct list_head *list = &group->notification_list; struct list_head *list = &group->notification_list;
struct fsnotify_event_holder *last_holder; struct fsnotify_event_holder *last_holder;
struct fsnotify_event *last_event; struct fsnotify_event *last_event;
int ret = 0;
/* easy to tell if priv was attached to the event */
INIT_LIST_HEAD(&priv->event_list);
/* /*
* There is one fsnotify_event_holder embedded inside each fsnotify_event. * There is one fsnotify_event_holder embedded inside each fsnotify_event.
@ -194,6 +192,7 @@ alloc_holder:
if (group->q_len >= group->max_events) { if (group->q_len >= group->max_events) {
event = &q_overflow_event; event = &q_overflow_event;
ret = -EOVERFLOW;
/* sorry, no private data on the overflow event */ /* sorry, no private data on the overflow event */
priv = NULL; priv = NULL;
} }
@ -235,7 +234,7 @@ alloc_holder:
mutex_unlock(&group->notification_mutex); mutex_unlock(&group->notification_mutex);
wake_up(&group->notification_waitq); wake_up(&group->notification_waitq);
return 0; return ret;
} }
/* /*