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
--- /dev/null
+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);
+ }
+
--- /dev/null
+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);
+ }
--- /dev/null
+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;
+ }