Skip to content

Commit 83c0e1b

Browse files
committed
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 <[email protected]> Reported-and-tested-by: Dave Jones <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent 13116df commit 83c0e1b

File tree

4 files changed

+30
-39
lines changed

4 files changed

+30
-39
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ static bool should_merge(struct fsnotify_event *old_fsn,
2828
}
2929

3030
/* 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)
3332
{
3433
struct fsnotify_event *test_event;
3534
bool do_merge = false;
@@ -43,7 +42,7 @@ static struct fsnotify_event *fanotify_merge(struct list_head *list,
4342
* one we should check for permission response.
4443
*/
4544
if (event->mask & FAN_ALL_PERM_EVENTS)
46-
return NULL;
45+
return 0;
4746
#endif
4847

4948
list_for_each_entry_reverse(test_event, list, list) {
@@ -54,10 +53,10 @@ static struct fsnotify_event *fanotify_merge(struct list_head *list,
5453
}
5554

5655
if (!do_merge)
57-
return NULL;
56+
return 0;
5857

5958
test_event->mask |= event->mask;
60-
return test_event;
59+
return 1;
6160
}
6261

6362
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
@@ -153,7 +152,6 @@ static int fanotify_handle_event(struct fsnotify_group *group,
153152
int ret = 0;
154153
struct fanotify_event_info *event;
155154
struct fsnotify_event *fsn_event;
156-
struct fsnotify_event *notify_fsn_event;
157155

158156
BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
159157
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
@@ -192,13 +190,11 @@ static int fanotify_handle_event(struct fsnotify_group *group,
192190
event->response = 0;
193191
#endif
194192

195-
notify_fsn_event = fsnotify_add_notify_event(group, fsn_event,
196-
fanotify_merge);
197-
if (notify_fsn_event) {
193+
ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge);
194+
if (ret) {
198195
/* Our event wasn't used in the end. Free it. */
199196
fsnotify_destroy_event(group, fsn_event);
200-
if (IS_ERR(notify_fsn_event))
201-
return PTR_ERR(notify_fsn_event);
197+
ret = 0;
202198
}
203199

204200
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS

fs/notify/inotify/inotify_fsnotify.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,13 @@ static bool event_compare(struct fsnotify_event *old_fsn,
5353
return false;
5454
}
5555

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)
5858
{
5959
struct fsnotify_event *last_event;
6060

6161
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);
6563
}
6664

6765
int inotify_handle_event(struct fsnotify_group *group,
@@ -73,9 +71,8 @@ int inotify_handle_event(struct fsnotify_group *group,
7371
{
7472
struct inotify_inode_mark *i_mark;
7573
struct inotify_event_info *event;
76-
struct fsnotify_event *added_event;
7774
struct fsnotify_event *fsn_event;
78-
int ret = 0;
75+
int ret;
7976
int len = 0;
8077
int alloc_len = sizeof(struct inotify_event_info);
8178

@@ -110,18 +107,16 @@ int inotify_handle_event(struct fsnotify_group *group,
110107
if (len)
111108
strcpy(event->name, file_name);
112109

113-
added_event = fsnotify_add_notify_event(group, fsn_event, inotify_merge);
114-
if (added_event) {
110+
ret = fsnotify_add_notify_event(group, fsn_event, inotify_merge);
111+
if (ret) {
115112
/* Our event wasn't used in the end. Free it. */
116113
fsnotify_destroy_event(group, fsn_event);
117-
if (IS_ERR(added_event))
118-
ret = PTR_ERR(added_event);
119114
}
120115

121116
if (inode_mark->mask & IN_ONESHOT)
122117
fsnotify_destroy_mark(inode_mark, group);
123118

124-
return ret;
119+
return 0;
125120
}
126121

127122
static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group)

fs/notify/notification.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,15 @@ void fsnotify_destroy_event(struct fsnotify_group *group,
7979

8080
/*
8181
* 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.
8484
*/
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 *))
8989
{
90-
struct fsnotify_event *return_event = NULL;
90+
int ret = 0;
9191
struct list_head *list = &group->notification_list;
9292

9393
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,
9898
/* Queue overflow event only if it isn't already queued */
9999
if (list_empty(&group->overflow_event.list))
100100
event = &group->overflow_event;
101-
return_event = event;
101+
ret = 1;
102102
}
103103

104104
if (!list_empty(list) && merge) {
105-
return_event = merge(list, event);
106-
if (return_event) {
105+
ret = merge(list, event);
106+
if (ret) {
107107
mutex_unlock(&group->notification_mutex);
108-
return return_event;
108+
return ret;
109109
}
110110
}
111111

@@ -115,7 +115,7 @@ struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group,
115115

116116
wake_up(&group->notification_waitq);
117117
kill_fasync(&group->fsn_fa, SIGIO, POLL_IN);
118-
return return_event;
118+
return ret;
119119
}
120120

121121
/*

include/linux/fsnotify_backend.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ extern int fsnotify_fasync(int fd, struct file *file, int on);
322322
extern void fsnotify_destroy_event(struct fsnotify_group *group,
323323
struct fsnotify_event *event);
324324
/* 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 *));
329329
/* true if the group notification queue is empty */
330330
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
331331
/* return, but do not dequeue the first event on the notification queue */

0 commit comments

Comments
 (0)