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/notify | |
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/notify')
-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 | /* |