]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Squashfs: add additional inode sanity checking
authorPhillip Lougher <phillip@squashfs.org.uk>
Tue, 23 Sep 2025 22:06:51 +0000 (23:06 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Sun, 28 Sep 2025 18:36:14 +0000 (11:36 -0700)
Patch series "Squashfs: performance improvement and a sanity check".

This patchset adds an additional sanity check when reading regular file
inodes, and adds support for SEEK_DATA/SEEK_HOLE lseek() whence values.

This patch (of 2):

Add an additional sanity check when reading regular file inodes.

A regular file if the file size is an exact multiple of the filesystem
block size cannot have a fragment.  This is because by definition a
fragment block stores tailends which are not a whole block in size.

Link: https://lkml.kernel.org/r/20250923220652.568416-1-phillip@squashfs.org.uk
Link: https://lkml.kernel.org/r/20250923220652.568416-2-phillip@squashfs.org.uk
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/squashfs/inode.c

index bb0529b09adc5da0b3e13f889b064d81f177effe..8f425dbf90524b47a1fda6f1768ca399fb40c45c 100644 (file)
@@ -144,8 +144,17 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                if (err < 0)
                        goto failed_read;
 
+               inode->i_size = le32_to_cpu(sqsh_ino->file_size);
                frag = le32_to_cpu(sqsh_ino->fragment);
                if (frag != SQUASHFS_INVALID_FRAG) {
+                       /*
+                        * the file cannot have a fragment (tailend) and have a
+                        * file size a multiple of the block size
+                        */
+                       if ((inode->i_size & (msblk->block_size - 1)) == 0) {
+                               err = -EINVAL;
+                               goto failed_read;
+                       }
                        frag_offset = le32_to_cpu(sqsh_ino->offset);
                        frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
                        if (frag_size < 0) {
@@ -159,7 +168,6 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                }
 
                set_nlink(inode, 1);
-               inode->i_size = le32_to_cpu(sqsh_ino->file_size);
                inode->i_fop = &generic_ro_fops;
                inode->i_mode |= S_IFREG;
                inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
@@ -188,8 +196,17 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                if (err < 0)
                        goto failed_read;
 
+               inode->i_size = le64_to_cpu(sqsh_ino->file_size);
                frag = le32_to_cpu(sqsh_ino->fragment);
                if (frag != SQUASHFS_INVALID_FRAG) {
+                       /*
+                        * the file cannot have a fragment (tailend) and have a
+                        * file size a multiple of the block size
+                        */
+                       if ((inode->i_size & (msblk->block_size - 1)) == 0) {
+                               err = -EINVAL;
+                               goto failed_read;
+                       }
                        frag_offset = le32_to_cpu(sqsh_ino->offset);
                        frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
                        if (frag_size < 0) {
@@ -204,7 +221,6 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 
                xattr_id = le32_to_cpu(sqsh_ino->xattr);
                set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
-               inode->i_size = le64_to_cpu(sqsh_ino->file_size);
                inode->i_op = &squashfs_inode_ops;
                inode->i_fop = &generic_ro_fops;
                inode->i_mode |= S_IFREG;