diff options
| author | Shahriyar Jalayeri <[email protected]> | 2026-07-28 08:55:39 +0200 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-08-27 15:01:02 -0600 |
| commit | 561ae28cb56a082cfa90c1c421c4955bc215470b (patch) | |
| tree | a066a3d7673ecfffc564896495c730ad136b563f | |
| parent | fdfe2ec48d5c1c2ed03073d73edd3fdd3fe1ffa1 (diff) | |
fs/squashfs: fix integer overflow in directory table allocation
sqfs_read_directory_table() allocates the directory table with
malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE). metablks_count is an
int and SQFS_METADATA_BLOCK_SIZE is 8192, so the multiply is evaluated in
int and wraps for metablks_count >= 2^19. metablks_count comes from the
attacker-controlled superblock (sqfs_count_metablks() grows it by one per
2-byte metadata header), so a crafted image under-allocates the buffer
while the fill loop still writes metablks_count metadata blocks into it,
a heap out-of-bounds write. It is reached by listing or reading the image
(sqfsls / sqfsload). The position list allocation on the next line has the
same unchecked-multiply shape.
Size both allocations with __builtin_mul_overflow() and reject the image
on overflow, as the disk-read buffers earlier in the same function already
do. Set the error return when either allocation fails so the caller does
not proceed with a NULL directory table.
Fixes: c51006130370 ("fs/squashfs: new filesystem")
Signed-off-by: Shahriyar Jalayeri <[email protected]>
Reviewed-by: Richard Genoud <[email protected]>
| -rw-r--r-- | fs/squashfs/sqfs.c | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c index 0768fc4a7b2..3aadcdd36ec 100644 --- a/fs/squashfs/sqfs.c +++ b/fs/squashfs/sqfs.c @@ -852,13 +852,28 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) if (metablks_count < 1) goto out; - *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE); - if (!*dir_table) + if (__builtin_mul_overflow(metablks_count, SQFS_METADATA_BLOCK_SIZE, + &buf_size)) { + metablks_count = -1; + goto out; + } + + *dir_table = malloc(buf_size); + if (!*dir_table) { + metablks_count = -1; + goto out; + } + + if (__builtin_mul_overflow(metablks_count, sizeof(u32), &buf_size)) { + metablks_count = -1; goto out; + } - *pos_list = malloc(metablks_count * sizeof(u32)); - if (!*pos_list) + *pos_list = malloc(buf_size); + if (!*pos_list) { + metablks_count = -1; goto out; + } ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset, metablks_count); |
