Hi,
Following on from the "Squashfs: sanity check information from disk"
patch from Dan Carpenter, I have added a couple more sanity checks,
and fixed a couple of existing sanity checks (including the patch from
Dan Carpenter).
These sanity checks mainly exist to trap maliciously corrupted
filesystems either through using a deliberately modified mksquashfs,
or where the user has deliberately chosen to generate uncompressed
metadata and then corrupted it.
Normally metadata in Squashfs filesystems is compressed, which means
corruption (either accidental or malicious) is detected when
trying to decompress the metadata. So corrupted data does not normally
get as far as the code paths in question here.
Phillip Lougher (5):
Squashfs: fix corruption check in get_dir_index_using_name()
Squashfs: fix corruption checks in squashfs_lookup()
Squashfs: fix corruption checks in squashfs_readdir()
Squashfs: add corruption check in get_dir_index_using_offset()
Squashfs: add corruption check for type in squashfs_readdir()
fs/squashfs/dir.c | 17 +++++++++++++----
fs/squashfs/namei.c | 7 +++----
fs/squashfs/squashfs_fs.h | 5 ++++-
3 files changed, 20 insertions(+), 9 deletions(-)
--
1.8.3.2
We read the type field from disk. This value should be sanity
checked for correctness to avoid an out of bounds access when
reading the squashfs_filetype_table array.
Signed-off-by: Phillip Lougher <[email protected]>
---
fs/squashfs/dir.c | 7 +++++--
fs/squashfs/squashfs_fs.h | 5 ++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c
index bd7155b..d8c2d74 100644
--- a/fs/squashfs/dir.c
+++ b/fs/squashfs/dir.c
@@ -112,8 +112,8 @@ static int squashfs_readdir(struct file *file, struct dir_context *ctx)
struct inode *inode = file_inode(file);
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
u64 block = squashfs_i(inode)->start + msblk->directory_table;
- int offset = squashfs_i(inode)->offset, length, type, err;
- unsigned int inode_number, dir_count, size;
+ int offset = squashfs_i(inode)->offset, length, err;
+ unsigned int inode_number, dir_count, size, type;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
@@ -206,6 +206,9 @@ static int squashfs_readdir(struct file *file, struct dir_context *ctx)
((short) le16_to_cpu(dire->inode_number));
type = le16_to_cpu(dire->type);
+ if (type > SQUASHFS_MAX_DIR_TYPE)
+ goto failed_read;
+
if (!dir_emit(ctx, dire->name, size,
inode_number,
squashfs_filetype_table[type]))
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
index 9e2349d..4b2beda 100644
--- a/fs/squashfs/squashfs_fs.h
+++ b/fs/squashfs/squashfs_fs.h
@@ -87,7 +87,7 @@
#define SQUASHFS_COMP_OPTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_COMP_OPT)
-/* Max number of types and file types */
+/* Inode types including extended types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_REG_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
@@ -103,6 +103,9 @@
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
+/* Max type value stored in directory entry */
+#define SQUASHFS_MAX_DIR_TYPE 7
+
/* Xattr types */
#define SQUASHFS_XATTR_USER 0
#define SQUASHFS_XATTR_TRUSTED 1
--
1.8.3.2
The dir_count and size fields when read from disk are sanity
checked for correctness. However, the sanity checks only check the
values are not greater than expected. As dir_count and size were
incorrectly defined as signed ints, this can lead to corrupted values
appearing as negative which are not trapped.
Signed-off-by: Phillip Lougher <[email protected]>
---
fs/squashfs/dir.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c
index f7f527b..1192084 100644
--- a/fs/squashfs/dir.c
+++ b/fs/squashfs/dir.c
@@ -105,9 +105,8 @@ static int squashfs_readdir(struct file *file, struct dir_context *ctx)
struct inode *inode = file_inode(file);
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
u64 block = squashfs_i(inode)->start + msblk->directory_table;
- int offset = squashfs_i(inode)->offset, length, dir_count, size,
- type, err;
- unsigned int inode_number;
+ int offset = squashfs_i(inode)->offset, length, type, err;
+ unsigned int inode_number, dir_count, size;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
--
1.8.3.2
We read the size (of the name) field from disk. This value should
be sanity checked for correctness to avoid blindly reading
huge amounts of unnecessary data from disk on corruption.
Note, here we're not actually reading the name into a buffer, but
skipping it, and so corruption doesn't cause buffer overflow, merely
lots of unnecessary amounts of data to be read.
Signed-off-by: Phillip Lougher <[email protected]>
---
fs/squashfs/dir.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c
index 1192084..bd7155b 100644
--- a/fs/squashfs/dir.c
+++ b/fs/squashfs/dir.c
@@ -54,6 +54,7 @@ static int get_dir_index_using_offset(struct super_block *sb,
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int err, i, index, length = 0;
+ unsigned int size;
struct squashfs_dir_index dir_index;
TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
@@ -81,8 +82,14 @@ static int get_dir_index_using_offset(struct super_block *sb,
*/
break;
+ size = le32_to_cpu(dir_index.size) + 1;
+
+ /* size should never be larger than SQUASHFS_NAME_LEN */
+ if (size > SQUASHFS_NAME_LEN)
+ break;
+
err = squashfs_read_metadata(sb, NULL, &index_start,
- &index_offset, le32_to_cpu(dir_index.size) + 1);
+ &index_offset, size);
if (err < 0)
break;
--
1.8.3.2
Patch "Squashfs: sanity check information from disk" from
Dan Carpenter adds a missing check for corruption in the
"size" field while reading the directory index from disk.
It, however, sets err to -EINVAL, this value is not used later, and
so setting it is completely redundant. So remove it.
Errors in reading the index are deliberately non-fatal. If we
get an error in reading the index we just return the part of the
index we have managed to read - the index isn't essential,
just quicker.
Signed-off-by: Phillip Lougher <[email protected]>
---
fs/squashfs/namei.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
index f866d42..342a5aa 100644
--- a/fs/squashfs/namei.c
+++ b/fs/squashfs/namei.c
@@ -104,10 +104,8 @@ static int get_dir_index_using_name(struct super_block *sb,
size = le32_to_cpu(index->size) + 1;
- if (size > SQUASHFS_NAME_LEN) {
- err = -EINVAL;
+ if (size > SQUASHFS_NAME_LEN)
break;
- }
err = squashfs_read_metadata(sb, index->name, &index_start,
&index_offset, size);
--
1.8.3.2
The dir_count and size fields when read from disk are sanity
checked for correctness. However, the sanity checks only check the
values are not greater than expected. As dir_count and size were
incorrectly defined as signed ints, this can lead to corrupted values
appearing as negative which are not trapped.
Signed-off-by: Phillip Lougher <[email protected]>
---
fs/squashfs/namei.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
index 342a5aa..67cad77 100644
--- a/fs/squashfs/namei.c
+++ b/fs/squashfs/namei.c
@@ -147,7 +147,8 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
struct squashfs_dir_entry *dire;
u64 block = squashfs_i(dir)->start + msblk->directory_table;
int offset = squashfs_i(dir)->offset;
- int err, length, dir_count, size;
+ int err, length;
+ unsigned int dir_count, size;
TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
--
1.8.3.2