diff options
author | Justin M. Forbes <jforbes@fedoraproject.org> | 2018-07-05 09:31:36 -0500 |
---|---|---|
committer | Justin M. Forbes <jforbes@fedoraproject.org> | 2018-07-05 09:31:36 -0500 |
commit | 1a9181ff17aa8e6c755e8e9f49b6a3b840c8ce8f (patch) | |
tree | 6c6a6fead0f2449d832dfaeb786f305db7413f4d /0001-xfs-More-robust-inode-extent-count-validation.patch | |
parent | 539a3afe2de9751709aaa31c8475b5128d923ee4 (diff) | |
download | kernel-1a9181ff17aa8e6c755e8e9f49b6a3b840c8ce8f.tar.gz kernel-1a9181ff17aa8e6c755e8e9f49b6a3b840c8ce8f.tar.xz kernel-1a9181ff17aa8e6c755e8e9f49b6a3b840c8ce8f.zip |
Fix CVE-2018-13053 CVE-2018-12896 CVE-2018-13093 CVE-2018-13094 CVE-2018-13095
Diffstat (limited to '0001-xfs-More-robust-inode-extent-count-validation.patch')
-rw-r--r-- | 0001-xfs-More-robust-inode-extent-count-validation.patch | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/0001-xfs-More-robust-inode-extent-count-validation.patch b/0001-xfs-More-robust-inode-extent-count-validation.patch new file mode 100644 index 000000000..767eb6fac --- /dev/null +++ b/0001-xfs-More-robust-inode-extent-count-validation.patch @@ -0,0 +1,141 @@ +From 23fcb3340d033d9f081e21e6c12c2db7eaa541d3 Mon Sep 17 00:00:00 2001 +From: Dave Chinner <dchinner@redhat.com> +Date: Thu, 21 Jun 2018 23:25:57 -0700 +Subject: [PATCH] xfs: More robust inode extent count validation + +When the inode is in extent format, it can't have more extents that +fit in the inode fork. We don't currenty check this, and so this +corruption goes unnoticed by the inode verifiers. This can lead to +crashes operating on invalid in-memory structures. + +Attempts to access such a inode will now error out in the verifier +rather than allowing modification operations to proceed. + +Reported-by: Wen Xu <wen.xu@gatech.edu> +Signed-off-by: Dave Chinner <dchinner@redhat.com> +Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> +[darrick: fix a typedef, add some braces and breaks to shut up compiler warnings] +Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> +--- + fs/xfs/libxfs/xfs_format.h | 3 ++ + fs/xfs/libxfs/xfs_inode_buf.c | 76 ++++++++++++++++++++++------------- + 2 files changed, 50 insertions(+), 29 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h +index 1c5a8aaf2bfc..7b4a43deb83e 100644 +--- a/fs/xfs/libxfs/xfs_format.h ++++ b/fs/xfs/libxfs/xfs_format.h +@@ -962,6 +962,9 @@ typedef enum xfs_dinode_fmt { + XFS_DFORK_DSIZE(dip, mp) : \ + XFS_DFORK_ASIZE(dip, mp)) + ++#define XFS_DFORK_MAXEXT(dip, mp, w) \ ++ (XFS_DFORK_SIZE(dip, mp, w) / sizeof(struct xfs_bmbt_rec)) ++ + /* + * Return pointers to the data or attribute forks. + */ +diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c +index d38d724534c4..33dc34655ac3 100644 +--- a/fs/xfs/libxfs/xfs_inode_buf.c ++++ b/fs/xfs/libxfs/xfs_inode_buf.c +@@ -374,6 +374,47 @@ xfs_log_dinode_to_disk( + } + } + ++static xfs_failaddr_t ++xfs_dinode_verify_fork( ++ struct xfs_dinode *dip, ++ struct xfs_mount *mp, ++ int whichfork) ++{ ++ uint32_t di_nextents = XFS_DFORK_NEXTENTS(dip, whichfork); ++ ++ switch (XFS_DFORK_FORMAT(dip, whichfork)) { ++ case XFS_DINODE_FMT_LOCAL: ++ /* ++ * no local regular files yet ++ */ ++ if (whichfork == XFS_DATA_FORK) { ++ if (S_ISREG(be16_to_cpu(dip->di_mode))) ++ return __this_address; ++ if (be64_to_cpu(dip->di_size) > ++ XFS_DFORK_SIZE(dip, mp, whichfork)) ++ return __this_address; ++ } ++ if (di_nextents) ++ return __this_address; ++ break; ++ case XFS_DINODE_FMT_EXTENTS: ++ if (di_nextents > XFS_DFORK_MAXEXT(dip, mp, whichfork)) ++ return __this_address; ++ break; ++ case XFS_DINODE_FMT_BTREE: ++ if (whichfork == XFS_ATTR_FORK) { ++ if (di_nextents > MAXAEXTNUM) ++ return __this_address; ++ } else if (di_nextents > MAXEXTNUM) { ++ return __this_address; ++ } ++ break; ++ default: ++ return __this_address; ++ } ++ return NULL; ++} ++ + xfs_failaddr_t + xfs_dinode_verify( + struct xfs_mount *mp, +@@ -441,24 +482,9 @@ xfs_dinode_verify( + case S_IFREG: + case S_IFLNK: + case S_IFDIR: +- switch (dip->di_format) { +- case XFS_DINODE_FMT_LOCAL: +- /* +- * no local regular files yet +- */ +- if (S_ISREG(mode)) +- return __this_address; +- if (di_size > XFS_DFORK_DSIZE(dip, mp)) +- return __this_address; +- if (dip->di_nextents) +- return __this_address; +- /* fall through */ +- case XFS_DINODE_FMT_EXTENTS: +- case XFS_DINODE_FMT_BTREE: +- break; +- default: +- return __this_address; +- } ++ fa = xfs_dinode_verify_fork(dip, mp, XFS_DATA_FORK); ++ if (fa) ++ return fa; + break; + case 0: + /* Uninitialized inode ok. */ +@@ -468,17 +494,9 @@ xfs_dinode_verify( + } + + if (XFS_DFORK_Q(dip)) { +- switch (dip->di_aformat) { +- case XFS_DINODE_FMT_LOCAL: +- if (dip->di_anextents) +- return __this_address; +- /* fall through */ +- case XFS_DINODE_FMT_EXTENTS: +- case XFS_DINODE_FMT_BTREE: +- break; +- default: +- return __this_address; +- } ++ fa = xfs_dinode_verify_fork(dip, mp, XFS_ATTR_FORK); ++ if (fa) ++ return fa; + } else { + /* + * If there is no fork offset, this may be a freshly-made inode +-- +2.17.1 + |