In parse_options(), if match_strdup() failed, parse_options() leaves
opts->iocharset in unexpected state (i.e. still pointing the freed
string). And this can be the cause of double free.

To fix, this initialize opts->iocharset always when freeing.

Reported-by: syzbot+90b8e10515ae88228...@syzkaller.appspotmail.com
Cc: <sta...@vger.kernel.org>
Signed-off-by: OGAWA Hirofumi <hirof...@mail.parknet.co.jp>
---

 fs/fat/inode.c |   20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff -puN fs/fat/inode.c~fat-fix-kmalloc-failure fs/fat/inode.c
--- linux/fs/fat/inode.c~fat-fix-kmalloc-failure        2018-07-12 
12:20:30.388600735 +0900
+++ linux-hirofumi/fs/fat/inode.c       2018-07-12 15:09:48.764070539 +0900
@@ -703,13 +703,21 @@ static void fat_set_state(struct super_b
        brelse(bh);
 }
 
+static void fat_reset_iocharset(struct fat_mount_options *opts)
+{
+       if (opts->iocharset != fat_default_iocharset) {
+               /* Note: opts->iocharset can be NULL here */
+               kfree(opts->iocharset);
+               opts->iocharset = fat_default_iocharset;
+       }
+}
+
 static void delayed_free(struct rcu_head *p)
 {
        struct msdos_sb_info *sbi = container_of(p, struct msdos_sb_info, rcu);
        unload_nls(sbi->nls_disk);
        unload_nls(sbi->nls_io);
-       if (sbi->options.iocharset != fat_default_iocharset)
-               kfree(sbi->options.iocharset);
+       fat_reset_iocharset(&sbi->options);
        kfree(sbi);
 }
 
@@ -1124,7 +1132,7 @@ static int parse_options(struct super_bl
        opts->fs_fmask = opts->fs_dmask = current_umask();
        opts->allow_utime = -1;
        opts->codepage = fat_default_codepage;
-       opts->iocharset = fat_default_iocharset;
+       fat_reset_iocharset(opts);
        if (is_vfat) {
                opts->shortname = VFAT_SFN_DISPLAY_WINNT|VFAT_SFN_CREATE_WIN95;
                opts->rodir = 0;
@@ -1281,8 +1289,7 @@ static int parse_options(struct super_bl
 
                /* vfat specific */
                case Opt_charset:
-                       if (opts->iocharset != fat_default_iocharset)
-                               kfree(opts->iocharset);
+                       fat_reset_iocharset(opts);
                        iocharset = match_strdup(&args[0]);
                        if (!iocharset)
                                return -ENOMEM;
@@ -1873,8 +1880,7 @@ out_fail:
                iput(fat_inode);
        unload_nls(sbi->nls_io);
        unload_nls(sbi->nls_disk);
-       if (sbi->options.iocharset != fat_default_iocharset)
-               kfree(sbi->options.iocharset);
+       fat_reset_iocharset(&sbi->options);
        sb->s_fs_info = NULL;
        kfree(sbi);
        return error;
_

-- 
OGAWA Hirofumi <hirof...@mail.parknet.co.jp>

Reply via email to