Skip to content

Commit d725e66

Browse files
committed
Revert "fsnotify: fix oops in fsnotify_clear_marks_by_group_flags()"
This reverts commit a2673b6. Kinglong Mee reports a memory leak with that patch, and Jan Kara confirms: "Thanks for report! You are right that my patch introduces a race between fsnotify kthread and fsnotify_destroy_group() which can result in leaking inotify event on group destruction. I haven't yet decided whether the right fix is not to queue events for dying notification group (as that is pointless anyway) or whether we should just fix the original problem differently... Whenever I look at fsnotify code mark handling I get lost in the maze of locks, lists, and subtle differences between how different notification systems handle notification marks :( I'll think about it over night" and after thinking about it, Jan says: "OK, I have looked into the code some more and I found another relatively simple way of fixing the original oops. It will be IMHO better than trying to fixup this issue which has more potential for breakage. I'll ask Linus to revert the fsnotify fix he already merged and send a new fix" Reported-by: Kinglong Mee <[email protected]> Requested-by: Jan Kara <[email protected]> Cc: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 71ebd1a commit d725e66

File tree

1 file changed

+20
-14
lines changed

1 file changed

+20
-14
lines changed

fs/notify/mark.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,31 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
152152
BUG();
153153

154154
list_del_init(&mark->g_list);
155+
155156
spin_unlock(&mark->lock);
156157

157158
if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
158159
iput(inode);
160+
/* release lock temporarily */
161+
mutex_unlock(&group->mark_mutex);
159162

160163
spin_lock(&destroy_lock);
161164
list_add(&mark->g_list, &destroy_list);
162165
spin_unlock(&destroy_lock);
163166
wake_up(&destroy_waitq);
167+
/*
168+
* We don't necessarily have a ref on mark from caller so the above destroy
169+
* may have actually freed it, unless this group provides a 'freeing_mark'
170+
* function which must be holding a reference.
171+
*/
172+
173+
/*
174+
* Some groups like to know that marks are being freed. This is a
175+
* callback to the group function to let it know that this mark
176+
* is being freed.
177+
*/
178+
if (group->ops->freeing_mark)
179+
group->ops->freeing_mark(mark, group);
164180

165181
/*
166182
* __fsnotify_update_child_dentry_flags(inode);
@@ -175,6 +191,8 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
175191
*/
176192

177193
atomic_dec(&group->num_marks);
194+
195+
mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
178196
}
179197

180198
void fsnotify_destroy_mark(struct fsnotify_mark *mark,
@@ -187,10 +205,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark,
187205

188206
/*
189207
* Destroy all marks in the given list. The marks must be already detached from
190-
* the original inode / vfsmount. Note that we can race with
191-
* fsnotify_clear_marks_by_group_flags(). However we hold a reference to each
192-
* mark so they won't get freed from under us and nobody else touches our
193-
* free_list list_head.
208+
* the original inode / vfsmount.
194209
*/
195210
void fsnotify_destroy_marks(struct list_head *to_free)
196211
{
@@ -391,7 +406,7 @@ struct fsnotify_mark *fsnotify_find_mark(struct hlist_head *head,
391406
}
392407

393408
/*
394-
* Clear any marks in a group in which mark->flags & flags is true.
409+
* clear any marks in a group in which mark->flags & flags is true
395410
*/
396411
void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group,
397412
unsigned int flags)
@@ -445,7 +460,6 @@ static int fsnotify_mark_destroy(void *ignored)
445460
{
446461
struct fsnotify_mark *mark, *next;
447462
struct list_head private_destroy_list;
448-
struct fsnotify_group *group;
449463

450464
for (;;) {
451465
spin_lock(&destroy_lock);
@@ -457,14 +471,6 @@ static int fsnotify_mark_destroy(void *ignored)
457471

458472
list_for_each_entry_safe(mark, next, &private_destroy_list, g_list) {
459473
list_del_init(&mark->g_list);
460-
group = mark->group;
461-
/*
462-
* Some groups like to know that marks are being freed.
463-
* This is a callback to the group function to let it
464-
* know that this mark is being freed.
465-
*/
466-
if (group && group->ops->freeing_mark)
467-
group->ops->freeing_mark(mark, group);
468474
fsnotify_put_mark(mark);
469475
}
470476

0 commit comments

Comments
 (0)