Skip to content

Commit f37aa4c

Browse files
ploughertorvalds
authored andcommitted
squashfs: add more sanity checks in id lookup
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/[email protected] Signed-off-by: Phillip Lougher <[email protected]> Reported-by: [email protected] Reported-by: [email protected] Reported-by: [email protected] Reported-by: [email protected] Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent e812cbb commit f37aa4c

File tree

4 files changed

+45
-12
lines changed

4 files changed

+45
-12
lines changed

fs/squashfs/id.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,15 @@ int squashfs_get_id(struct super_block *sb, unsigned int index,
3535
struct squashfs_sb_info *msblk = sb->s_fs_info;
3636
int block = SQUASHFS_ID_BLOCK(index);
3737
int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
38-
u64 start_block = le64_to_cpu(msblk->id_table[block]);
38+
u64 start_block;
3939
__le32 disk_id;
4040
int err;
4141

42+
if (index >= msblk->ids)
43+
return -EINVAL;
44+
45+
start_block = le64_to_cpu(msblk->id_table[block]);
46+
4247
err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
4348
sizeof(disk_id));
4449
if (err < 0)
@@ -56,7 +61,10 @@ __le64 *squashfs_read_id_index_table(struct super_block *sb,
5661
u64 id_table_start, u64 next_table, unsigned short no_ids)
5762
{
5863
unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
64+
unsigned int indexes = SQUASHFS_ID_BLOCKS(no_ids);
65+
int n;
5966
__le64 *table;
67+
u64 start, end;
6068

6169
TRACE("In read_id_index_table, length %d\n", length);
6270

@@ -67,20 +75,36 @@ __le64 *squashfs_read_id_index_table(struct super_block *sb,
6775
return ERR_PTR(-EINVAL);
6876

6977
/*
70-
* length bytes should not extend into the next table - this check
71-
* also traps instances where id_table_start is incorrectly larger
72-
* than the next table start
78+
* The computed size of the index table (length bytes) should exactly
79+
* match the table start and end points
7380
*/
74-
if (id_table_start + length > next_table)
81+
if (length != (next_table - id_table_start))
7582
return ERR_PTR(-EINVAL);
7683

7784
table = squashfs_read_table(sb, id_table_start, length);
85+
if (IS_ERR(table))
86+
return table;
7887

7988
/*
80-
* table[0] points to the first id lookup table metadata block, this
81-
* should be less than id_table_start
89+
* table[0], table[1], ... table[indexes - 1] store the locations
90+
* of the compressed id blocks. Each entry should be less than
91+
* the next (i.e. table[0] < table[1]), and the difference between them
92+
* should be SQUASHFS_METADATA_SIZE or less. table[indexes - 1]
93+
* should be less than id_table_start, and again the difference
94+
* should be SQUASHFS_METADATA_SIZE or less
8295
*/
83-
if (!IS_ERR(table) && le64_to_cpu(table[0]) >= id_table_start) {
96+
for (n = 0; n < (indexes - 1); n++) {
97+
start = le64_to_cpu(table[n]);
98+
end = le64_to_cpu(table[n + 1]);
99+
100+
if (start >= end || (end - start) > SQUASHFS_METADATA_SIZE) {
101+
kfree(table);
102+
return ERR_PTR(-EINVAL);
103+
}
104+
}
105+
106+
start = le64_to_cpu(table[indexes - 1]);
107+
if (start >= id_table_start || (id_table_start - start) > SQUASHFS_METADATA_SIZE) {
84108
kfree(table);
85109
return ERR_PTR(-EINVAL);
86110
}

fs/squashfs/squashfs_fs_sb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ struct squashfs_sb_info {
6464
unsigned int inodes;
6565
unsigned int fragments;
6666
int xattr_ids;
67+
unsigned int ids;
6768
};
6869
#endif

fs/squashfs/super.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
166166
msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
167167
msblk->inodes = le32_to_cpu(sblk->inodes);
168168
msblk->fragments = le32_to_cpu(sblk->fragments);
169+
msblk->ids = le16_to_cpu(sblk->no_ids);
169170
flags = le16_to_cpu(sblk->flags);
170171

171172
TRACE("Found valid superblock on %pg\n", sb->s_bdev);
@@ -177,7 +178,7 @@ static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
177178
TRACE("Block size %d\n", msblk->block_size);
178179
TRACE("Number of inodes %d\n", msblk->inodes);
179180
TRACE("Number of fragments %d\n", msblk->fragments);
180-
TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
181+
TRACE("Number of ids %d\n", msblk->ids);
181182
TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
182183
TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
183184
TRACE("sblk->fragment_table_start %llx\n",
@@ -236,8 +237,7 @@ static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
236237
allocate_id_index_table:
237238
/* Allocate and read id index table */
238239
msblk->id_table = squashfs_read_id_index_table(sb,
239-
le64_to_cpu(sblk->id_table_start), next_table,
240-
le16_to_cpu(sblk->no_ids));
240+
le64_to_cpu(sblk->id_table_start), next_table, msblk->ids);
241241
if (IS_ERR(msblk->id_table)) {
242242
errorf(fc, "unable to read id index table");
243243
err = PTR_ERR(msblk->id_table);

fs/squashfs/xattr.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *,
1717
static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb,
1818
u64 start, u64 *xattr_table_start, int *xattr_ids)
1919
{
20+
struct squashfs_xattr_id_table *id_table;
21+
22+
id_table = squashfs_read_table(sb, start, sizeof(*id_table));
23+
if (IS_ERR(id_table))
24+
return (__le64 *) id_table;
25+
26+
*xattr_table_start = le64_to_cpu(id_table->xattr_table_start);
27+
kfree(id_table);
28+
2029
ERROR("Xattrs in filesystem, these will be ignored\n");
21-
*xattr_table_start = start;
2230
return ERR_PTR(-ENOTSUPP);
2331
}
2432

0 commit comments

Comments
 (0)