Skip to content
Snippets Groups Projects
  • Qu Wenruo's avatar
    511a1303
    fs: btrfs: limit the mapped length to the original length · 511a1303
    Qu Wenruo authored and Tom Rini's avatar Tom Rini committed
    
    [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: default avatarAndreas Schwab <schwab@linux-m68k.org>
    Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
    511a1303
    History
    fs: btrfs: limit the mapped length to the original length
    Qu Wenruo authored and Tom Rini's avatar Tom Rini committed
    
    [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: default avatarAndreas Schwab <schwab@linux-m68k.org>
    Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
volumes.c 30.75 KiB