summaryrefslogtreecommitdiffstats
path: root/0001-xfs-More-robust-inode-extent-count-validation.patch
diff options
context:
space:
mode:
authorJustin M. Forbes <jforbes@fedoraproject.org>2018-07-05 09:31:36 -0500
committerJustin M. Forbes <jforbes@fedoraproject.org>2018-07-05 09:31:36 -0500
commit1a9181ff17aa8e6c755e8e9f49b6a3b840c8ce8f (patch)
tree6c6a6fead0f2449d832dfaeb786f305db7413f4d /0001-xfs-More-robust-inode-extent-count-validation.patch
parent539a3afe2de9751709aaa31c8475b5128d923ee4 (diff)
downloadkernel-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.patch141
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
+