Skip to content

Commit 5838d44

Browse files
jankaratorvalds
authored andcommitted
fanotify: fix double free of pending permission events
Commit 8581679 ("fanotify: Fix use after free for permission events") introduced a double free issue for permission events which are pending in group's notification queue while group is being destroyed. These events are freed from fanotify_handle_event() but they are not removed from groups notification queue and thus they get freed again from fsnotify_flush_notify(). Fix the problem by removing permission events from notification queue before freeing them if we skip processing access response. Also expand comments in fanotify_release() to explain group shutdown in detail. Fixes: 8581679 Signed-off-by: Jan Kara <[email protected]> Reported-by: Douglas Leeder <[email protected]> Tested-by: Douglas Leeder <[email protected]> Reported-by: Heinrich Schuchard <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 8ba8fa9 commit 5838d44

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,15 @@ static int fanotify_get_response(struct fsnotify_group *group,
7070
wait_event(group->fanotify_data.access_waitq, event->response ||
7171
atomic_read(&group->fanotify_data.bypass_perm));
7272

73-
if (!event->response) /* bypass_perm set */
73+
if (!event->response) { /* bypass_perm set */
74+
/*
75+
* Event was canceled because group is being destroyed. Remove
76+
* it from group's event list because we are responsible for
77+
* freeing the permission event.
78+
*/
79+
fsnotify_remove_event(group, &event->fae.fse);
7480
return 0;
81+
}
7582

7683
/* userspace responded, convert to something usable */
7784
switch (event->response) {

fs/notify/fanotify/fanotify_user.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ static int fanotify_release(struct inode *ignored, struct file *file)
359359
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
360360
struct fanotify_perm_event_info *event, *next;
361361

362+
/*
363+
* There may be still new events arriving in the notification queue
364+
* but since userspace cannot use fanotify fd anymore, no event can
365+
* enter or leave access_list by now.
366+
*/
362367
spin_lock(&group->fanotify_data.access_lock);
363368

364369
atomic_inc(&group->fanotify_data.bypass_perm);
@@ -373,6 +378,13 @@ static int fanotify_release(struct inode *ignored, struct file *file)
373378
}
374379
spin_unlock(&group->fanotify_data.access_lock);
375380

381+
/*
382+
* Since bypass_perm is set, newly queued events will not wait for
383+
* access response. Wake up the already sleeping ones now.
384+
* synchronize_srcu() in fsnotify_destroy_group() will wait for all
385+
* processes sleeping in fanotify_handle_event() waiting for access
386+
* response and thus also for all permission events to be freed.
387+
*/
376388
wake_up(&group->fanotify_data.access_waitq);
377389
#endif
378390

fs/notify/notification.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ void fsnotify_destroy_event(struct fsnotify_group *group,
7373
/* Overflow events are per-group and we don't want to free them */
7474
if (!event || event->mask == FS_Q_OVERFLOW)
7575
return;
76-
76+
/* If the event is still queued, we have a problem... */
77+
WARN_ON(!list_empty(&event->list));
7778
group->ops->free_event(event);
7879
}
7980

@@ -124,6 +125,21 @@ int fsnotify_add_event(struct fsnotify_group *group,
124125
return ret;
125126
}
126127

128+
/*
129+
* Remove @event from group's notification queue. It is the responsibility of
130+
* the caller to destroy the event.
131+
*/
132+
void fsnotify_remove_event(struct fsnotify_group *group,
133+
struct fsnotify_event *event)
134+
{
135+
mutex_lock(&group->notification_mutex);
136+
if (!list_empty(&event->list)) {
137+
list_del_init(&event->list);
138+
group->q_len--;
139+
}
140+
mutex_unlock(&group->notification_mutex);
141+
}
142+
127143
/*
128144
* Remove and return the first event from the notification list. It is the
129145
* responsibility of the caller to destroy the obtained event

include/linux/fsnotify_backend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ extern int fsnotify_add_event(struct fsnotify_group *group,
326326
struct fsnotify_event *event,
327327
int (*merge)(struct list_head *,
328328
struct fsnotify_event *));
329+
/* Remove passed event from groups notification queue */
330+
extern void fsnotify_remove_event(struct fsnotify_group *group, struct fsnotify_event *event);
329331
/* true if the group notification queue is empty */
330332
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
331333
/* return, but do not dequeue the first event on the notification queue */

0 commit comments

Comments
 (0)