Skip to content

Commit

Permalink
exfat: move freeing sbi, upcase table and dropping nls into rcu-delay…
Browse files Browse the repository at this point in the history
…ed 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: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
  • Loading branch information
Al Viro authored and namjaejeon committed Aug 7, 2024
1 parent 8269c2d commit 7ef5cda
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 19 deletions.
1 change: 1 addition & 0 deletions exfat_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,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
Expand Down
14 changes: 4 additions & 10 deletions nls.c
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,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;

Expand All @@ -674,8 +673,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) {
Expand All @@ -702,15 +700,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;
Expand Down Expand Up @@ -741,8 +736,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)
Expand Down
20 changes: 11 additions & 9 deletions super.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,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)
Expand Down Expand Up @@ -620,7 +617,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);
Expand All @@ -633,8 +630,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;
Expand Down Expand Up @@ -733,12 +728,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;
}

Expand Down Expand Up @@ -803,13 +796,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 = {
Expand Down

0 comments on commit 7ef5cda

Please sign in to comment.