| From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> |
| Date: Fri, 20 Jul 2018 17:53:42 -0700 |
| Subject: fat: fix memory allocation failure handling of match_strdup() |
| |
| commit 35033ab988c396ad7bce3b6d24060c16a9066db8 upstream. |
| |
| 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. |
| |
| Link: http://lkml.kernel.org/r/8736wp9dzc.fsf@mail.parknet.co.jp |
| Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> |
| Reported-by: syzbot+90b8e10515ae88228a92@syzkaller.appspotmail.com |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/fat/inode.c | 20 +++++++++++++------- |
| 1 file changed, 13 insertions(+), 7 deletions(-) |
| |
| --- a/fs/fat/inode.c |
| +++ b/fs/fat/inode.c |
| @@ -610,13 +610,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); |
| } |
| |
| @@ -1031,7 +1039,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; |
| @@ -1181,8 +1189,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; |
| @@ -1763,8 +1770,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; |