Commit 79c1587b authored by Namjae Jeon's avatar Namjae Jeon
Browse files

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: default avatar <syzbot+a725ab460fc1def9896f@syzkaller.appspotmail.com>
Tested-by: default avatar <syzbot+a725ab460fc1def9896f@syzkaller.appspotmail.com>
Reviewed-by: default avatarYuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: default avatarSungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent 6dfba108
Loading
Loading
Loading
Loading
+60 −12
Original line number Diff line number Diff line
@@ -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,10 +101,19 @@ 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;
		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++]);

@@ -69,10 +121,6 @@ static int exfat_allocate_bitmap(struct super_block *sb,
	sbi->vol_amap = NULL;
	return -EIO;
}
	}

	return 0;
}

int exfat_load_bitmap(struct super_block *sb)
{