Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

Jan Kara 83c0e1b4 13116dfd

+30 -39
+7 -11
fs/notify/fanotify/fanotify.c
··· 28 28 } 29 29 30 30 /* and the list better be locked by something too! */ 31 - static struct fsnotify_event *fanotify_merge(struct list_head *list, 32 - struct fsnotify_event *event) 31 + static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) 33 32 { 34 33 struct fsnotify_event *test_event; 35 34 bool do_merge = false; ··· 42 43 * one we should check for permission response. 43 44 */ 44 45 if (event->mask & FAN_ALL_PERM_EVENTS) 45 - return NULL; 46 + return 0; 46 47 #endif 47 48 48 49 list_for_each_entry_reverse(test_event, list, list) { ··· 53 54 } 54 55 55 56 if (!do_merge) 56 - return NULL; 57 + return 0; 57 58 58 59 test_event->mask |= event->mask; 59 - return test_event; 60 + return 1; 60 61 } 61 62 62 63 #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS ··· 152 153 int ret = 0; 153 154 struct fanotify_event_info *event; 154 155 struct fsnotify_event *fsn_event; 155 - struct fsnotify_event *notify_fsn_event; 156 156 157 157 BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); 158 158 BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); ··· 190 192 event->response = 0; 191 193 #endif 192 194 193 - notify_fsn_event = fsnotify_add_notify_event(group, fsn_event, 194 - fanotify_merge); 195 - if (notify_fsn_event) { 195 + ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge); 196 + if (ret) { 196 197 /* Our event wasn't used in the end. Free it. */ 197 198 fsnotify_destroy_event(group, fsn_event); 198 - if (IS_ERR(notify_fsn_event)) 199 - return PTR_ERR(notify_fsn_event); 199 + ret = 0; 200 200 } 201 201 202 202 #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+7 -12
fs/notify/inotify/inotify_fsnotify.c
··· 53 53 return false; 54 54 } 55 55 56 - static struct fsnotify_event *inotify_merge(struct list_head *list, 57 - struct fsnotify_event *event) 56 + static int inotify_merge(struct list_head *list, 57 + struct fsnotify_event *event) 58 58 { 59 59 struct fsnotify_event *last_event; 60 60 61 61 last_event = list_entry(list->prev, struct fsnotify_event, list); 62 - if (!event_compare(last_event, event)) 63 - return NULL; 64 - return last_event; 62 + return event_compare(last_event, event); 65 63 } 66 64 67 65 int inotify_handle_event(struct fsnotify_group *group, ··· 71 73 { 72 74 struct inotify_inode_mark *i_mark; 73 75 struct inotify_event_info *event; 74 - struct fsnotify_event *added_event; 75 76 struct fsnotify_event *fsn_event; 76 - int ret = 0; 77 + int ret; 77 78 int len = 0; 78 79 int alloc_len = sizeof(struct inotify_event_info); 79 80 ··· 107 110 if (len) 108 111 strcpy(event->name, file_name); 109 112 110 - added_event = fsnotify_add_notify_event(group, fsn_event, inotify_merge); 111 - if (added_event) { 113 + ret = fsnotify_add_notify_event(group, fsn_event, inotify_merge); 114 + if (ret) { 112 115 /* Our event wasn't used in the end. Free it. */ 113 116 fsnotify_destroy_event(group, fsn_event); 114 - if (IS_ERR(added_event)) 115 - ret = PTR_ERR(added_event); 116 117 } 117 118 118 119 if (inode_mark->mask & IN_ONESHOT) 119 120 fsnotify_destroy_mark(inode_mark, group); 120 121 121 - return ret; 122 + return 0; 122 123 } 123 124 124 125 static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group)
+12 -12
fs/notify/notification.c
··· 79 79 80 80 /* 81 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 83 - * group's notification queue, a reference is taken on event. 82 + * event off the queue to deal with. The function returns 0 if the event was 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, 86 - struct fsnotify_event *event, 87 - struct fsnotify_event *(*merge)(struct list_head *, 88 - struct fsnotify_event *)) 85 + int fsnotify_add_notify_event(struct fsnotify_group *group, 86 + struct fsnotify_event *event, 87 + int (*merge)(struct list_head *, 88 + struct fsnotify_event *)) 89 89 { 90 - struct fsnotify_event *return_event = NULL; 90 + int ret = 0; 91 91 struct list_head *list = &group->notification_list; 92 92 93 93 pr_debug("%s: group=%p event=%p\n", __func__, group, event); ··· 98 98 /* Queue overflow event only if it isn't already queued */ 99 99 if (list_empty(&group->overflow_event.list)) 100 100 event = &group->overflow_event; 101 - return_event = event; 101 + ret = 1; 102 102 } 103 103 104 104 if (!list_empty(list) && merge) { 105 - return_event = merge(list, event); 106 - if (return_event) { 105 + ret = merge(list, event); 106 + if (ret) { 107 107 mutex_unlock(&group->notification_mutex); 108 - return return_event; 108 + return ret; 109 109 } 110 110 } 111 111 ··· 115 115 116 116 wake_up(&group->notification_waitq); 117 117 kill_fasync(&group->fsn_fa, SIGIO, POLL_IN); 118 - return return_event; 118 + return ret; 119 119 } 120 120 121 121 /*
+4 -4
include/linux/fsnotify_backend.h
··· 322 322 extern void fsnotify_destroy_event(struct fsnotify_group *group, 323 323 struct fsnotify_event *event); 324 324 /* attach the event to the group notification queue */ 325 - extern struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, 326 - struct fsnotify_event *event, 327 - struct fsnotify_event *(*merge)(struct list_head *, 328 - struct fsnotify_event *)); 325 + extern int fsnotify_add_notify_event(struct fsnotify_group *group, 326 + struct fsnotify_event *event, 327 + int (*merge)(struct list_head *, 328 + struct fsnotify_event *)); 329 329 /* true if the group notification queue is empty */ 330 330 extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); 331 331 /* return, but do not dequeue the first event on the notification queue */