Commit a13d1a4d authored by Al Viro's avatar Al Viro
Browse files

exfat: move freeing sbi, upcase table and dropping nls into rcu-delayed helper



That stuff can be accessed by ->d_hash()/->d_compare(); as it is, we have
a hard-to-hit UAF if rcu pathwalk manages to get into ->d_hash() on a filesystem
that is in process of getting shut down.

Besides, having nls and upcase table cleanup moved from ->put_super() towards
the place where sbi is freed makes for simpler failure exits.

Acked-by: default avatarChristian Brauner <brauner@kernel.org>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 529f89a9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -275,6 +275,7 @@ struct exfat_sb_info {

	spinlock_t inode_hash_lock;
	struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
	struct rcu_head rcu;
};

#define EXFAT_CACHE_VALID	0
+4 −10
Original line number Diff line number Diff line
@@ -655,7 +655,6 @@ static int exfat_load_upcase_table(struct super_block *sb,
	unsigned int sect_size = sb->s_blocksize;
	unsigned int i, index = 0;
	u32 chksum = 0;
	int ret;
	unsigned char skip = false;
	unsigned short *upcase_table;

@@ -673,8 +672,7 @@ static int exfat_load_upcase_table(struct super_block *sb,
		if (!bh) {
			exfat_err(sb, "failed to read sector(0x%llx)",
				  (unsigned long long)sector);
			ret = -EIO;
			goto free_table;
			return -EIO;
		}
		sector++;
		for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
@@ -701,15 +699,12 @@ static int exfat_load_upcase_table(struct super_block *sb,

	exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
		  index, chksum, utbl_checksum);
	ret = -EINVAL;
free_table:
	exfat_free_upcase_table(sbi);
	return ret;
	return -EINVAL;
}

static int exfat_load_default_upcase_table(struct super_block *sb)
{
	int i, ret = -EIO;
	int i;
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
	unsigned char skip = false;
	unsigned short uni = 0, *upcase_table;
@@ -740,8 +735,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
		return 0;

	/* FATAL error: default upcase table has error */
	exfat_free_upcase_table(sbi);
	return ret;
	return -EIO;
}

int exfat_create_upcase_table(struct super_block *sb)
+11 −9
Original line number Diff line number Diff line
@@ -39,9 +39,6 @@ static void exfat_put_super(struct super_block *sb)
	exfat_free_bitmap(sbi);
	brelse(sbi->boot_bh);
	mutex_unlock(&sbi->s_lock);

	unload_nls(sbi->nls_io);
	exfat_free_upcase_table(sbi);
}

static int exfat_sync_fs(struct super_block *sb, int wait)
@@ -600,7 +597,7 @@ static int __exfat_fill_super(struct super_block *sb)
	ret = exfat_load_bitmap(sb);
	if (ret) {
		exfat_err(sb, "failed to load alloc-bitmap");
		goto free_upcase_table;
		goto free_bh;
	}

	ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
@@ -613,8 +610,6 @@ static int __exfat_fill_super(struct super_block *sb)

free_alloc_bitmap:
	exfat_free_bitmap(sbi);
free_upcase_table:
	exfat_free_upcase_table(sbi);
free_bh:
	brelse(sbi->boot_bh);
	return ret;
@@ -701,12 +696,10 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
	sb->s_root = NULL;

free_table:
	exfat_free_upcase_table(sbi);
	exfat_free_bitmap(sbi);
	brelse(sbi->boot_bh);

check_nls_io:
	unload_nls(sbi->nls_io);
	return err;
}

@@ -771,13 +764,22 @@ static int exfat_init_fs_context(struct fs_context *fc)
	return 0;
}

static void delayed_free(struct rcu_head *p)
{
	struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu);

	unload_nls(sbi->nls_io);
	exfat_free_upcase_table(sbi);
	exfat_free_sbi(sbi);
}

static void exfat_kill_sb(struct super_block *sb)
{
	struct exfat_sb_info *sbi = sb->s_fs_info;

	kill_block_super(sb);
	if (sbi)
		exfat_free_sbi(sbi);
		call_rcu(&sbi->rcu, delayed_free);
}

static struct file_system_type exfat_fs_type = {