Skip to content

Commit b37199e

Browse files
author
Al Viro
committed
rcuwalk: recheck mount_lock after mountpoint crossing attempts
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. Cc: [email protected] Signed-off-by: Al Viro <[email protected]>
1 parent e825196 commit b37199e

File tree

1 file changed

+13
-16
lines changed

1 file changed

+13
-16
lines changed

fs/namei.c

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
11091109
return false;
11101110

11111111
if (!d_mountpoint(path->dentry))
1112-
break;
1112+
return true;
11131113

11141114
mounted = __lookup_mnt(path->mnt, path->dentry);
11151115
if (!mounted)
@@ -1125,20 +1125,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
11251125
*/
11261126
*inode = path->dentry->d_inode;
11271127
}
1128-
return true;
1129-
}
1130-
1131-
static void follow_mount_rcu(struct nameidata *nd)
1132-
{
1133-
while (d_mountpoint(nd->path.dentry)) {
1134-
struct mount *mounted;
1135-
mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
1136-
if (!mounted)
1137-
break;
1138-
nd->path.mnt = &mounted->mnt;
1139-
nd->path.dentry = mounted->mnt.mnt_root;
1140-
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
1141-
}
1128+
return read_seqretry(&mount_lock, nd->m_seq);
11421129
}
11431130

11441131
static int follow_dotdot_rcu(struct nameidata *nd)
@@ -1166,7 +1153,17 @@ static int follow_dotdot_rcu(struct nameidata *nd)
11661153
break;
11671154
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
11681155
}
1169-
follow_mount_rcu(nd);
1156+
while (d_mountpoint(nd->path.dentry)) {
1157+
struct mount *mounted;
1158+
mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
1159+
if (!mounted)
1160+
break;
1161+
nd->path.mnt = &mounted->mnt;
1162+
nd->path.dentry = mounted->mnt.mnt_root;
1163+
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
1164+
if (!read_seqretry(&mount_lock, nd->m_seq))
1165+
goto failed;
1166+
}
11701167
nd->inode = nd->path.dentry->d_inode;
11711168
return 0;
11721169

0 commit comments

Comments
 (0)