Skip to content

Commit a13d1a4

Browse files
author
Al Viro
committed
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: Christian Brauner <brauner@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 529f89a commit a13d1a4

3 files changed

Lines changed: 16 additions & 19 deletions

File tree

fs/exfat/exfat_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ struct exfat_sb_info {
275275

276276
spinlock_t inode_hash_lock;
277277
struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
278+
struct rcu_head rcu;
278279
};
279280

280281
#define EXFAT_CACHE_VALID 0

fs/exfat/nls.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,6 @@ static int exfat_load_upcase_table(struct super_block *sb,
655655
unsigned int sect_size = sb->s_blocksize;
656656
unsigned int i, index = 0;
657657
u32 chksum = 0;
658-
int ret;
659658
unsigned char skip = false;
660659
unsigned short *upcase_table;
661660

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

702700
exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
703701
index, chksum, utbl_checksum);
704-
ret = -EINVAL;
705-
free_table:
706-
exfat_free_upcase_table(sbi);
707-
return ret;
702+
return -EINVAL;
708703
}
709704

710705
static int exfat_load_default_upcase_table(struct super_block *sb)
711706
{
712-
int i, ret = -EIO;
707+
int i;
713708
struct exfat_sb_info *sbi = EXFAT_SB(sb);
714709
unsigned char skip = false;
715710
unsigned short uni = 0, *upcase_table;
@@ -740,8 +735,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
740735
return 0;
741736

742737
/* FATAL error: default upcase table has error */
743-
exfat_free_upcase_table(sbi);
744-
return ret;
738+
return -EIO;
745739
}
746740

747741
int exfat_create_upcase_table(struct super_block *sb)

fs/exfat/super.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ static void exfat_put_super(struct super_block *sb)
3939
exfat_free_bitmap(sbi);
4040
brelse(sbi->boot_bh);
4141
mutex_unlock(&sbi->s_lock);
42-
43-
unload_nls(sbi->nls_io);
44-
exfat_free_upcase_table(sbi);
4542
}
4643

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

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

614611
free_alloc_bitmap:
615612
exfat_free_bitmap(sbi);
616-
free_upcase_table:
617-
exfat_free_upcase_table(sbi);
618613
free_bh:
619614
brelse(sbi->boot_bh);
620615
return ret;
@@ -701,12 +696,10 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
701696
sb->s_root = NULL;
702697

703698
free_table:
704-
exfat_free_upcase_table(sbi);
705699
exfat_free_bitmap(sbi);
706700
brelse(sbi->boot_bh);
707701

708702
check_nls_io:
709-
unload_nls(sbi->nls_io);
710703
return err;
711704
}
712705

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

767+
static void delayed_free(struct rcu_head *p)
768+
{
769+
struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu);
770+
771+
unload_nls(sbi->nls_io);
772+
exfat_free_upcase_table(sbi);
773+
exfat_free_sbi(sbi);
774+
}
775+
774776
static void exfat_kill_sb(struct super_block *sb)
775777
{
776778
struct exfat_sb_info *sbi = sb->s_fs_info;
777779

778780
kill_block_super(sb);
779781
if (sbi)
780-
exfat_free_sbi(sbi);
782+
call_rcu(&sbi->rcu, delayed_free);
781783
}
782784

783785
static struct file_system_type exfat_fs_type = {

0 commit comments

Comments
 (0)