hfsplus: fix overflow in hfsplus_get_block
Subsystems (2)
PATCH 1/3 hfsplus: fix overflow in hfsplus_get_block
For filesystems larger than 2TB the final sector number passed to map_bh might overflow the range representable in a 32-bit data type. Make sure we use a sector_t for it and the arithmetics calculating it.
fs/hfsplus/extents.c +6 -3
@@ -209,6 +209,7 @@ int hfsplus_get_block(struct inode *inodstruct hfsplus_inode_info *hip =HFSPLUS_I (inode);int res = -EIO ;u32 ablock, dblock, mask; +sector_t sector;int was_dirty =0 ;int shift;
@@ -255,10 +256,12 @@ int hfsplus_get_block(struct inode *inod done:dprint (DBG_EXTENT ,"get_block(%lu): %llu - %u\n" , inode->i_ino, (long long )iblock, dblock); + mask = (1 << sbi->fs_shift) -1 ; -map_bh (bh_result, sb, - (dblock << sbi->fs_shift) + sbi->blockoffset + - (iblock & mask)); + sector = ((sector_t )dblock << sbi->fs_shift) + + sbi->blockoffset + (iblock & mask); +map_bh (bh_result, sb, sector); +if (create) {set_buffer_new (bh_result); hip->phys_size += sb->s_blocksize;
PATCH 2/3 hfsplus: fix overflow in hfsplus_read_wrapper
For partitions larger than 2TB or at such an offset the hfs wrapper code in hfsplus might overflow the range representable in a 32-bit data type. Make sure we use a sector_t for the arithmetics leading to it.
I'm not sure this code can be readed at all as hfs itself never supported such large volumes.
fs/hfsplus/wrapper.c +3 -2
@@ -169,8 +169,9 @@ reread:if (!hfsplus_read_mdb (sbi->s_vhdr, &wd)) goto out_free_backup_vhdr; wd.ablk_size >>=HFSPLUS_SECTOR_SHIFT ; - part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; - part_size = wd.embed_count * wd.ablk_size; + part_start += (sector_t )wd.ablk_start + + (sector_t )wd.embed_start * wd.ablk_size; + part_size = (sector_t )wd.embed_count * wd.ablk_size; goto reread;default :/*
PATCH 2/3 hfsplus: lift the 2TB size limit
In reply to Christoph HellwigReplace the hardcoded 2TB limit with a dynamic limit based on the block size now that we have fixed the few overflows preventing operation with large volumes.
fs/hfsplus/super.c +9 -0
@@ -393,6 +393,13 @@ static int hfsplus_fill_super(struct supif (!sbi->rsrc_clump_blocks) sbi->rsrc_clump_blocks =1 ; + err =generic_check_addressable (sbi->alloc_blksz_shift, + sbi->total_blocks); +if (err) { +printk (KERN_ERR "hfs: filesystem size too large.\n" ); + goto out_free_vhdr; + } +/* Set up operations so we can load metadata */ sb->s_op = &hfsplus_sops; sb->s_maxbytes =MAX_LFS_FILESIZE ;
@@ -417,6 +424,8 @@ static int hfsplus_fill_super(struct sup sb->s_flags |=MS_RDONLY ; } + err = -EINVAL ; +/* Load metadata objects (B*Trees) */ sbi->ext_tree =hfs_btree_open (sb,HFSPLUS_EXT_CNID );if (!sbi->ext_tree) {
fs/hfsplus/wrapper.c +0 -4
@@ -138,10 +138,6 @@ int hfsplus_read_wrapper(struct super_blif (hfsplus_get_last_session (sb, &part_start, &part_size)) goto out; -if ((u64 )part_start + part_size >0x100000000ULL ) { -pr_err ("hfs: volumes larger than 2TB are not supported yet\n" ); - goto out; - } error = -ENOMEM ; sbi->s_vhdr =kmalloc (HFSPLUS_SECTOR_SIZE ,GFP_KERNEL );