fsnotify: pin both inode and vfsmount mark
authorMiklos Szeredi <mszeredi@redhat.com>
Mon, 30 Oct 2017 20:14:55 +0000 (21:14 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Nov 2017 08:40:47 +0000 (08:40 +0000)
commit 0d6ec079d6aaa098b978d6395973bb027c752a03 upstream.

We may fail to pin one of the marks in fsnotify_prepare_user_wait() when
dropping the srcu read lock, resulting in use after free at the next
iteration.

Solution is to store both marks in iter_info instead of just the one we'll
be sending the event for.

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")
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/notify/fsnotify.c

index 0c4583b61717646e491a28d879b5762faeb8956e..0747162938296d4baa60f28f73b3080de869a6bb 100644 (file)
@@ -335,6 +335,13 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
                                                    struct fsnotify_mark, obj_list);
                        vfsmount_group = vfsmount_mark->group;
                }
+               /*
+                * Need to protect both marks against freeing so that we can
+                * continue iteration from this place, regardless of which mark
+                * we actually happen to send an event for.
+                */
+               iter_info.inode_mark = inode_mark;
+               iter_info.vfsmount_mark = vfsmount_mark;
 
                if (inode_group && vfsmount_group) {
                        int cmp = fsnotify_compare_groups(inode_group,
@@ -348,9 +355,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
                        }
                }
 
-               iter_info.inode_mark = inode_mark;
-               iter_info.vfsmount_mark = vfsmount_mark;
-
                ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
                                    data, data_is, cookie, file_name,
                                    &iter_info);