mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
exfat: validate the cluster bitmap bits of directory
Syzbot created this issue by testing an image that did not have the root cluster bitmap bit marked. After accessing a file through the root directory via exfat_lookup, when creating a file again with mkdir, the root cluster bit can be allocated for direcotry, which can cause the root cluster to be zeroed out and the same entry can be allocated in the same cluster. This patch improved this issue by adding exfat_test_bitmap to validate the cluster bits of the root directory and directory. And the first cluster bit of the root directory should never be unset except when storage is corrupted. This bit is set to allow operations after mount. Reported-by: syzbot+5216036fc59c43d1ee02@syzkaller.appspotmail.com Tested-by: syzbot+5216036fc59c43d1ee02@syzkaller.appspotmail.com Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
@@ -183,11 +183,10 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
|
||||
kvfree(sbi->vol_amap);
|
||||
}
|
||||
|
||||
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync)
|
||||
{
|
||||
int i, b;
|
||||
unsigned int ent_idx;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
if (!is_valid_cluster(sbi, clu))
|
||||
@@ -202,11 +201,10 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync)
|
||||
{
|
||||
int i, b;
|
||||
unsigned int ent_idx;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
if (!is_valid_cluster(sbi, clu))
|
||||
@@ -226,6 +224,28 @@ int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu)
|
||||
{
|
||||
int i, b;
|
||||
unsigned int ent_idx;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
if (!sbi->vol_amap)
|
||||
return true;
|
||||
|
||||
if (!is_valid_cluster(sbi, clu))
|
||||
return false;
|
||||
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
|
||||
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
if (!test_bit_le(b, sbi->vol_amap[i]->b_data))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
|
||||
* the cluster heap.
|
||||
|
||||
@@ -604,6 +604,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!exfat_test_bitmap(sb, clu)) {
|
||||
exfat_err(sb, "failed to test cluster bit(%u)", clu);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* byte offset in cluster */
|
||||
off = EXFAT_CLU_OFFSET(off, sbi);
|
||||
|
||||
|
||||
@@ -452,8 +452,9 @@ int exfat_count_num_clusters(struct super_block *sb,
|
||||
/* balloc.c */
|
||||
int exfat_load_bitmap(struct super_block *sb);
|
||||
void exfat_free_bitmap(struct exfat_sb_info *sbi);
|
||||
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync);
|
||||
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
|
||||
int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync);
|
||||
int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync);
|
||||
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu);
|
||||
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
|
||||
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
|
||||
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
|
||||
|
||||
@@ -205,7 +205,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
|
||||
cur_cmap_i = next_cmap_i;
|
||||
}
|
||||
|
||||
err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
|
||||
err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)));
|
||||
if (err)
|
||||
break;
|
||||
clu++;
|
||||
@@ -233,7 +233,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
|
||||
cur_cmap_i = next_cmap_i;
|
||||
}
|
||||
|
||||
if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))))
|
||||
if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))))
|
||||
break;
|
||||
|
||||
if (sbi->options.discard) {
|
||||
@@ -409,7 +409,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
}
|
||||
|
||||
/* update allocation bitmap */
|
||||
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) {
|
||||
if (exfat_set_bitmap(sb, new_clu, sync_bmap)) {
|
||||
ret = -EIO;
|
||||
goto free_cluster;
|
||||
}
|
||||
|
||||
@@ -629,6 +629,17 @@ static int __exfat_fill_super(struct super_block *sb,
|
||||
goto free_bh;
|
||||
}
|
||||
|
||||
if (!exfat_test_bitmap(sb, sbi->root_dir)) {
|
||||
exfat_warn(sb, "failed to test first cluster bit of root dir(%u)",
|
||||
sbi->root_dir);
|
||||
/*
|
||||
* The first cluster bit of the root directory should never
|
||||
* be unset except when storage is corrupted. This bit is
|
||||
* set to allow operations after mount.
|
||||
*/
|
||||
exfat_set_bitmap(sb, sbi->root_dir, false);
|
||||
}
|
||||
|
||||
ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
|
||||
if (ret) {
|
||||
exfat_err(sb, "failed to scan clusters");
|
||||
|
||||
Reference in New Issue
Block a user