diff options
| author | Jan Kara <jack@suse.cz> | 2014-01-28 12:53:22 -0500 |
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2014-01-29 07:57:10 -0500 |
| commit | 83c0e1b442b488571f4fef4a91c2fe52eed6c705 (patch) | |
| tree | b29a85223f2e5e166f075266edb1bf2e0ef5cf57 /fs | |
| parent | 13116dfd13c8c9d60ea04ece13419af2de8e2e37 (diff) | |
fsnotify: Do not return merged event from fsnotify_add_notify_event()
The event returned from fsnotify_add_notify_event() cannot ever be used
safely as the event may be freed by the time the function returns (after
dropping notification_mutex). So change the prototype to just return
whether the event was added or merged into some existing event.
Reported-and-tested-by: Jiri Kosina <jkosina@suse.cz>
Reported-and-tested-by: Dave Jones <davej@fedoraproject.org>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/notify/fanotify/fanotify.c | 18 | ||||
| -rw-r--r-- | fs/notify/inotify/inotify_fsnotify.c | 19 | ||||
| -rw-r--r-- | fs/notify/notification.c | 24 |
3 files changed, 26 insertions, 35 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index cc78e2fbc8e4..c7e5e8f54748 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
| @@ -28,8 +28,7 @@ static bool should_merge(struct fsnotify_event *old_fsn, | |||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /* and the list better be locked by something too! */ | 30 | /* and the list better be locked by something too! */ |
| 31 | static struct fsnotify_event *fanotify_merge(struct list_head *list, | 31 | static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) |
| 32 | struct fsnotify_event *event) | ||
| 33 | { | 32 | { |
| 34 | struct fsnotify_event *test_event; | 33 | struct fsnotify_event *test_event; |
| 35 | bool do_merge = false; | 34 | bool do_merge = false; |
| @@ -43,7 +42,7 @@ static struct fsnotify_event *fanotify_merge(struct list_head *list, | |||
| 43 | * one we should check for permission response. | 42 | * one we should check for permission response. |
| 44 | */ | 43 | */ |
| 45 | if (event->mask & FAN_ALL_PERM_EVENTS) | 44 | if (event->mask & FAN_ALL_PERM_EVENTS) |
| 46 | return NULL; | 45 | return 0; |
| 47 | #endif | 46 | #endif |
| 48 | 47 | ||
| 49 | list_for_each_entry_reverse(test_event, list, list) { | 48 | list_for_each_entry_reverse(test_event, list, list) { |
| @@ -54,10 +53,10 @@ static struct fsnotify_event *fanotify_merge(struct list_head *list, | |||
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | if (!do_merge) | 55 | if (!do_merge) |
| 57 | return NULL; | 56 | return 0; |
| 58 | 57 | ||
| 59 | test_event->mask |= event->mask; | 58 | test_event->mask |= event->mask; |
| 60 | return test_event; | 59 | return 1; |
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 62 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
| @@ -153,7 +152,6 @@ static int fanotify_handle_event(struct fsnotify_group *group, | |||
| 153 | int ret = 0; | 152 | int ret = 0; |
| 154 | struct fanotify_event_info *event; | 153 | struct fanotify_event_info *event; |
| 155 | struct fsnotify_event *fsn_event; | 154 | struct fsnotify_event *fsn_event; |
| 156 | struct fsnotify_event *notify_fsn_event; | ||
| 157 | 155 | ||
| 158 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); | 156 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); |
| 159 | BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); | 157 | BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); |
| @@ -192,13 +190,11 @@ static int fanotify_handle_event(struct fsnotify_group *group, | |||
| 192 | event->response = 0; | 190 | event->response = 0; |
| 193 | #endif | 191 | #endif |
| 194 | 192 | ||
| 195 | notify_fsn_event = fsnotify_add_notify_event(group, fsn_event, | 193 | ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge); |
| 196 | fanotify_merge); | 194 | if (ret) { |
| 197 | if (notify_fsn_event) { | ||
| 198 | /* Our event wasn't used in the end. Free it. */ | 195 | /* Our event wasn't used in the end. Free it. */ |
| 199 | fsnotify_destroy_event(group, fsn_event); | 196 | fsnotify_destroy_event(group, fsn_event); |
| 200 | if (IS_ERR(notify_fsn_event)) | 197 | ret = 0; |
| 201 | return PTR_ERR(notify_fsn_event); | ||
| 202 | } | 198 | } |
| 203 | 199 | ||
| 204 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 200 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index aad1a35e9af1..d5ee56348bb8 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c | |||
| @@ -53,15 +53,13 @@ static bool event_compare(struct fsnotify_event *old_fsn, | |||
| 53 | return false; | 53 | return false; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | static struct fsnotify_event *inotify_merge(struct list_head *list, | 56 | static int inotify_merge(struct list_head *list, |
| 57 | struct fsnotify_event *event) | 57 | struct fsnotify_event *event) |
| 58 | { | 58 | { |
| 59 | struct fsnotify_event *last_event; | 59 | struct fsnotify_event *last_event; |
| 60 | 60 | ||
| 61 | last_event = list_entry(list->prev, struct fsnotify_event, list); | 61 | last_event = list_entry(list->prev, struct fsnotify_event, list); |
| 62 | if (!event_compare(last_event, event)) | 62 | return event_compare(last_event, event); |
| 63 | return NULL; | ||
| 64 | return last_event; | ||
| 65 | } | 63 | } |
| 66 | 64 | ||
| 67 | int inotify_handle_event(struct fsnotify_group *group, | 65 | int inotify_handle_event(struct fsnotify_group *group, |
| @@ -73,9 +71,8 @@ int inotify_handle_event(struct fsnotify_group *group, | |||
| 73 | { | 71 | { |
| 74 | struct inotify_inode_mark *i_mark; | 72 | struct inotify_inode_mark *i_mark; |
| 75 | struct inotify_event_info *event; | 73 | struct inotify_event_info *event; |
| 76 | struct fsnotify_event *added_event; | ||
| 77 | struct fsnotify_event *fsn_event; | 74 | struct fsnotify_event *fsn_event; |
| 78 | int ret = 0; | 75 | int ret; |
| 79 | int len = 0; | 76 | int len = 0; |
| 80 | int alloc_len = sizeof(struct inotify_event_info); | 77 | int alloc_len = sizeof(struct inotify_event_info); |
| 81 | 78 | ||
| @@ -110,18 +107,16 @@ int inotify_handle_event(struct fsnotify_group *group, | |||
| 110 | if (len) | 107 | if (len) |
| 111 | strcpy(event->name, file_name); | 108 | strcpy(event->name, file_name); |
| 112 | 109 | ||
| 113 | added_event = fsnotify_add_notify_event(group, fsn_event, inotify_merge); | 110 | ret = fsnotify_add_notify_event(group, fsn_event, inotify_merge); |
| 114 | if (added_event) { | 111 | if (ret) { |
| 115 | /* Our event wasn't used in the end. Free it. */ | 112 | /* Our event wasn't used in the end. Free it. */ |
| 116 | fsnotify_destroy_event(group, fsn_event); | 113 | fsnotify_destroy_event(group, fsn_event); |
| 117 | if (IS_ERR(added_event)) | ||
| 118 | ret = PTR_ERR(added_event); | ||
| 119 | } | 114 | } |
| 120 | 115 | ||
| 121 | if (inode_mark->mask & IN_ONESHOT) | 116 | if (inode_mark->mask & IN_ONESHOT) |
| 122 | fsnotify_destroy_mark(inode_mark, group); | 117 | fsnotify_destroy_mark(inode_mark, group); |
| 123 | 118 | ||
| 124 | return ret; | 119 | return 0; |
| 125 | } | 120 | } |
| 126 | 121 | ||
| 127 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) | 122 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 952237b8e2d2..18b3c4427dca 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
| @@ -79,15 +79,15 @@ void fsnotify_destroy_event(struct fsnotify_group *group, | |||
| 79 | 79 | ||
| 80 | /* | 80 | /* |
| 81 | * Add an event to the group notification queue. The group can later pull this | 81 | * Add an event to the group notification queue. The group can later pull this |
| 82 | * event off the queue to deal with. If the event is successfully added to the | 82 | * event off the queue to deal with. The function returns 0 if the event was |
| 83 | * group's notification queue, a reference is taken on event. | 83 | * added to the queue, 1 if the event was merged with some other queued event. |
| 84 | */ | 84 | */ |
| 85 | struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, | 85 | int fsnotify_add_notify_event(struct fsnotify_group *group, |
| 86 | struct fsnotify_event *event, | 86 | struct fsnotify_event *event, |
| 87 | struct fsnotify_event *(*merge)(struct list_head *, | 87 | int (*merge)(struct list_head *, |
| 88 | struct fsnotify_event *)) | 88 | struct fsnotify_event *)) |
| 89 | { | 89 | { |
| 90 | struct fsnotify_event *return_event = NULL; | 90 | int ret = 0; |
| 91 | struct list_head *list = &group->notification_list; | 91 | struct list_head *list = &group->notification_list; |
| 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); |
| @@ -98,14 +98,14 @@ struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, | |||
| 98 | /* Queue overflow event only if it isn't already queued */ | 98 | /* Queue overflow event only if it isn't already queued */ |
| 99 | if (list_empty(&group->overflow_event.list)) | 99 | if (list_empty(&group->overflow_event.list)) |
| 100 | event = &group->overflow_event; | 100 | event = &group->overflow_event; |
| 101 | return_event = event; | 101 | ret = 1; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | if (!list_empty(list) && merge) { | 104 | if (!list_empty(list) && merge) { |
| 105 | return_event = merge(list, event); | 105 | ret = merge(list, event); |
| 106 | if (return_event) { | 106 | if (ret) { |
| 107 | mutex_unlock(&group->notification_mutex); | 107 | mutex_unlock(&group->notification_mutex); |
| 108 | return return_event; | 108 | return ret; |
| 109 | } | 109 | } |
| 110 | } | 110 | } |
| 111 | 111 | ||
| @@ -115,7 +115,7 @@ struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, | |||
| 115 | 115 | ||
| 116 | wake_up(&group->notification_waitq); | 116 | wake_up(&group->notification_waitq); |
| 117 | kill_fasync(&group->fsn_fa, SIGIO, POLL_IN); | 117 | kill_fasync(&group->fsn_fa, SIGIO, POLL_IN); |
| 118 | return return_event; | 118 | return ret; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | /* | 121 | /* |
