* [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink()
@ 2024-08-02 16:36 Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 2/4] squashfs: Fix integer overflow in sqfs_inode_size() Richard Weinberger
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Richard Weinberger @ 2024-08-02 16:36 UTC (permalink / raw)
To: u-boot
Cc: upstream+uboot, trini, miquel.raynal, thomas.petazzoni,
jmcosta944, Richard Weinberger
A carefully crafted squashfs filesystem can exhibit an inode size of 0xffffffff,
as a consequence malloc() will do a zero allocation.
Later in the function the inode size is again used for copying data.
So an attacker can overwrite memory.
Avoid the overflow by using the __builtin_add_overflow() helper.
Signed-off-by: Richard Weinberger <richard@nod.at>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
fs/squashfs/sqfs.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 1430e671a5..16a07c0622 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -422,8 +422,10 @@ static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
char *resolved, *target;
u32 sz;
- sz = get_unaligned_le32(&sym->symlink_size);
- target = malloc(sz + 1);
+ if (__builtin_add_overflow(get_unaligned_le32(&sym->symlink_size), 1, &sz))
+ return NULL;
+
+ target = malloc(sz);
if (!target)
return NULL;
@@ -431,9 +433,9 @@ static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
* There is no trailling null byte in the symlink's target path, so a
* copy is made and a '\0' is added at its end.
*/
- target[sz] = '\0';
+ target[sz - 1] = '\0';
/* Get target name (relative path) */
- strncpy(target, sym->symlink, sz);
+ strncpy(target, sym->symlink, sz - 1);
/* Relative -> absolute path conversion */
resolved = sqfs_get_abs_path(base_path, target);
--
2.35.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/4] squashfs: Fix integer overflow in sqfs_inode_size()
2024-08-02 16:36 [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Richard Weinberger
@ 2024-08-02 16:36 ` Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 3/4] squashfs: Check sqfs_find_inode() return value Richard Weinberger
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Richard Weinberger @ 2024-08-02 16:36 UTC (permalink / raw)
To: u-boot
Cc: upstream+uboot, trini, miquel.raynal, thomas.petazzoni,
jmcosta944, Richard Weinberger
A carefully crafted squashfs filesystem can exhibit an extremly large
inode size and overflow the calculation in sqfs_inode_size().
As a consequence, the squashfs driver will read from wrong locations.
Fix by using __builtin_add_overflow() to detect the overflow.
Signed-off-by: Richard Weinberger <richard@nod.at>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
fs/squashfs/sqfs_inode.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c
index d25cfb53e7..bb3ccd37e3 100644
--- a/fs/squashfs/sqfs_inode.c
+++ b/fs/squashfs/sqfs_inode.c
@@ -78,11 +78,16 @@ int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size)
case SQFS_SYMLINK_TYPE:
case SQFS_LSYMLINK_TYPE: {
+ int size;
+
struct squashfs_symlink_inode *symlink =
(struct squashfs_symlink_inode *)inode;
- return sizeof(*symlink) +
- get_unaligned_le32(&symlink->symlink_size);
+ if (__builtin_add_overflow(sizeof(*symlink),
+ get_unaligned_le32(&symlink->symlink_size), &size))
+ return -EINVAL;
+
+ return size;
}
case SQFS_BLKDEV_TYPE:
--
2.35.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/4] squashfs: Check sqfs_find_inode() return value
2024-08-02 16:36 [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 2/4] squashfs: Fix integer overflow in sqfs_inode_size() Richard Weinberger
@ 2024-08-02 16:36 ` Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving Richard Weinberger
2024-08-16 3:47 ` [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Tom Rini
3 siblings, 0 replies; 6+ messages in thread
From: Richard Weinberger @ 2024-08-02 16:36 UTC (permalink / raw)
To: u-boot
Cc: upstream+uboot, trini, miquel.raynal, thomas.petazzoni,
jmcosta944, Richard Weinberger
The function can fail and return NULL.
Signed-off-by: Richard Weinberger <richard@nod.at>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
fs/squashfs/sqfs.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 16a07c0622..fa99d514f2 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -474,6 +474,8 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
/* Start by root inode */
table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
sblk->inodes, sblk->block_size);
+ if (!table)
+ return -EINVAL;
dir = (struct squashfs_dir_inode *)table;
ldir = (struct squashfs_ldir_inode *)table;
@@ -529,6 +531,8 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
/* Get reference to inode in the inode table */
table = sqfs_find_inode(dirs->inode_table, new_inode_number,
sblk->inodes, sblk->block_size);
+ if (!table)
+ return -EINVAL;
dir = (struct squashfs_dir_inode *)table;
/* Check for symbolic link and inode type sanity */
@@ -1025,6 +1029,8 @@ int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
sblk->block_size);
+ if (!ipos)
+ return -SQFS_STOP_READDIR;
base = (struct squashfs_base_inode *)ipos;
@@ -1381,6 +1387,10 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
sblk->block_size);
+ if (!ipos) {
+ ret = -EINVAL;
+ goto out;
+ }
base = (struct squashfs_base_inode *)ipos;
switch (get_unaligned_le16(&base->inode_type)) {
@@ -1629,6 +1639,13 @@ int sqfs_size(const char *filename, loff_t *size)
i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
sblk->block_size);
+
+ if (!ipos) {
+ *size = 0;
+ ret = -EINVAL;
+ goto free_strings;
+ }
+
free(dirs->entry);
dirs->entry = NULL;
--
2.35.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving
2024-08-02 16:36 [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 2/4] squashfs: Fix integer overflow in sqfs_inode_size() Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 3/4] squashfs: Check sqfs_find_inode() return value Richard Weinberger
@ 2024-08-02 16:36 ` Richard Weinberger
2024-08-12 7:51 ` Miquel Raynal
2024-08-16 3:47 ` [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Tom Rini
3 siblings, 1 reply; 6+ messages in thread
From: Richard Weinberger @ 2024-08-02 16:36 UTC (permalink / raw)
To: u-boot
Cc: upstream+uboot, trini, miquel.raynal, thomas.petazzoni,
jmcosta944, Richard Weinberger
The squashfs driver blindly follows symlinks, and calls sqfs_size()
recursively. So an attacker can create a crafted filesystem and with
a deep enough nesting level a stack overflow can be achieved.
Fix by limiting the nesting level to 8.
Signed-off-by: Richard Weinberger <richard@nod.at>
---
Changes since v1:
- Add MAX_SYMLINK_NEST define
---
fs/squashfs/sqfs.c | 76 +++++++++++++++++++++++++++++++++++++---------
1 file changed, 61 insertions(+), 15 deletions(-)
diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index fa99d514f2..af7ff80a7b 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -24,7 +24,12 @@
#include "sqfs_filesystem.h"
#include "sqfs_utils.h"
+#define MAX_SYMLINK_NEST 8
+
static struct squashfs_ctxt ctxt;
+static int symlinknest;
+
+static int sqfs_readdir_nest(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp);
static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf)
{
@@ -510,7 +515,7 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
goto out;
}
- while (!sqfs_readdir(dirsp, &dent)) {
+ while (!sqfs_readdir_nest(dirsp, &dent)) {
ret = strcmp(dent->name, token_list[j]);
if (!ret)
break;
@@ -537,6 +542,11 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
/* Check for symbolic link and inode type sanity */
if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
+ if (++symlinknest == MAX_SYMLINK_NEST) {
+ ret = -ELOOP;
+ goto out;
+ }
+
sym = (struct squashfs_symlink_inode *)table;
/* Get first j + 1 tokens */
path = sqfs_concat_tokens(token_list, j + 1);
@@ -884,7 +894,7 @@ out:
return metablks_count;
}
-int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
+static int sqfs_opendir_nest(const char *filename, struct fs_dir_stream **dirsp)
{
unsigned char *inode_table = NULL, *dir_table = NULL;
int j, token_count = 0, ret = 0, metablks_count;
@@ -979,7 +989,19 @@ out:
return ret;
}
+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
+{
+ symlinknest = 0;
+ return sqfs_opendir_nest(filename, dirsp);
+}
+
int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
+{
+ symlinknest = 0;
+ return sqfs_readdir_nest(fs_dirs, dentp);
+}
+
+static int sqfs_readdir_nest(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
{
struct squashfs_super_block *sblk = ctxt.sblk;
struct squashfs_dir_stream *dirs;
@@ -1325,8 +1347,8 @@ static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
return datablk_count;
}
-int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
- loff_t *actread)
+static int sqfs_read_nest(const char *filename, void *buf, loff_t offset,
+ loff_t len, loff_t *actread)
{
char *dir = NULL, *fragment_block, *datablock = NULL;
char *fragment = NULL, *file = NULL, *resolved, *data;
@@ -1356,11 +1378,11 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
}
/*
- * sqfs_opendir will uncompress inode and directory tables, and will
+ * sqfs_opendir_nest will uncompress inode and directory tables, and will
* return a pointer to the directory that contains the requested file.
*/
sqfs_split_path(&file, &dir, filename);
- ret = sqfs_opendir(dir, &dirsp);
+ ret = sqfs_opendir_nest(dir, &dirsp);
if (ret) {
goto out;
}
@@ -1368,7 +1390,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
dirs = (struct squashfs_dir_stream *)dirsp;
/* For now, only regular files are able to be loaded */
- while (!sqfs_readdir(dirsp, &dent)) {
+ while (!sqfs_readdir_nest(dirsp, &dent)) {
ret = strcmp(dent->name, file);
if (!ret)
break;
@@ -1421,9 +1443,14 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
break;
case SQFS_SYMLINK_TYPE:
case SQFS_LSYMLINK_TYPE:
+ if (++symlinknest == MAX_SYMLINK_NEST) {
+ ret = -ELOOP;
+ goto out;
+ }
+
symlink = (struct squashfs_symlink_inode *)ipos;
resolved = sqfs_resolve_symlink(symlink, filename);
- ret = sqfs_read(resolved, buf, offset, len, actread);
+ ret = sqfs_read_nest(resolved, buf, offset, len, actread);
free(resolved);
goto out;
case SQFS_BLKDEV_TYPE:
@@ -1594,7 +1621,14 @@ out:
return ret;
}
-int sqfs_size(const char *filename, loff_t *size)
+int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ symlinknest = 0;
+ return sqfs_read_nest(filename, buf, offset, len, actread);
+}
+
+static int sqfs_size_nest(const char *filename, loff_t *size)
{
struct squashfs_super_block *sblk = ctxt.sblk;
struct squashfs_symlink_inode *symlink;
@@ -1610,10 +1644,10 @@ int sqfs_size(const char *filename, loff_t *size)
sqfs_split_path(&file, &dir, filename);
/*
- * sqfs_opendir will uncompress inode and directory tables, and will
+ * sqfs_opendir_nest will uncompress inode and directory tables, and will
* return a pointer to the directory that contains the requested file.
*/
- ret = sqfs_opendir(dir, &dirsp);
+ ret = sqfs_opendir_nest(dir, &dirsp);
if (ret) {
ret = -EINVAL;
goto free_strings;
@@ -1621,7 +1655,7 @@ int sqfs_size(const char *filename, loff_t *size)
dirs = (struct squashfs_dir_stream *)dirsp;
- while (!sqfs_readdir(dirsp, &dent)) {
+ while (!sqfs_readdir_nest(dirsp, &dent)) {
ret = strcmp(dent->name, file);
if (!ret)
break;
@@ -1661,6 +1695,11 @@ int sqfs_size(const char *filename, loff_t *size)
break;
case SQFS_SYMLINK_TYPE:
case SQFS_LSYMLINK_TYPE:
+ if (++symlinknest == MAX_SYMLINK_NEST) {
+ *size = 0;
+ return -ELOOP;
+ }
+
symlink = (struct squashfs_symlink_inode *)ipos;
resolved = sqfs_resolve_symlink(symlink, filename);
ret = sqfs_size(resolved, size);
@@ -1700,10 +1739,11 @@ int sqfs_exists(const char *filename)
sqfs_split_path(&file, &dir, filename);
/*
- * sqfs_opendir will uncompress inode and directory tables, and will
+ * sqfs_opendir_nest will uncompress inode and directory tables, and will
* return a pointer to the directory that contains the requested file.
*/
- ret = sqfs_opendir(dir, &dirsp);
+ symlinknest = 0;
+ ret = sqfs_opendir_nest(dir, &dirsp);
if (ret) {
ret = -EINVAL;
goto free_strings;
@@ -1711,7 +1751,7 @@ int sqfs_exists(const char *filename)
dirs = (struct squashfs_dir_stream *)dirsp;
- while (!sqfs_readdir(dirsp, &dent)) {
+ while (!sqfs_readdir_nest(dirsp, &dent)) {
ret = strcmp(dent->name, file);
if (!ret)
break;
@@ -1728,6 +1768,12 @@ free_strings:
return ret == 0;
}
+int sqfs_size(const char *filename, loff_t *size)
+{
+ symlinknest = 0;
+ return sqfs_size_nest(filename, size);
+}
+
void sqfs_close(void)
{
sqfs_decompressor_cleanup(&ctxt);
--
2.35.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving
2024-08-02 16:36 ` [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving Richard Weinberger
@ 2024-08-12 7:51 ` Miquel Raynal
0 siblings, 0 replies; 6+ messages in thread
From: Miquel Raynal @ 2024-08-12 7:51 UTC (permalink / raw)
To: Richard Weinberger
Cc: u-boot, upstream+uboot, trini, thomas.petazzoni, jmcosta944
Hi Richard,
richard@nod.at wrote on Fri, 2 Aug 2024 18:36:47 +0200:
> The squashfs driver blindly follows symlinks, and calls sqfs_size()
> recursively. So an attacker can create a crafted filesystem and with
> a deep enough nesting level a stack overflow can be achieved.
>
> Fix by limiting the nesting level to 8.
>
> Signed-off-by: Richard Weinberger <richard@nod.at>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Thanks,
Miquèl
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink()
2024-08-02 16:36 [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Richard Weinberger
` (2 preceding siblings ...)
2024-08-02 16:36 ` [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving Richard Weinberger
@ 2024-08-16 3:47 ` Tom Rini
3 siblings, 0 replies; 6+ messages in thread
From: Tom Rini @ 2024-08-16 3:47 UTC (permalink / raw)
To: u-boot, Richard Weinberger
Cc: upstream+uboot, miquel.raynal, thomas.petazzoni, jmcosta944
On Fri, 02 Aug 2024 18:36:44 +0200, Richard Weinberger wrote:
> A carefully crafted squashfs filesystem can exhibit an inode size of 0xffffffff,
> as a consequence malloc() will do a zero allocation.
> Later in the function the inode size is again used for copying data.
> So an attacker can overwrite memory.
> Avoid the overflow by using the __builtin_add_overflow() helper.
>
>
> [...]
Applied to u-boot/next, thanks!
--
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-08-16 3:47 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-02 16:36 [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 2/4] squashfs: Fix integer overflow in sqfs_inode_size() Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 3/4] squashfs: Check sqfs_find_inode() return value Richard Weinberger
2024-08-02 16:36 ` [PATCH v2 4/4] squashfs: Fix stack overflow while symlink resolving Richard Weinberger
2024-08-12 7:51 ` Miquel Raynal
2024-08-16 3:47 ` [PATCH v2 1/4] squashfs: Fix integer overflow in sqfs_resolve_symlink() Tom Rini
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox