]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 11 Feb 2021 14:35:15 +0000 (15:35 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 11 Feb 2021 14:35:15 +0000 (15:35 +0100)
added patches:
squashfs-add-more-sanity-checks-in-id-lookup.patch
squashfs-add-more-sanity-checks-in-inode-lookup.patch
squashfs-add-more-sanity-checks-in-xattr-id-lookup.patch

queue-4.9/series
queue-4.9/squashfs-add-more-sanity-checks-in-id-lookup.patch [new file with mode: 0644]
queue-4.9/squashfs-add-more-sanity-checks-in-inode-lookup.patch [new file with mode: 0644]
queue-4.9/squashfs-add-more-sanity-checks-in-xattr-id-lookup.patch [new file with mode: 0644]

index f8872ad21d8fc60ea4c14d616192713714d5620f..a8ec603d22a5121eaf44400076975c2f5c1cbb19 100644 (file)
@@ -13,3 +13,6 @@ memcg-fix-a-crash-in-wb_workfn-when-a-device-disappe.patch
 futex-ensure-the-correct-return-value-from-futex_lock_pi.patch
 futex-change-locking-rules.patch
 futex-cure-exit-race.patch
+squashfs-add-more-sanity-checks-in-id-lookup.patch
+squashfs-add-more-sanity-checks-in-inode-lookup.patch
+squashfs-add-more-sanity-checks-in-xattr-id-lookup.patch
diff --git a/queue-4.9/squashfs-add-more-sanity-checks-in-id-lookup.patch b/queue-4.9/squashfs-add-more-sanity-checks-in-id-lookup.patch
new file mode 100644 (file)
index 0000000..87b46eb
--- /dev/null
@@ -0,0 +1,177 @@
+From f37aa4c7366e23f91b81d00bafd6a7ab54e4a381 Mon Sep 17 00:00:00 2001
+From: Phillip Lougher <phillip@squashfs.org.uk>
+Date: Tue, 9 Feb 2021 13:41:53 -0800
+Subject: squashfs: add more sanity checks in id lookup
+
+From: Phillip Lougher <phillip@squashfs.org.uk>
+
+commit f37aa4c7366e23f91b81d00bafd6a7ab54e4a381 upstream.
+
+Sysbot has reported a number of "slab-out-of-bounds reads" and
+"use-after-free read" errors which has been identified as being caused
+by a corrupted index value read from the inode.  This could be because
+the metadata block is uncompressed, or because the "compression" bit has
+been corrupted (turning a compressed block into an uncompressed block).
+
+This patch adds additional sanity checks to detect this, and the
+following corruption.
+
+1. It checks against corruption of the ids count.  This can either
+   lead to a larger table to be read, or a smaller than expected
+   table to be read.
+
+   In the case of a too large ids count, this would often have been
+   trapped by the existing sanity checks, but this patch introduces
+   a more exact check, which can identify too small values.
+
+2. It checks the contents of the index table for corruption.
+
+Link: https://lkml.kernel.org/r/20210204130249.4495-3-phillip@squashfs.org.uk
+Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
+Reported-by: syzbot+b06d57ba83f604522af2@syzkaller.appspotmail.com
+Reported-by: syzbot+c021ba012da41ee9807c@syzkaller.appspotmail.com
+Reported-by: syzbot+5024636e8b5fd19f0f19@syzkaller.appspotmail.com
+Reported-by: syzbot+bcbc661df46657d0fa4f@syzkaller.appspotmail.com
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/squashfs/id.c             |   40 ++++++++++++++++++++++++++++++++--------
+ fs/squashfs/squashfs_fs_sb.h |    1 +
+ fs/squashfs/super.c          |    6 +++---
+ fs/squashfs/xattr.h          |   10 +++++++++-
+ 4 files changed, 45 insertions(+), 12 deletions(-)
+
+--- a/fs/squashfs/id.c
++++ b/fs/squashfs/id.c
+@@ -48,10 +48,15 @@ int squashfs_get_id(struct super_block *
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int block = SQUASHFS_ID_BLOCK(index);
+       int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
+-      u64 start_block = le64_to_cpu(msblk->id_table[block]);
++      u64 start_block;
+       __le32 disk_id;
+       int err;
++      if (index >= msblk->ids)
++              return -EINVAL;
++
++      start_block = le64_to_cpu(msblk->id_table[block]);
++
+       err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
+                                                       sizeof(disk_id));
+       if (err < 0)
+@@ -69,7 +74,10 @@ __le64 *squashfs_read_id_index_table(str
+               u64 id_table_start, u64 next_table, unsigned short no_ids)
+ {
+       unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
++      unsigned int indexes = SQUASHFS_ID_BLOCKS(no_ids);
++      int n;
+       __le64 *table;
++      u64 start, end;
+       TRACE("In read_id_index_table, length %d\n", length);
+@@ -80,20 +88,36 @@ __le64 *squashfs_read_id_index_table(str
+               return ERR_PTR(-EINVAL);
+       /*
+-       * length bytes should not extend into the next table - this check
+-       * also traps instances where id_table_start is incorrectly larger
+-       * than the next table start
++       * The computed size of the index table (length bytes) should exactly
++       * match the table start and end points
+        */
+-      if (id_table_start + length > next_table)
++      if (length != (next_table - id_table_start))
+               return ERR_PTR(-EINVAL);
+       table = squashfs_read_table(sb, id_table_start, length);
++      if (IS_ERR(table))
++              return table;
+       /*
+-       * table[0] points to the first id lookup table metadata block, this
+-       * should be less than id_table_start
++       * table[0], table[1], ... table[indexes - 1] store the locations
++       * of the compressed id blocks.   Each entry should be less than
++       * the next (i.e. table[0] < table[1]), and the difference between them
++       * should be SQUASHFS_METADATA_SIZE or less.  table[indexes - 1]
++       * should be less than id_table_start, and again the difference
++       * should be SQUASHFS_METADATA_SIZE or less
+        */
+-      if (!IS_ERR(table) && le64_to_cpu(table[0]) >= id_table_start) {
++      for (n = 0; n < (indexes - 1); n++) {
++              start = le64_to_cpu(table[n]);
++              end = le64_to_cpu(table[n + 1]);
++
++              if (start >= end || (end - start) > SQUASHFS_METADATA_SIZE) {
++                      kfree(table);
++                      return ERR_PTR(-EINVAL);
++              }
++      }
++
++      start = le64_to_cpu(table[indexes - 1]);
++      if (start >= id_table_start || (id_table_start - start) > SQUASHFS_METADATA_SIZE) {
+               kfree(table);
+               return ERR_PTR(-EINVAL);
+       }
+--- a/fs/squashfs/squashfs_fs_sb.h
++++ b/fs/squashfs/squashfs_fs_sb.h
+@@ -77,5 +77,6 @@ struct squashfs_sb_info {
+       unsigned int                            inodes;
+       unsigned int                            fragments;
+       int                                     xattr_ids;
++      unsigned int                            ids;
+ };
+ #endif
+--- a/fs/squashfs/super.c
++++ b/fs/squashfs/super.c
+@@ -176,6 +176,7 @@ static int squashfs_fill_super(struct su
+       msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
+       msblk->inodes = le32_to_cpu(sblk->inodes);
+       msblk->fragments = le32_to_cpu(sblk->fragments);
++      msblk->ids = le16_to_cpu(sblk->no_ids);
+       flags = le16_to_cpu(sblk->flags);
+       TRACE("Found valid superblock on %pg\n", sb->s_bdev);
+@@ -187,7 +188,7 @@ static int squashfs_fill_super(struct su
+       TRACE("Block size %d\n", msblk->block_size);
+       TRACE("Number of inodes %d\n", msblk->inodes);
+       TRACE("Number of fragments %d\n", msblk->fragments);
+-      TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
++      TRACE("Number of ids %d\n", msblk->ids);
+       TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
+       TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
+       TRACE("sblk->fragment_table_start %llx\n",
+@@ -244,8 +245,7 @@ static int squashfs_fill_super(struct su
+ allocate_id_index_table:
+       /* Allocate and read id index table */
+       msblk->id_table = squashfs_read_id_index_table(sb,
+-              le64_to_cpu(sblk->id_table_start), next_table,
+-              le16_to_cpu(sblk->no_ids));
++              le64_to_cpu(sblk->id_table_start), next_table, msblk->ids);
+       if (IS_ERR(msblk->id_table)) {
+               ERROR("unable to read id index table\n");
+               err = PTR_ERR(msblk->id_table);
+--- a/fs/squashfs/xattr.h
++++ b/fs/squashfs/xattr.h
+@@ -30,8 +30,16 @@ extern int squashfs_xattr_lookup(struct
+ static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb,
+               u64 start, u64 *xattr_table_start, int *xattr_ids)
+ {
++      struct squashfs_xattr_id_table *id_table;
++
++      id_table = squashfs_read_table(sb, start, sizeof(*id_table));
++      if (IS_ERR(id_table))
++              return (__le64 *) id_table;
++
++      *xattr_table_start = le64_to_cpu(id_table->xattr_table_start);
++      kfree(id_table);
++
+       ERROR("Xattrs in filesystem, these will be ignored\n");
+-      *xattr_table_start = start;
+       return ERR_PTR(-ENOTSUPP);
+ }
diff --git a/queue-4.9/squashfs-add-more-sanity-checks-in-inode-lookup.patch b/queue-4.9/squashfs-add-more-sanity-checks-in-inode-lookup.patch
new file mode 100644 (file)
index 0000000..e22e851
--- /dev/null
@@ -0,0 +1,119 @@
+From eabac19e40c095543def79cb6ffeb3a8588aaff4 Mon Sep 17 00:00:00 2001
+From: Phillip Lougher <phillip@squashfs.org.uk>
+Date: Tue, 9 Feb 2021 13:41:56 -0800
+Subject: squashfs: add more sanity checks in inode lookup
+
+From: Phillip Lougher <phillip@squashfs.org.uk>
+
+commit eabac19e40c095543def79cb6ffeb3a8588aaff4 upstream.
+
+Sysbot has reported an "slab-out-of-bounds read" error which has been
+identified as being caused by a corrupted "ino_num" value read from the
+inode.  This could be because the metadata block is uncompressed, or
+because the "compression" bit has been corrupted (turning a compressed
+block into an uncompressed block).
+
+This patch adds additional sanity checks to detect this, and the
+following corruption.
+
+1. It checks against corruption of the inodes count.  This can either
+   lead to a larger table to be read, or a smaller than expected
+   table to be read.
+
+   In the case of a too large inodes count, this would often have been
+   trapped by the existing sanity checks, but this patch introduces
+   a more exact check, which can identify too small values.
+
+2. It checks the contents of the index table for corruption.
+
+[phillip@squashfs.org.uk: fix checkpatch issue]
+  Link: https://lkml.kernel.org/r/527909353.754618.1612769948607@webmail.123-reg.co.uk
+
+Link: https://lkml.kernel.org/r/20210204130249.4495-4-phillip@squashfs.org.uk
+Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
+Reported-by: syzbot+04419e3ff19d2970ea28@syzkaller.appspotmail.com
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/squashfs/export.c |   41 +++++++++++++++++++++++++++++++++--------
+ 1 file changed, 33 insertions(+), 8 deletions(-)
+
+--- a/fs/squashfs/export.c
++++ b/fs/squashfs/export.c
+@@ -54,12 +54,17 @@ static long long squashfs_inode_lookup(s
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
+       int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
+-      u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]);
++      u64 start;
+       __le64 ino;
+       int err;
+       TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
++      if (ino_num == 0 || (ino_num - 1) >= msblk->inodes)
++              return -EINVAL;
++
++      start = le64_to_cpu(msblk->inode_lookup_table[blk]);
++
+       err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
+       if (err < 0)
+               return err;
+@@ -124,7 +129,10 @@ __le64 *squashfs_read_inode_lookup_table
+               u64 lookup_table_start, u64 next_table, unsigned int inodes)
+ {
+       unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
++      unsigned int indexes = SQUASHFS_LOOKUP_BLOCKS(inodes);
++      int n;
+       __le64 *table;
++      u64 start, end;
+       TRACE("In read_inode_lookup_table, length %d\n", length);
+@@ -134,20 +142,37 @@ __le64 *squashfs_read_inode_lookup_table
+       if (inodes == 0)
+               return ERR_PTR(-EINVAL);
+-      /* length bytes should not extend into the next table - this check
+-       * also traps instances where lookup_table_start is incorrectly larger
+-       * than the next table start
++      /*
++       * The computed size of the lookup table (length bytes) should exactly
++       * match the table start and end points
+        */
+-      if (lookup_table_start + length > next_table)
++      if (length != (next_table - lookup_table_start))
+               return ERR_PTR(-EINVAL);
+       table = squashfs_read_table(sb, lookup_table_start, length);
++      if (IS_ERR(table))
++              return table;
+       /*
+-       * table[0] points to the first inode lookup table metadata block,
+-       * this should be less than lookup_table_start
++       * table0], table[1], ... table[indexes - 1] store the locations
++       * of the compressed inode lookup blocks.  Each entry should be
++       * less than the next (i.e. table[0] < table[1]), and the difference
++       * between them should be SQUASHFS_METADATA_SIZE or less.
++       * table[indexes - 1] should  be less than lookup_table_start, and
++       * again the difference should be SQUASHFS_METADATA_SIZE or less
+        */
+-      if (!IS_ERR(table) && le64_to_cpu(table[0]) >= lookup_table_start) {
++      for (n = 0; n < (indexes - 1); n++) {
++              start = le64_to_cpu(table[n]);
++              end = le64_to_cpu(table[n + 1]);
++
++              if (start >= end || (end - start) > SQUASHFS_METADATA_SIZE) {
++                      kfree(table);
++                      return ERR_PTR(-EINVAL);
++              }
++      }
++
++      start = le64_to_cpu(table[indexes - 1]);
++      if (start >= lookup_table_start || (lookup_table_start - start) > SQUASHFS_METADATA_SIZE) {
+               kfree(table);
+               return ERR_PTR(-EINVAL);
+       }
diff --git a/queue-4.9/squashfs-add-more-sanity-checks-in-xattr-id-lookup.patch b/queue-4.9/squashfs-add-more-sanity-checks-in-xattr-id-lookup.patch
new file mode 100644 (file)
index 0000000..5eaa499
--- /dev/null
@@ -0,0 +1,139 @@
+From 506220d2ba21791314af569211ffd8870b8208fa Mon Sep 17 00:00:00 2001
+From: Phillip Lougher <phillip@squashfs.org.uk>
+Date: Tue, 9 Feb 2021 13:42:00 -0800
+Subject: squashfs: add more sanity checks in xattr id lookup
+
+From: Phillip Lougher <phillip@squashfs.org.uk>
+
+commit 506220d2ba21791314af569211ffd8870b8208fa upstream.
+
+Sysbot has reported a warning where a kmalloc() attempt exceeds the
+maximum limit.  This has been identified as corruption of the xattr_ids
+count when reading the xattr id lookup table.
+
+This patch adds a number of additional sanity checks to detect this
+corruption and others.
+
+1. It checks for a corrupted xattr index read from the inode.  This could
+   be because the metadata block is uncompressed, or because the
+   "compression" bit has been corrupted (turning a compressed block
+   into an uncompressed block).  This would cause an out of bounds read.
+
+2. It checks against corruption of the xattr_ids count.  This can either
+   lead to the above kmalloc failure, or a smaller than expected
+   table to be read.
+
+3. It checks the contents of the index table for corruption.
+
+[phillip@squashfs.org.uk: fix checkpatch issue]
+  Link: https://lkml.kernel.org/r/270245655.754655.1612770082682@webmail.123-reg.co.uk
+
+Link: https://lkml.kernel.org/r/20210204130249.4495-5-phillip@squashfs.org.uk
+Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
+Reported-by: syzbot+2ccea6339d368360800d@syzkaller.appspotmail.com
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/squashfs/xattr_id.c |   66 ++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 57 insertions(+), 9 deletions(-)
+
+--- a/fs/squashfs/xattr_id.c
++++ b/fs/squashfs/xattr_id.c
+@@ -44,10 +44,15 @@ int squashfs_xattr_lookup(struct super_b
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int block = SQUASHFS_XATTR_BLOCK(index);
+       int offset = SQUASHFS_XATTR_BLOCK_OFFSET(index);
+-      u64 start_block = le64_to_cpu(msblk->xattr_id_table[block]);
++      u64 start_block;
+       struct squashfs_xattr_id id;
+       int err;
++      if (index >= msblk->xattr_ids)
++              return -EINVAL;
++
++      start_block = le64_to_cpu(msblk->xattr_id_table[block]);
++
+       err = squashfs_read_metadata(sb, &id, &start_block, &offset,
+                                                       sizeof(id));
+       if (err < 0)
+@@ -63,13 +68,17 @@ int squashfs_xattr_lookup(struct super_b
+ /*
+  * Read uncompressed xattr id lookup table indexes from disk into memory
+  */
+-__le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 start,
++__le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 table_start,
+               u64 *xattr_table_start, int *xattr_ids)
+ {
+-      unsigned int len;
++      struct squashfs_sb_info *msblk = sb->s_fs_info;
++      unsigned int len, indexes;
+       struct squashfs_xattr_id_table *id_table;
++      __le64 *table;
++      u64 start, end;
++      int n;
+-      id_table = squashfs_read_table(sb, start, sizeof(*id_table));
++      id_table = squashfs_read_table(sb, table_start, sizeof(*id_table));
+       if (IS_ERR(id_table))
+               return (__le64 *) id_table;
+@@ -83,13 +92,52 @@ __le64 *squashfs_read_xattr_id_table(str
+       if (*xattr_ids == 0)
+               return ERR_PTR(-EINVAL);
+-      /* xattr_table should be less than start */
+-      if (*xattr_table_start >= start)
++      len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids);
++      indexes = SQUASHFS_XATTR_BLOCKS(*xattr_ids);
++
++      /*
++       * The computed size of the index table (len bytes) should exactly
++       * match the table start and end points
++       */
++      start = table_start + sizeof(*id_table);
++      end = msblk->bytes_used;
++
++      if (len != (end - start))
+               return ERR_PTR(-EINVAL);
+-      len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids);
++      table = squashfs_read_table(sb, start, len);
++      if (IS_ERR(table))
++              return table;
++
++      /* table[0], table[1], ... table[indexes - 1] store the locations
++       * of the compressed xattr id blocks.  Each entry should be less than
++       * the next (i.e. table[0] < table[1]), and the difference between them
++       * should be SQUASHFS_METADATA_SIZE or less.  table[indexes - 1]
++       * should be less than table_start, and again the difference
++       * shouls be SQUASHFS_METADATA_SIZE or less.
++       *
++       * Finally xattr_table_start should be less than table[0].
++       */
++      for (n = 0; n < (indexes - 1); n++) {
++              start = le64_to_cpu(table[n]);
++              end = le64_to_cpu(table[n + 1]);
++
++              if (start >= end || (end - start) > SQUASHFS_METADATA_SIZE) {
++                      kfree(table);
++                      return ERR_PTR(-EINVAL);
++              }
++      }
++
++      start = le64_to_cpu(table[indexes - 1]);
++      if (start >= table_start || (table_start - start) > SQUASHFS_METADATA_SIZE) {
++              kfree(table);
++              return ERR_PTR(-EINVAL);
++      }
+-      TRACE("In read_xattr_index_table, length %d\n", len);
++      if (*xattr_table_start >= le64_to_cpu(table[0])) {
++              kfree(table);
++              return ERR_PTR(-EINVAL);
++      }
+-      return squashfs_read_table(sb, start + sizeof(*id_table), len);
++      return table;
+ }