aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-01-28 12:53:22 -0500
committerJan Kara <jack@suse.cz>2014-01-29 07:57:10 -0500
commit83c0e1b442b488571f4fef4a91c2fe52eed6c705 (patch)
treeb29a85223f2e5e166f075266edb1bf2e0ef5cf57
parent13116dfd13c8c9d60ea04ece13419af2de8e2e37 (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>
-rw-r--r--fs/notify/fanotify/fanotify.c18
-rw-r--r--fs/notify/inotify/inotify_fsnotify.c19
-rw-r--r--fs/notify/notification.c24
-rw-r--r--include/linux/fsnotify_backend.h8
4 files changed, 30 insertions, 39 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! */
31static struct fsnotify_event *fanotify_merge(struct list_head *list, 31static 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
56static struct fsnotify_event *inotify_merge(struct list_head *list, 56static 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
67int inotify_handle_event(struct fsnotify_group *group, 65int 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
127static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) 122static 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 */
85struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, 85int 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/*
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 7d8d5e608594..3d286ff49ab0 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -322,10 +322,10 @@ extern int fsnotify_fasync(int fd, struct file *file, int on);
322extern void fsnotify_destroy_event(struct fsnotify_group *group, 322extern void fsnotify_destroy_event(struct fsnotify_group *group,
323 struct fsnotify_event *event); 323 struct fsnotify_event *event);
324/* attach the event to the group notification queue */ 324/* attach the event to the group notification queue */
325extern struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, 325extern int fsnotify_add_notify_event(struct fsnotify_group *group,
326 struct fsnotify_event *event, 326 struct fsnotify_event *event,
327 struct fsnotify_event *(*merge)(struct list_head *, 327 int (*merge)(struct list_head *,
328 struct fsnotify_event *)); 328 struct fsnotify_event *));
329/* true if the group notification queue is empty */ 329/* true if the group notification queue is empty */
330extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); 330extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
331/* return, but do not dequeue the first event on the notification queue */ 331/* return, but do not dequeue the first event on the notification queue */