mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 00:53:34 +02:00
exfat: validate cluster allocation bits of the allocation bitmap
syzbot created an exfat image with cluster bits not set for the allocation bitmap. exfat-fs reads and uses the allocation bitmap without checking this. The problem is that if the start cluster of the allocation bitmap is 6, cluster 6 can be allocated when creating a directory with mkdir. exfat zeros out this cluster in exfat_mkdir, which can delete existing entries. This can reallocate the allocated entries. In addition, the allocation bitmap is also zeroed out, so cluster 6 can be reallocated. This patch adds exfat_test_bitmap_range to validate that clusters used for the allocation bitmap are correctly marked as in-use. Reported-by: syzbot+a725ab460fc1def9896f@syzkaller.appspotmail.com Tested-by: syzbot+a725ab460fc1def9896f@syzkaller.appspotmail.com Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
6dfba10838
commit
79c1587b6c
|
|
@ -26,12 +26,55 @@
|
|||
/*
|
||||
* Allocation Bitmap Management Functions
|
||||
*/
|
||||
static bool exfat_test_bitmap_range(struct super_block *sb, unsigned int clu,
|
||||
unsigned int count)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
unsigned int start = clu;
|
||||
unsigned int end = clu + count;
|
||||
unsigned int ent_idx, i, b;
|
||||
unsigned int bit_offset, bits_to_check;
|
||||
__le_long *bitmap_le;
|
||||
unsigned long mask, word;
|
||||
|
||||
if (!is_valid_cluster(sbi, start) || !is_valid_cluster(sbi, end - 1))
|
||||
return false;
|
||||
|
||||
while (start < end) {
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(start);
|
||||
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
bitmap_le = (__le_long *)sbi->vol_amap[i]->b_data;
|
||||
|
||||
/* Calculate how many bits we can check in the current word */
|
||||
bit_offset = b % BITS_PER_LONG;
|
||||
bits_to_check = min(end - start,
|
||||
(unsigned int)(BITS_PER_LONG - bit_offset));
|
||||
|
||||
/* Create a bitmask for the range of bits to check */
|
||||
if (bits_to_check >= BITS_PER_LONG)
|
||||
mask = ~0UL;
|
||||
else
|
||||
mask = ((1UL << bits_to_check) - 1) << bit_offset;
|
||||
word = lel_to_cpu(bitmap_le[b / BITS_PER_LONG]);
|
||||
|
||||
/* Check if all bits in the mask are set */
|
||||
if ((word & mask) != mask)
|
||||
return false;
|
||||
|
||||
start += bits_to_check;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int exfat_allocate_bitmap(struct super_block *sb,
|
||||
struct exfat_dentry *ep)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
long long map_size;
|
||||
unsigned int i, need_map_size;
|
||||
unsigned int i, j, need_map_size;
|
||||
sector_t sector;
|
||||
|
||||
sbi->map_clu = le32_to_cpu(ep->dentry.bitmap.start_clu);
|
||||
|
|
@ -58,20 +101,25 @@ static int exfat_allocate_bitmap(struct super_block *sb,
|
|||
sector = exfat_cluster_to_sector(sbi, sbi->map_clu);
|
||||
for (i = 0; i < sbi->map_sectors; i++) {
|
||||
sbi->vol_amap[i] = sb_bread(sb, sector + i);
|
||||
if (!sbi->vol_amap[i]) {
|
||||
/* release all buffers and free vol_amap */
|
||||
int j = 0;
|
||||
|
||||
while (j < i)
|
||||
brelse(sbi->vol_amap[j++]);
|
||||
|
||||
kvfree(sbi->vol_amap);
|
||||
sbi->vol_amap = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
if (!sbi->vol_amap[i])
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (exfat_test_bitmap_range(sb, sbi->map_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(map_size, sbi)) == false)
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
j = 0;
|
||||
/* release all buffers and free vol_amap */
|
||||
while (j < i)
|
||||
brelse(sbi->vol_amap[j++]);
|
||||
|
||||
kvfree(sbi->vol_amap);
|
||||
sbi->vol_amap = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int exfat_load_bitmap(struct super_block *sb)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user