This is a note to let you know that I've just added the patch titled rcuwalk: recheck mount_lock after mountpoint crossing attempts to the 3.13-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: rcuwalk-recheck-mount_lock-after-mountpoint-crossing-attempts.patch and it can be found in the queue-3.13 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. >From b37199e626b31e1175fb06764c5d1d687723aac2 Mon Sep 17 00:00:00 2001 From: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Date: Thu, 20 Mar 2014 15:18:22 -0400 Subject: rcuwalk: recheck mount_lock after mountpoint crossing attempts From: Al Viro <viro@xxxxxxxxxxxxxxxxxx> commit b37199e626b31e1175fb06764c5d1d687723aac2 upstream. We can get false negative from __lookup_mnt() if an unrelated vfsmount gets moved. In that case legitimize_mnt() is guaranteed to fail, and we will fall back to non-RCU walk... unless we end up running into a hard error on a filesystem object we wouldn't have reached if not for that false negative. IOW, delaying that check until the end of pathname resolution is wrong - we should recheck right after we attempt to cross the mountpoint. We don't need to recheck unless we see d_mountpoint() being true - in that case even if we have just raced with mount/umount, we can simply go on as if we'd come at the moment when the sucker wasn't a mountpoint; if we run into a hard error as the result, it was a legitimate outcome. __lookup_mnt() returning NULL is different in that respect, since it might've happened due to operation on completely unrelated mountpoint. Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- fs/namei.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) --- a/fs/namei.c +++ b/fs/namei.c @@ -1098,7 +1098,7 @@ static bool __follow_mount_rcu(struct na return false; if (!d_mountpoint(path->dentry)) - break; + return true; mounted = __lookup_mnt(path->mnt, path->dentry); if (!mounted) @@ -1114,20 +1114,7 @@ static bool __follow_mount_rcu(struct na */ *inode = path->dentry->d_inode; } - return true; -} - -static void follow_mount_rcu(struct nameidata *nd) -{ - while (d_mountpoint(nd->path.dentry)) { - struct mount *mounted; - mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry); - if (!mounted) - break; - nd->path.mnt = &mounted->mnt; - nd->path.dentry = mounted->mnt.mnt_root; - nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); - } + return read_seqretry(&mount_lock, nd->m_seq); } static int follow_dotdot_rcu(struct nameidata *nd) @@ -1155,7 +1142,17 @@ static int follow_dotdot_rcu(struct name break; nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } - follow_mount_rcu(nd); + while (d_mountpoint(nd->path.dentry)) { + struct mount *mounted; + mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry); + if (!mounted) + break; + nd->path.mnt = &mounted->mnt; + nd->path.dentry = mounted->mnt.mnt_root; + nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); + if (!read_seqretry(&mount_lock, nd->m_seq)) + goto failed; + } nd->inode = nd->path.dentry->d_inode; return 0; Patches currently in stable-queue which might be from viro@xxxxxxxxxxxxxxxxxx are queue-3.13/rcuwalk-recheck-mount_lock-after-mountpoint-crossing-attempts.patch queue-3.13/make-prepend_name-work-correctly-when-called-with-negative-buflen.patch -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html