Commit f72adfd5 authored by Eric Paris's avatar Eric Paris

fsnotify: fix list walk order

Marks were stored on the inode and vfsmonut mark list in order from
highest memory address to lowest memory address.  The code to walk those
lists thought they were in order from lowest to highest with
unpredictable results when trying to match up marks from each.  It was
possible that extra events would be sent to userspace when inode
marks ignoring events wouldn't get matched with the vfsmount marks.

This problem only affected fanotify when using both vfsmount and inode
marks simultaneously.
Signed-off-by: default avatarEric Paris <eparis@redhat.com>
parent a2f13ad0
...@@ -261,27 +261,26 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, ...@@ -261,27 +261,26 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
while (inode_node || vfsmount_node) { while (inode_node || vfsmount_node) {
used_inode = used_vfsmount = false; used_inode = used_vfsmount = false;
inode_group = vfsmount_group = NULL;
if (inode_node) { if (inode_node) {
inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
struct fsnotify_mark, i.i_list); struct fsnotify_mark, i.i_list);
inode_group = inode_mark->group; inode_group = inode_mark->group;
} else }
inode_group = (void *)-1;
if (vfsmount_node) { if (vfsmount_node) {
vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu), vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
struct fsnotify_mark, m.m_list); struct fsnotify_mark, m.m_list);
vfsmount_group = vfsmount_mark->group; vfsmount_group = vfsmount_mark->group;
} else }
vfsmount_group = (void *)-1;
if (inode_group < vfsmount_group) { if (inode_group > vfsmount_group) {
/* handle inode */ /* handle inode */
send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
data_is, cookie, file_name, &event); data_is, cookie, file_name, &event);
used_inode = true; used_inode = true;
} else if (vfsmount_group < inode_group) { } else if (vfsmount_group > inode_group) {
send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
data_is, cookie, file_name, &event); data_is, cookie, file_name, &event);
used_vfsmount = true; used_vfsmount = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment