diff --git a/sys/fs/ext2fs/ext2_extents.c b/sys/fs/ext2fs/ext2_extents.c --- a/sys/fs/ext2fs/ext2_extents.c +++ b/sys/fs/ext2fs/ext2_extents.c @@ -61,8 +61,10 @@ #ifdef EXT2FS_PRINT_EXTENTS static const bool print_extents_walk = true; -static int ext4_ext_check_header(struct inode *, struct ext4_extent_header *); -static int ext4_ext_walk_header(struct inode *, struct ext4_extent_header *); +static int ext4_ext_check_header(struct inode *, struct ext4_extent_header *, + int); +static int ext4_ext_walk_header(struct inode *, struct ext4_extent_header *, + int); static inline e4fs_daddr_t ext4_ext_index_pblock(struct ext4_extent_index *); static inline e4fs_daddr_t ext4_ext_extent_pblock(struct ext4_extent *); @@ -80,7 +82,8 @@ } static int -ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk) +ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, int depth, + bool do_walk) { struct m_ext2fs *fs; struct buf *bp; @@ -91,7 +94,8 @@ if (print_extents_walk) printf(" index %p => (blk %u pblk %ju)\n", ex, - le32toh(ex->ei_blk), (uint64_t)le16toh(ex->ei_leaf_hi) << 32 | + le32toh(ex->ei_blk), + (uint64_t)le16toh(ex->ei_leaf_hi) << 32 | le32toh(ex->ei_leaf_lo)); if(!do_walk) @@ -108,7 +112,8 @@ return (error); } - error = ext4_ext_walk_header(ip, (struct ext4_extent_header *)bp->b_data); + error = ext4_ext_walk_header(ip, + (struct ext4_extent_header *)bp->b_data, depth); brelse(bp); @@ -135,42 +140,62 @@ } static int -ext4_ext_walk_header(struct inode *ip, struct ext4_extent_header *eh) +ext4_ext_walk_header(struct inode *ip, struct ext4_extent_header *eh, int depth) { int i, error = 0; - error = ext4_ext_check_header(ip, eh); + error = ext4_ext_check_header(ip, eh, depth); if (error) return (error); if (print_extents_walk) printf("header %p => (entries %d max %d depth %d gen %d)\n", eh, le16toh(eh->eh_ecount), - le16toh(eh->eh_max), le16toh(eh->eh_depth), le32toh(eh->eh_gen)); + le16toh(eh->eh_max), le16toh(eh->eh_depth), + le32toh(eh->eh_gen)); for (i = 0; i < le16toh(eh->eh_ecount) && error == 0; i++) if (eh->eh_depth != 0) error = ext4_ext_walk_index(ip, - (struct ext4_extent_index *)(eh + 1 + i), true); + (struct ext4_extent_index *)(eh + 1 + i), depth - 1, + true); else - error = ext4_ext_walk_extent(ip, (struct ext4_extent *)(eh + 1 + i)); + error = ext4_ext_walk_extent(ip, + (struct ext4_extent *)(eh + 1 + i)); return (error); } +int +ext4_ext_walk(struct inode *ip) +{ + struct ext4_extent_header *ehp; + + ehp = (struct ext4_extent_header *)ip->i_db; + + if (print_extents_walk) + printf("Extent status:ip=%ju\n", ip->i_number); + + if (!(ip->i_flag & IN_E4EXTENTS)) + return (0); + + return (ext4_ext_walk_header(ip, ehp, 0)); +} + static int ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path) { - int k, l, error = 0; + int k, depth, error = 0; - l = path->ep_depth; + depth = path->ep_depth; if (print_extents_walk) printf("ip=%ju, Path:\n", ip->i_number); - for (k = 0; k <= l && error == 0; k++, path++) { + for (k = 0; k <= depth && error == 0; k++, path++) { if (path->ep_index) { - error = ext4_ext_walk_index(ip, path->ep_index, false); + error = ext4_ext_walk_index(ip, path->ep_index, + depth - 1, false); } else if (path->ep_ext) { error = ext4_ext_walk_extent(ip, path->ep_ext); } @@ -178,22 +203,6 @@ return (error); } - -int -ext4_ext_walk(struct inode *ip) -{ - struct ext4_extent_header *ehp; - - ehp = (struct ext4_extent_header *)ip->i_db; - - if (print_extents_walk) - printf("Extent status:ip=%ju\n", ip->i_number); - - if (!(ip->i_flag & IN_E4EXTENTS)) - return (0); - - return (ext4_ext_walk_header(ip, ehp)); -} #endif static inline struct ext4_extent_header * @@ -277,8 +286,168 @@ return (ret); } +static inline int +ext4_ext_space_root(struct inode *ip) +{ + int size; + + size = sizeof(ip->i_data); + size -= sizeof(struct ext4_extent_header); + size /= sizeof(struct ext4_extent); + + return (size); +} + +static inline int +ext4_ext_space_block(struct inode *ip) +{ + struct m_ext2fs *fs; + int size; + + fs = ip->i_e2fs; + + size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent); + + return (size); +} + +static inline int +ext4_ext_space_root_idx(struct inode *ip) +{ + int size; + + size = sizeof(ip->i_data); + size -= sizeof(struct ext4_extent_header); + size /= sizeof(struct ext4_extent_index); + + return (size); +} + +static inline int +ext4_ext_space_block_idx(struct inode *ip) +{ + struct m_ext2fs *fs; + int size; + + fs = ip->i_e2fs; + + size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / + sizeof(struct ext4_extent_index); + + return (size); +} + static int -ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh) +ext4_ext_max_entries(struct inode *ip, int depth) +{ + + if (depth == ext4_ext_inode_depth(ip)) { + if (depth == 0) + return (ext4_ext_space_root(ip)); + else + return (ext4_ext_space_root_idx(ip)); + } else { + if (depth == 0) + return (ext4_ext_space_block(ip)); + else + return (ext4_ext_space_block_idx(ip)); + } +} + +static inline uint16_t +ext4_ext_get_actual_len(struct ext4_extent *ext) +{ + + return (le16toh(ext->e_len) <= EXT_INIT_MAX_LEN ? + le16toh(ext->e_len) : (le16toh(ext->e_len) - EXT_INIT_MAX_LEN)); +} + + +static int +ext4_inode_block_validate(struct inode *ip, e4fs_daddr_t start_blk, + unsigned int count) +{ + struct m_ext2fs *fs; + + fs = ip->i_e2fs; + + if ((start_blk <= le32toh(fs->e2fs->e2fs_first_dblock)) || + (start_blk + count < start_blk) || + (start_blk + count > fs->e2fs_bcount)) + return (EIO); + + return (0); +} + +static int +ext4_validate_extent(struct inode *ip, struct ext4_extent *ext) +{ + e4fs_daddr_t blk = ext4_ext_extent_pblock(ext); + uint32_t lblk = le32toh(ext->e_blk); + int len = ext4_ext_get_actual_len(ext); + + if (lblk + len <= lblk) + return (EIO); + + return (ext4_inode_block_validate(ip, blk, len)); +} + +static int +ext4_validate_extent_idx(struct inode *ip, struct ext4_extent_index *ext_idx) +{ + e4fs_daddr_t blk = ext4_ext_index_pblock(ext_idx); + + return (ext4_inode_block_validate(ip, blk, 1)); +} + +static int +ext4_validate_extent_entries(struct inode *ip, struct ext4_extent_header *eh, + int depth) +{ + unsigned int count; + + count = le16toh(eh->eh_ecount); + if (count == 0) + return (0); + + if (depth == 0) { + struct ext4_extent *ext = EXT_FIRST_EXTENT(eh); + uint32_t lblk = 0; + uint32_t prev = 0; + int len = 0; + while (count) { + /* leaf entries */ + if (ext4_validate_extent(ip, ext)) + return (EIO); + + /* Check for overlapping extents */ + lblk = le32toh(ext->e_blk); + len = ext4_ext_get_actual_len(ext); + if ((lblk <= prev) && prev) + return (EIO); + + ext++; + count--; + prev = lblk + len - 1; + } + } else { + struct ext4_extent_index *ext_idx = EXT_FIRST_INDEX(eh); + while (count) { + if (ext4_validate_extent_idx(ip, ext_idx)) + return (EIO); + + ext_idx++; + count--; + } + } + + return (0); +} + +static int +ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh, + int depth) { char *error_msg; @@ -286,18 +455,32 @@ error_msg = "header: invalid magic"; goto corrupted; } + if (le16toh(eh->eh_depth) != depth || + le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX) + { + error_msg = "header: invalid eh_depth"; + goto corrupted; + } if (eh->eh_max == 0) { error_msg = "header: invalid eh_max"; goto corrupted; } + if (le16toh(eh->eh_max) > ext4_ext_max_entries(ip, depth)) { + error_msg = "header: too large eh_max"; + goto corrupted; + } if (le16toh(eh->eh_ecount) > le16toh(eh->eh_max)) { error_msg = "header: invalid eh_entries"; goto corrupted; } - if (eh->eh_depth > 5) { + if (le16toh(eh->eh_depth) > EXT4_EXT_DEPTH_MAX) { error_msg = "header: invalid eh_depth"; goto corrupted; } + if (ext4_validate_extent_entries(ip, eh, depth)) { + error_msg = "header: invalid extent entries"; + goto corrupted; + } return (0); @@ -426,7 +609,7 @@ ppos = 0; alloc = 0; - error = ext4_ext_check_header(ip, eh); + error = ext4_ext_check_header(ip, eh, depth); if (error) return (error); @@ -472,7 +655,7 @@ bqrelse(bp); eh = ext4_ext_block_header(path[ppos].ep_data); - if (ext4_ext_check_header(ip, eh) || + if (ext4_ext_check_header(ip, eh, i - 1) || ext2_extent_blk_csum_verify(ip, path[ppos].ep_data)) { error = EIO; goto error; @@ -483,7 +666,7 @@ i--; } - error = ext4_ext_check_header(ip, eh); + error = ext4_ext_check_header(ip, eh, 0); if (error) goto error; @@ -505,32 +688,6 @@ return (error); } -static inline int -ext4_ext_space_root(struct inode *ip) -{ - int size; - - size = sizeof(ip->i_data); - size -= sizeof(struct ext4_extent_header); - size /= sizeof(struct ext4_extent); - - return (size); -} - -static inline int -ext4_ext_space_block(struct inode *ip) -{ - struct m_ext2fs *fs; - int size; - - fs = ip->i_e2fs; - - size = (fs->e2fs_bsize - sizeof(struct ext4_extent_header)) / - sizeof(struct ext4_extent); - - return (size); -} - static inline int ext4_ext_space_block_index(struct inode *ip) { @@ -1339,14 +1496,6 @@ return (error); } -static inline uint16_t -ext4_ext_get_actual_len(struct ext4_extent *ext) -{ - - return (le16toh(ext->e_len) <= EXT_INIT_MAX_LEN ? - le16toh(ext->e_len) : (le16toh(ext->e_len) - EXT_INIT_MAX_LEN)); -} - static inline struct ext4_extent_header * ext4_ext_header(struct inode *ip) { @@ -1503,7 +1652,7 @@ goto err; } - error = ext4_ext_check_header(ip, eh); + error = ext4_ext_check_header(ip, eh, depth); if (error) goto err; @@ -1544,7 +1693,7 @@ ehp = (struct ext4_extent_header *)ip->i_db; depth = ext4_ext_inode_depth(ip); - error = ext4_ext_check_header(ip, ehp); + error = ext4_ext_check_header(ip, ehp, depth); if(error) return (error); diff --git a/sys/fs/ext2fs/ext2_inode_cnv.c b/sys/fs/ext2fs/ext2_inode_cnv.c --- a/sys/fs/ext2fs/ext2_inode_cnv.c +++ b/sys/fs/ext2fs/ext2_inode_cnv.c @@ -65,11 +65,11 @@ in->i_uid, in->i_gid, (uintmax_t)in->i_size); printf("Links: %3d Blockcount: %ju\n", in->i_nlink, (uintmax_t)in->i_blocks); - printf("ctime: 0x%x ", in->i_ctime); - printf("atime: 0x%x ", in->i_atime); - printf("mtime: 0x%x ", in->i_mtime); + printf("ctime: 0x%llx ", (unsigned long long)in->i_ctime); + printf("atime: 0x%llx ", (unsigned long long)in->i_atime); + printf("mtime: 0x%llx ", (unsigned long long)in->i_mtime); if (E2DI_HAS_XTIME(in)) - printf("crtime %#x\n", in->i_birthtime); + printf("crtime %llx\n", (unsigned long long)in->i_birthtime); else printf("\n"); if (in->i_flag & IN_E4EXTENTS) {