]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
fsnotify: fix pinning group in fsnotify_prepare_user_wait()
authorMiklos Szeredi <mszeredi@redhat.com>
Mon, 30 Oct 2017 20:14:56 +0000 (21:14 +0100)
committerJan Kara <jack@suse.cz>
Tue, 31 Oct 2017 16:54:56 +0000 (17:54 +0100)
Blind increment of group's user_waits is not enough, we could be far enough
in the group's destruction that it isn't taken into account (i.e. grabbing
the mark ref afterwards doesn't guarantee that it was the ref coming from
the _group_ that was grabbed).

Instead we need to check (under lock) that the mark is still attached to
the group after having obtained a ref to the mark.  If not, skip it.

Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Fixes: 9385a84d7e1f ("fsnotify: Pass fsnotify_iter_info into handle_event handler")
Cc: <stable@vger.kernel.org> # v4.12
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/mark.c

index 7ecd15add6a7f86d54db5ab21c425b3f49be9ac0..f3a32ea15b4959a03c17543908810ac1fe9b34f7 100644 (file)
@@ -255,23 +255,20 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
  */
 static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark)
 {
-       struct fsnotify_group *group;
-
        if (!mark)
                return true;
 
-       group = mark->group;
-       /*
-        * Since acquisition of mark reference is an atomic op as well, we can
-        * be sure this inc is seen before any effect of refcount increment.
-        */
-       atomic_inc(&group->user_waits);
-       if (atomic_inc_not_zero(&mark->refcnt))
-               return true;
-
-       if (atomic_dec_and_test(&group->user_waits) && group->shutdown)
-               wake_up(&group->notification_waitq);
-
+       if (atomic_inc_not_zero(&mark->refcnt)) {
+               spin_lock(&mark->lock);
+               if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) {
+                       /* mark is attached, group is still alive then */
+                       atomic_inc(&mark->group->user_waits);
+                       spin_unlock(&mark->lock);
+                       return true;
+               }
+               spin_unlock(&mark->lock);
+               fsnotify_put_mark(mark);
+       }
        return false;
 }