Skip to content

Commit 119e1ef

Browse files
author
Al Viro
committed
fix __legitimize_mnt()/mntput() race
__legitimize_mnt() has two problems - one is that in case of success the check of mount_lock is not ordered wrt preceding increment of refcount, making it possible to have successful __legitimize_mnt() on one CPU just before the otherwise final mntpu() on another, with __legitimize_mnt() not seeing mntput() taking the lock and mntput() not seeing the increment done by __legitimize_mnt(). Solved by a pair of barriers. Another is that failure of __legitimize_mnt() on the second read_seqretry() leaves us with reference that'll need to be dropped by caller; however, if that races with final mntput() we can end up with caller dropping rcu_read_lock() and doing mntput() to release that reference - with the first mntput() having freed the damn thing just as rcu_read_lock() had been dropped. Solution: in "do mntput() yourself" failure case grab mount_lock, check if MNT_DOOMED has been set by racing final mntput() that has missed our increment and if it has - undo the increment and treat that as "failure, caller doesn't need to drop anything" case. It's not easy to hit - the final mntput() has to come right after the first read_seqretry() in __legitimize_mnt() *and* manage to miss the increment done by __legitimize_mnt() before the second read_seqretry() in there. The things that are almost impossible to hit on bare hardware are not impossible on SMP KVM, though... Reported-by: Oleg Nesterov <oleg@redhat.com> Fixes: 48a066e ("RCU'd vsfmounts") Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 9ea0a46 commit 119e1ef

File tree

1 file changed

+14
-0
lines changed

1 file changed

+14
-0
lines changed

fs/namespace.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,12 +659,21 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
659659
return 0;
660660
mnt = real_mount(bastard);
661661
mnt_add_count(mnt, 1);
662+
smp_mb(); // see mntput_no_expire()
662663
if (likely(!read_seqretry(&mount_lock, seq)))
663664
return 0;
664665
if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
665666
mnt_add_count(mnt, -1);
666667
return 1;
667668
}
669+
lock_mount_hash();
670+
if (unlikely(bastard->mnt_flags & MNT_DOOMED)) {
671+
mnt_add_count(mnt, -1);
672+
unlock_mount_hash();
673+
return 1;
674+
}
675+
unlock_mount_hash();
676+
/* caller will mntput() */
668677
return -1;
669678
}
670679

@@ -1210,6 +1219,11 @@ static void mntput_no_expire(struct mount *mnt)
12101219
return;
12111220
}
12121221
lock_mount_hash();
1222+
/*
1223+
* make sure that if __legitimize_mnt() has not seen us grab
1224+
* mount_lock, we'll see their refcount increment here.
1225+
*/
1226+
smp_mb();
12131227
mnt_add_count(mnt, -1);
12141228
if (mnt_get_count(mnt)) {
12151229
rcu_read_unlock();

0 commit comments

Comments
 (0)