Skip to content

Commit eabac19

Browse files
ploughertorvalds
authored andcommitted
squashfs: add more sanity checks in inode lookup
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. [[email protected]: fix checkpatch issue] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Phillip Lougher <[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 f37aa4c commit eabac19

File tree

1 file changed

+33
-8
lines changed

1 file changed

+33
-8
lines changed

fs/squashfs/export.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,17 @@ static long long squashfs_inode_lookup(struct super_block *sb, int ino_num)
4141
struct squashfs_sb_info *msblk = sb->s_fs_info;
4242
int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
4343
int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
44-
u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]);
44+
u64 start;
4545
__le64 ino;
4646
int err;
4747

4848
TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
4949

50+
if (ino_num == 0 || (ino_num - 1) >= msblk->inodes)
51+
return -EINVAL;
52+
53+
start = le64_to_cpu(msblk->inode_lookup_table[blk]);
54+
5055
err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
5156
if (err < 0)
5257
return err;
@@ -111,7 +116,10 @@ __le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
111116
u64 lookup_table_start, u64 next_table, unsigned int inodes)
112117
{
113118
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
119+
unsigned int indexes = SQUASHFS_LOOKUP_BLOCKS(inodes);
120+
int n;
114121
__le64 *table;
122+
u64 start, end;
115123

116124
TRACE("In read_inode_lookup_table, length %d\n", length);
117125

@@ -121,20 +129,37 @@ __le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
121129
if (inodes == 0)
122130
return ERR_PTR(-EINVAL);
123131

124-
/* length bytes should not extend into the next table - this check
125-
* also traps instances where lookup_table_start is incorrectly larger
126-
* than the next table start
132+
/*
133+
* The computed size of the lookup table (length bytes) should exactly
134+
* match the table start and end points
127135
*/
128-
if (lookup_table_start + length > next_table)
136+
if (length != (next_table - lookup_table_start))
129137
return ERR_PTR(-EINVAL);
130138

131139
table = squashfs_read_table(sb, lookup_table_start, length);
140+
if (IS_ERR(table))
141+
return table;
132142

133143
/*
134-
* table[0] points to the first inode lookup table metadata block,
135-
* this should be less than lookup_table_start
144+
* table0], table[1], ... table[indexes - 1] store the locations
145+
* of the compressed inode lookup blocks. Each entry should be
146+
* less than the next (i.e. table[0] < table[1]), and the difference
147+
* between them should be SQUASHFS_METADATA_SIZE or less.
148+
* table[indexes - 1] should be less than lookup_table_start, and
149+
* again the difference should be SQUASHFS_METADATA_SIZE or less
136150
*/
137-
if (!IS_ERR(table) && le64_to_cpu(table[0]) >= lookup_table_start) {
151+
for (n = 0; n < (indexes - 1); n++) {
152+
start = le64_to_cpu(table[n]);
153+
end = le64_to_cpu(table[n + 1]);
154+
155+
if (start >= end || (end - start) > SQUASHFS_METADATA_SIZE) {
156+
kfree(table);
157+
return ERR_PTR(-EINVAL);
158+
}
159+
}
160+
161+
start = le64_to_cpu(table[indexes - 1]);
162+
if (start >= lookup_table_start || (lookup_table_start - start) > SQUASHFS_METADATA_SIZE) {
138163
kfree(table);
139164
return ERR_PTR(-EINVAL);
140165
}

0 commit comments

Comments
 (0)