Skip to content

Commit 866cba3

Browse files
committed
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>
1 parent 4e163c3 commit 866cba3

5 files changed

Lines changed: 46 additions & 9 deletions

File tree

fs/exfat/balloc.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,10 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
183183
kvfree(sbi->vol_amap);
184184
}
185185

186-
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
186+
int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync)
187187
{
188188
int i, b;
189189
unsigned int ent_idx;
190-
struct super_block *sb = inode->i_sb;
191190
struct exfat_sb_info *sbi = EXFAT_SB(sb);
192191

193192
if (!is_valid_cluster(sbi, clu))
@@ -202,11 +201,10 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
202201
return 0;
203202
}
204203

205-
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
204+
int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync)
206205
{
207206
int i, b;
208207
unsigned int ent_idx;
209-
struct super_block *sb = inode->i_sb;
210208
struct exfat_sb_info *sbi = EXFAT_SB(sb);
211209

212210
if (!is_valid_cluster(sbi, clu))
@@ -226,6 +224,28 @@ int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
226224
return 0;
227225
}
228226

227+
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu)
228+
{
229+
int i, b;
230+
unsigned int ent_idx;
231+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
232+
233+
if (!sbi->vol_amap)
234+
return true;
235+
236+
if (!is_valid_cluster(sbi, clu))
237+
return false;
238+
239+
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
240+
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
241+
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
242+
243+
if (!test_bit_le(b, sbi->vol_amap[i]->b_data))
244+
return false;
245+
246+
return true;
247+
}
248+
229249
/*
230250
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
231251
* the cluster heap.

fs/exfat/dir.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir
604604
if (ret)
605605
return ret;
606606

607+
if (!exfat_test_bitmap(sb, clu)) {
608+
exfat_err(sb, "failed to test cluster bit(%u)", clu);
609+
return -EIO;
610+
}
611+
607612
/* byte offset in cluster */
608613
off = EXFAT_CLU_OFFSET(off, sbi);
609614

fs/exfat/exfat_fs.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,9 @@ int exfat_count_num_clusters(struct super_block *sb,
452452
/* balloc.c */
453453
int exfat_load_bitmap(struct super_block *sb);
454454
void exfat_free_bitmap(struct exfat_sb_info *sbi);
455-
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync);
456-
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
455+
int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync);
456+
int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync);
457+
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu);
457458
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
458459
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
459460
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);

fs/exfat/fatent.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
205205
cur_cmap_i = next_cmap_i;
206206
}
207207

208-
err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
208+
err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)));
209209
if (err)
210210
break;
211211
clu++;
@@ -233,7 +233,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
233233
cur_cmap_i = next_cmap_i;
234234
}
235235

236-
if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))))
236+
if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))))
237237
break;
238238

239239
if (sbi->options.discard) {
@@ -409,7 +409,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
409409
}
410410

411411
/* update allocation bitmap */
412-
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) {
412+
if (exfat_set_bitmap(sb, new_clu, sync_bmap)) {
413413
ret = -EIO;
414414
goto free_cluster;
415415
}

fs/exfat/super.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,17 @@ static int __exfat_fill_super(struct super_block *sb,
629629
goto free_bh;
630630
}
631631

632+
if (!exfat_test_bitmap(sb, sbi->root_dir)) {
633+
exfat_warn(sb, "failed to test first cluster bit of root dir(%u)",
634+
sbi->root_dir);
635+
/*
636+
* The first cluster bit of the root directory should never
637+
* be unset except when storage is corrupted. This bit is
638+
* set to allow operations after mount.
639+
*/
640+
exfat_set_bitmap(sb, sbi->root_dir, false);
641+
}
642+
632643
ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
633644
if (ret) {
634645
exfat_err(sb, "failed to scan clusters");

0 commit comments

Comments
 (0)