Skip to content

Commit

Permalink
vfs: fix the rest of sget() races
Browse files Browse the repository at this point in the history
unfortunately, just checking MS_BORN after having grabbed ->s_umount in
sget() is not enough; places that pick superblock from a list and
grab s_umount shared need the same check in addition to checking for
->s_root; otherwise three-way race between failing mount, sget() and
such list-walker can leave us with list-walker coming *second*, when
temporary active ref grabbed by sget() (to be dropped when sget()
notices that original mount has failed by checking MS_BORN) has
lead to deactivate_locked_super() from failing ->mount() *not* doing
->kill_sb() and just releasing ->s_umount.  Once sget() gets through
and notices that MS_BORN had never been set it will drop the active
ref and fs will be shut down and kicked out of all lists, but it's
too late for something like sync_supers().

Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Al Viro committed Jan 4, 2012
1 parent cf31e70 commit dabe0dc
Showing 1 changed file with 13 additions and 7 deletions.
20 changes: 13 additions & 7 deletions fs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ bool grab_super_passive(struct super_block *sb)
spin_unlock(&sb_lock);

if (down_read_trylock(&sb->s_umount)) {
if (sb->s_root)
if (sb->s_root && (sb->s_flags & MS_BORN))
return true;
up_read(&sb->s_umount);
}
Expand Down Expand Up @@ -505,7 +505,7 @@ void sync_supers(void)
spin_unlock(&sb_lock);

down_read(&sb->s_umount);
if (sb->s_root && sb->s_dirt)
if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN))
sb->s_op->write_super(sb);
up_read(&sb->s_umount);

Expand Down Expand Up @@ -540,7 +540,7 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)
spin_unlock(&sb_lock);

down_read(&sb->s_umount);
if (sb->s_root)
if (sb->s_root && (sb->s_flags & MS_BORN))
f(sb, arg);
up_read(&sb->s_umount);

Expand Down Expand Up @@ -575,7 +575,7 @@ void iterate_supers_type(struct file_system_type *type,
spin_unlock(&sb_lock);

down_read(&sb->s_umount);
if (sb->s_root)
if (sb->s_root && (sb->s_flags & MS_BORN))
f(sb, arg);
up_read(&sb->s_umount);

Expand Down Expand Up @@ -616,7 +616,7 @@ struct super_block *get_super(struct block_device *bdev)
spin_unlock(&sb_lock);
down_read(&sb->s_umount);
/* still alive? */
if (sb->s_root)
if (sb->s_root && (sb->s_flags & MS_BORN))
return sb;
up_read(&sb->s_umount);
/* nope, got unmounted */
Expand Down Expand Up @@ -676,7 +676,7 @@ struct super_block *user_get_super(dev_t dev)
spin_unlock(&sb_lock);
down_read(&sb->s_umount);
/* still alive? */
if (sb->s_root)
if (sb->s_root && (sb->s_flags & MS_BORN))
return sb;
up_read(&sb->s_umount);
/* nope, got unmounted */
Expand Down Expand Up @@ -763,7 +763,8 @@ static void do_emergency_remount(struct work_struct *work)
sb->s_count++;
spin_unlock(&sb_lock);
down_write(&sb->s_umount);
if (sb->s_root && sb->s_bdev && !(sb->s_flags & MS_RDONLY)) {
if (sb->s_root && sb->s_bdev && (sb->s_flags & MS_BORN) &&
!(sb->s_flags & MS_RDONLY)) {
/*
* What lock protects sb->s_flags??
*/
Expand Down Expand Up @@ -1146,6 +1147,11 @@ int freeze_super(struct super_block *sb)
return -EBUSY;
}

if (!(sb->s_flags & MS_BORN)) {
up_write(&sb->s_umount);
return 0; /* sic - it's "nothing to do" */
}

if (sb->s_flags & MS_RDONLY) {
sb->s_frozen = SB_FREEZE_TRANS;
smp_wmb();
Expand Down

0 comments on commit dabe0dc

Please sign in to comment.