From nobody Thu Dec 30 06:15:20 2021 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 863121920720; Thu, 30 Dec 2021 06:15:23 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4JPdL26zbXz3mVl; Thu, 30 Dec 2021 06:15:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id DF2D213962; Thu, 30 Dec 2021 06:15:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 1BU6FKc6065010; Thu, 30 Dec 2021 06:15:20 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 1BU6FK9L065009; Thu, 30 Dec 2021 06:15:20 GMT (envelope-from git) Date: Thu, 30 Dec 2021 06:15:20 GMT Message-Id: <202112300615.1BU6FK9L065009@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Fedor Uporov Subject: git: f1d5e2c862ef - main - Improve extents verification logic List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: fsu X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: f1d5e2c862ef599efd1705b04d505d5415b77f82 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1640844923; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=GL0Tnv/tw1qbRRhv+nZAvH3extMOr1o2ciaWnCvJho0=; b=f2NPOj0oDoawLKe4xBq69lnkdQOWwsJ3k4cf9GC8r+HqlmesyFMr1w/VoGnyiMUWyi++vE RtXoGTAvu1yhPVcyK5zZEi67VWM7PAnQlSJNXr6ZPbaWm+u+LY0JoRTWpQmEnTeJi2799K EKiT5re/NH3gqKIgsd1GhbOwpyx1JDafRoGZk3PeMvHRGPdoAv2150L96fb+DywXruICLg Y9hjEmHjsgCkaOMuTjAlnw94p0vXNjfa1KeNVq6msJLtj2HH+4YncST5hBRwtn7ywU9m5h WO19LQ1hVFIf1QErBl3aWLJbt2TcD3d3/wzeZbKVQBeUXewW6kyau1dvqqKC3g== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1640844923; a=rsa-sha256; cv=none; b=m0x3jY+M6+0C/ZohOvNsdUw0gnIxchsBAibUC+jE+nlsNy/vHq0MXcOc3UELYr3nde+5vp LPu85iR6r4kQTZT45sGkQO4spupfSm1FozxSyEviONegtAgRvxo7txl6LGXGydg1VslG8u nKPwKWV5MjnF5lLRgI1kdp1fD22+EIrzRh3+SEpRnd9WQyzN+kgiXNmJ1NO1Ax0Qa3X03M XCMI49WEfTClndx46OU2XFuqPnlShZmHF/pHbjw7khhhwksJQJkn/tr+YFLd5L2e58b9CV mJYwyD4SFVFVNC/yA9cHuns2Kv24ao6E9upbhPUCdodj5ixZWQW8IHsafrP4Eg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by fsu: URL: https://cgit.FreeBSD.org/src/commit/?id=f1d5e2c862ef599efd1705b04d505d5415b77f82 commit f1d5e2c862ef599efd1705b04d505d5415b77f82 Author: Fedor Uporov AuthorDate: 2021-12-24 14:18:15 +0000 Commit: Fedor Uporov CommitDate: 2021-12-30 06:14:45 +0000 Improve extents verification logic Add functionality for extents validation inside the filesystem extents block. The main logic is implemented under ext4_validate_extent_entries() function, which verifies extents or extents indexes depending of extent depth value. PR: 259112 Reported by: Robert Morris Reviewed by: pfg MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D33375 --- sys/fs/ext2fs/ext2_extents.c | 291 +++++++++++++++++++++++++++++++---------- sys/fs/ext2fs/ext2_inode_cnv.c | 8 +- 2 files changed, 224 insertions(+), 75 deletions(-) diff --git a/sys/fs/ext2fs/ext2_extents.c b/sys/fs/ext2fs/ext2_extents.c index 3d393c4b7bbe..7aac71cf3fbc 100644 --- a/sys/fs/ext2fs/ext2_extents.c +++ b/sys/fs/ext2fs/ext2_extents.c @@ -61,8 +61,10 @@ static MALLOC_DEFINE(M_EXT2EXTENTS, "ext2_extents", "EXT2 extents"); #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 @@ ext4_ext_blk_check(struct inode *ip, e4fs_daddr_t blk) } 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 @@ ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk 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 @@ ext4_ext_walk_index(struct inode *ip, struct ext4_extent_index *ex, bool do_walk 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 @@ ext4_ext_walk_extent(struct inode *ip, struct ext4_extent *ep) } 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 @@ ext4_ext_print_path(struct inode *ip, struct ext4_extent_path *path) 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 @@ ext4_ext_in_cache(struct inode *ip, daddr_t lbn, struct ext4_extent *ep) 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 @@ ext4_ext_check_header(struct inode *ip, struct ext4_extent_header *eh) 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 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block, 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 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block, 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 @@ ext4_ext_find_extent(struct inode *ip, daddr_t block, i--; } - error = ext4_ext_check_header(ip, eh); + error = ext4_ext_check_header(ip, eh, 0); if (error) goto error; @@ -505,32 +688,6 @@ error: 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 @@ out2: 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 @@ ext4_read_extent_tree_block(struct inode *ip, e4fs_daddr_t pblk, 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 @@ ext4_ext_remove_space(struct inode *ip, off_t length, int flags, 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 index 445000c509db..c5f7d0657e36 100644 --- a/sys/fs/ext2fs/ext2_inode_cnv.c +++ b/sys/fs/ext2fs/ext2_inode_cnv.c @@ -65,11 +65,11 @@ ext2_print_inode(struct inode *in) 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) {