VFS: Fix memory leak caused by concurrently mounting fs with subtype
authorChenXiaoSong <chenxiaosong2@huawei.com>
Tue, 2 Nov 2021 14:22:06 +0000 (22:22 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 15 May 2022 17:40:27 +0000 (19:40 +0200)
If two processes mount same superblock, memory leak occurs:

CPU0               |  CPU1
do_new_mount       |  do_new_mount
  fs_set_subtype   |    fs_set_subtype
    kstrdup        |
                   |      kstrdup
    memrory leak   |

The following reproducer triggers the problem:

1. shell command: mount -t ntfs /dev/sda1 /mnt &
2. c program: mount("/dev/sda1", "/mnt", "fuseblk", 0, "...")

with kmemleak report being along the lines of

unreferenced object 0xffff888235f1a5c0 (size 8):
  comm "mount.ntfs", pid 2860, jiffies 4295757824 (age 43.423s)
  hex dump (first 8 bytes):
    00 a5 f1 35 82 88 ff ff                          ...5....
  backtrace:
    [<00000000656e30cc>] __kmalloc_track_caller+0x16e/0x430
    [<000000008e591727>] kstrdup+0x3e/0x90
    [<000000008430d12b>] do_mount.cold+0x7b/0xd9
    [<0000000078d639cd>] ksys_mount+0xb2/0x150
    [<000000006015988d>] __x64_sys_mount+0x29/0x40
    [<00000000e0a7c118>] do_syscall_64+0xc1/0x1d0
    [<00000000bcea7df5>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
    [<00000000803a4067>] 0xffffffffffffffff

Linus's tree already have refactoring patchset [1], one of them can fix this bug:
        c30da2e981a7 ("fuse: convert to use the new mount API")
After refactoring, init super_block->s_subtype in fuse_fill_super.

Since we did not merge the refactoring patchset in this branch, I create this patch.
This patch fix this by adding a write lock while calling fs_set_subtype.

[1] https://patchwork.kernel.org/project/linux-fsdevel/patch/20190903113640.7984-3-mszeredi@redhat.com/

Fixes: 79c0b2df79eb ("add filesystem subtype support")
Cc: David Howells <dhowells@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: ChenXiaoSong <chenxiaosong2@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/namespace.c

index 683668a20bed7576b1d7b3d20c1956ffb98f5554..a0c549f2721ca37a4df52ac9477d06365d7b5e9d 100644 (file)
@@ -2570,9 +2570,12 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
                return -ENODEV;
 
        mnt = vfs_kern_mount(type, sb_flags, name, data);
-       if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
-           !mnt->mnt_sb->s_subtype)
-               mnt = fs_set_subtype(mnt, fstype);
+       if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE)) {
+               down_write(&mnt->mnt_sb->s_umount);
+               if (!mnt->mnt_sb->s_subtype)
+                       mnt = fs_set_subtype(mnt, fstype);
+               up_write(&mnt->mnt_sb->s_umount);
+       }
 
        put_filesystem(type);
        if (IS_ERR(mnt))