Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+46 -9
+24 -4
fs/exfat/balloc.c
··· 183 183 kvfree(sbi->vol_amap); 184 184 } 185 185 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) 187 187 { 188 188 int i, b; 189 189 unsigned int ent_idx; 190 - struct super_block *sb = inode->i_sb; 191 190 struct exfat_sb_info *sbi = EXFAT_SB(sb); 192 191 193 192 if (!is_valid_cluster(sbi, clu)) ··· 201 202 return 0; 202 203 } 203 204 204 - int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) 205 + int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync) 205 206 { 206 207 int i, b; 207 208 unsigned int ent_idx; 208 - struct super_block *sb = inode->i_sb; 209 209 struct exfat_sb_info *sbi = EXFAT_SB(sb); 210 210 211 211 if (!is_valid_cluster(sbi, clu)) ··· 222 224 exfat_update_bh(sbi->vol_amap[i], sync); 223 225 224 226 return 0; 227 + } 228 + 229 + bool exfat_test_bitmap(struct super_block *sb, unsigned int clu) 230 + { 231 + int i, b; 232 + unsigned int ent_idx; 233 + struct exfat_sb_info *sbi = EXFAT_SB(sb); 234 + 235 + if (!sbi->vol_amap) 236 + return true; 237 + 238 + if (!is_valid_cluster(sbi, clu)) 239 + return false; 240 + 241 + ent_idx = CLUSTER_TO_BITMAP_ENT(clu); 242 + i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); 243 + b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); 244 + 245 + if (!test_bit_le(b, sbi->vol_amap[i]->b_data)) 246 + return false; 247 + 248 + return true; 225 249 } 226 250 227 251 /*
+5
fs/exfat/dir.c
··· 604 604 if (ret) 605 605 return ret; 606 606 607 + if (!exfat_test_bitmap(sb, clu)) { 608 + exfat_err(sb, "failed to test cluster bit(%u)", clu); 609 + return -EIO; 610 + } 611 + 607 612 /* byte offset in cluster */ 608 613 off = EXFAT_CLU_OFFSET(off, sbi); 609 614
+3 -2
fs/exfat/exfat_fs.h
··· 452 452 /* balloc.c */ 453 453 int exfat_load_bitmap(struct super_block *sb); 454 454 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); 457 458 unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu); 458 459 int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count); 459 460 int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
+3 -3
fs/exfat/fatent.c
··· 205 205 cur_cmap_i = next_cmap_i; 206 206 } 207 207 208 - err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); 208 + err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))); 209 209 if (err) 210 210 break; 211 211 clu++; ··· 233 233 cur_cmap_i = next_cmap_i; 234 234 } 235 235 236 - if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)))) 236 + if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)))) 237 237 break; 238 238 239 239 if (sbi->options.discard) { ··· 409 409 } 410 410 411 411 /* update allocation bitmap */ 412 - if (exfat_set_bitmap(inode, new_clu, sync_bmap)) { 412 + if (exfat_set_bitmap(sb, new_clu, sync_bmap)) { 413 413 ret = -EIO; 414 414 goto free_cluster; 415 415 }
+11
fs/exfat/super.c
··· 629 629 goto free_bh; 630 630 } 631 631 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 + 632 643 ret = exfat_count_used_clusters(sb, &sbi->used_clusters); 633 644 if (ret) { 634 645 exfat_err(sb, "failed to scan clusters");