fs/super.c: fix race between freeze_super() and thaw_super()
authorOleg Nesterov <oleg@redhat.com>
Mon, 26 Sep 2016 16:07:48 +0000 (18:07 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Feb 2017 10:03:29 +0000 (11:03 +0100)
commit 89f39af129382a40d7cd1f6914617282cfeee28e upstream.

Change thaw_super() to check frozen != SB_FREEZE_COMPLETE rather than
frozen == SB_UNFROZEN, otherwise it can race with freeze_super() which
drops sb->s_umount after SB_FREEZE_WRITE to preserve the lock ordering.

In this case thaw_super() will wrongly call s_op->unfreeze_fs() before
it was actually frozen, and call sb_freeze_unlock() which leads to the
unbalanced percpu_up_write(). Unfortunately lockdep can't detect this,
so this triggers misc BUG_ON()'s in kernel/rcu/sync.c.

Reported-and-tested-by: Nikolay Borisov <kernel@kyup.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Willy Tarreau <w@1wt.eu>
fs/super.c

index 97280e76179c5f5dd79d109a527e23103c62b1b6..fd3281d1ec45f749bfbc83ca684e321b2905dd38 100644 (file)
@@ -1327,8 +1327,8 @@ int freeze_super(struct super_block *sb)
                }
        }
        /*
-        * This is just for debugging purposes so that fs can warn if it
-        * sees write activity when frozen is set to SB_FREEZE_COMPLETE.
+        * For debugging purposes so that fs can warn if it sees write activity
+        * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super().
         */
        sb->s_writers.frozen = SB_FREEZE_COMPLETE;
        up_write(&sb->s_umount);
@@ -1347,7 +1347,7 @@ int thaw_super(struct super_block *sb)
        int error;
 
        down_write(&sb->s_umount);
-       if (sb->s_writers.frozen == SB_UNFROZEN) {
+       if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) {
                up_write(&sb->s_umount);
                return -EINVAL;
        }