Commit 29c06365 authored by Sang-Heon Jeon's avatar Sang-Heon Jeon Committed by Namjae Jeon
Browse files

exfat: combine iocharset and utf8 option setup



Currently, exfat utf8 mount option depends on the iocharset option
value. After exfat remount, utf8 option may become inconsistent with
iocharset option.

If the options are inconsistent; (specifically, iocharset=utf8 but
utf8=0) readdir may reference uninitalized NLS, leading to a null
pointer dereference.

Extract and combine utf8/iocharset setup logic into exfat_set_iocharset().
Then Replace iocharset setup logic to exfat_set_iocharset to prevent
utf8/iocharset option inconsistentcy after remount.

Reported-by: default avatar <syzbot+3e9cb93e3c5f90d28e19@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=3e9cb93e3c5f90d28e19


Signed-off-by: default avatarSang-Heon Jeon <ekffu200098@gmail.com>
Fixes: acab02ffcd6b ("exfat: support modifying mount options via remount")
Tested-by: default avatar <syzbot+3e9cb93e3c5f90d28e19@syzkaller.appspotmail.com>
Reviewed-by: default avatarYuezhang Mo <Yuezhang.Mo@sony.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent e6fd5d3a
Loading
Loading
Loading
Loading
+15 −9
Original line number Diff line number Diff line
@@ -31,6 +31,16 @@ static void exfat_free_iocharset(struct exfat_sb_info *sbi)
		kfree(sbi->options.iocharset);
}

static void exfat_set_iocharset(struct exfat_mount_options *opts,
				char *iocharset)
{
	opts->iocharset = iocharset;
	if (!strcmp(opts->iocharset, "utf8"))
		opts->utf8 = 1;
	else
		opts->utf8 = 0;
}

static void exfat_put_super(struct super_block *sb)
{
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
@@ -292,7 +302,7 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
		break;
	case Opt_charset:
		exfat_free_iocharset(sbi);
		opts->iocharset = param->string;
		exfat_set_iocharset(opts, param->string);
		param->string = NULL;
		break;
	case Opt_errors:
@@ -664,8 +674,8 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
	/* set up enough so that it can read an inode */
	exfat_hash_init(sb);

	if (!strcmp(sbi->options.iocharset, "utf8"))
		opts->utf8 = 1;
	if (sbi->options.utf8)
		set_default_d_op(sb, &exfat_utf8_dentry_ops);
	else {
		sbi->nls_io = load_nls(sbi->options.iocharset);
		if (!sbi->nls_io) {
@@ -674,12 +684,8 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
			err = -EINVAL;
			goto free_table;
		}
	}

	if (sbi->options.utf8)
		set_default_d_op(sb, &exfat_utf8_dentry_ops);
	else
		set_default_d_op(sb, &exfat_dentry_ops);
	}

	root_inode = new_inode(sb);
	if (!root_inode) {
@@ -809,8 +815,8 @@ static int exfat_init_fs_context(struct fs_context *fc)
	sbi->options.fs_fmask = current->fs->umask;
	sbi->options.fs_dmask = current->fs->umask;
	sbi->options.allow_utime = -1;
	sbi->options.iocharset = exfat_default_iocharset;
	sbi->options.errors = EXFAT_ERRORS_RO;
	exfat_set_iocharset(&sbi->options, exfat_default_iocharset);

	fc->s_fs_info = sbi;
	fc->ops = &exfat_context_ops;