mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
fs: btrfs: limit the mapped length to the original length
[BUG] There is a bug report that btrfs driver caused hang during file read: This breaks btrfs on the HiFive Unmatched. => pci enum PCIE-0: Link up (Gen1-x8, Bus0) => nvme scan => load nvme 0:2 0x8c000000 /boot/dtb/sifive/hifive-unmatched-a00.dtb [hangs] [CAUSE] The reporter provided some debug output: read_extent_data: cur=615817216, orig_len=16384, cur_len=16384 read_extent_data: btrfs_map_block: cur_len=479944704; ret=0 read_extent_data: ret=0 read_extent_data: cur=615833600, orig_len=4096, cur_len=4096 read_extent_data: btrfs_map_block: cur_len=479928320; ret=0 Note the second and the last line, the @cur_len is 450+MiB, which is almost a chunk size. And inside __btrfs_map_block(), we limits the returned value to stripe length, but that's depending on the chunk type: if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID1C3 | BTRFS_BLOCK_GROUP_RAID1C4 | BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6 | BTRFS_BLOCK_GROUP_RAID10 | BTRFS_BLOCK_GROUP_DUP)) { /* we limit the length of each bio to what fits in a stripe */ *length = min_t(u64, ce->size - offset, map->stripe_len - stripe_offset); } else { *length = ce->size - offset; } This means, if the chunk is SINGLE profile, then we don't limit the returned length at all, and even for other profiles, we can still return a length much larger than the requested one. [FIX] Properly clamp the returned length, preventing it from returning a much larger range than expected. Reported-by: Andreas Schwab <schwab@linux-m68k.org> Signed-off-by: Qu Wenruo <wqu@suse.com>
This commit is contained in:
parent
8c39999acb
commit
511a1303c9
1 changed files with 2 additions and 0 deletions
|
@ -956,6 +956,7 @@ int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
|
||||||
struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
|
struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
|
||||||
struct cache_extent *ce;
|
struct cache_extent *ce;
|
||||||
struct map_lookup *map;
|
struct map_lookup *map;
|
||||||
|
u64 orig_len = *length;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
u64 stripe_offset;
|
u64 stripe_offset;
|
||||||
u64 *raid_map = NULL;
|
u64 *raid_map = NULL;
|
||||||
|
@ -1047,6 +1048,7 @@ again:
|
||||||
} else {
|
} else {
|
||||||
*length = ce->size - offset;
|
*length = ce->size - offset;
|
||||||
}
|
}
|
||||||
|
*length = min_t(u64, *length, orig_len);
|
||||||
|
|
||||||
if (!multi_ret)
|
if (!multi_ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
Loading…
Reference in a new issue