]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Squashfs: fix uninit-value in squashfs_get_parent
authorPhillip Lougher <phillip@squashfs.org.uk>
Thu, 18 Sep 2025 23:33:08 +0000 (00:33 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 15 Oct 2025 09:56:39 +0000 (11:56 +0200)
commit 74058c0a9fc8b2b4d5f4a0ef7ee2cfa66a9e49cf upstream.

Syzkaller reports a "KMSAN: uninit-value in squashfs_get_parent" bug.

This is caused by open_by_handle_at() being called with a file handle
containing an invalid parent inode number.  In particular the inode number
is that of a symbolic link, rather than a directory.

Squashfs_get_parent() gets called with that symbolic link inode, and
accesses the parent member field.

unsigned int parent_ino = squashfs_i(inode)->parent;

Because non-directory inodes in Squashfs do not have a parent value, this
is uninitialised, and this causes an uninitialised value access.

The fix is to initialise parent with the invalid inode 0, which will cause
an EINVAL error to be returned.

Regular inodes used to share the parent field with the block_list_start
field.  This is removed in this commit to enable the parent field to
contain the invalid inode number 0.

Link: https://lkml.kernel.org/r/20250918233308.293861-1-phillip@squashfs.org.uk
Fixes: 122601408d20 ("Squashfs: export operations")
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
Reported-by: syzbot+157bdef5cf596ad0da2c@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/68cc2431.050a0220.139b6.0001.GAE@google.com/
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/squashfs/inode.c
fs/squashfs/squashfs_fs_i.h

index 95a9ff9e23997569d4f53810cc977764bfa46e24..c381d08c30c207e9a5b6a07dbf83dae7a1a9fffe 100644 (file)
@@ -165,6 +165,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
                squashfs_i(inode)->block_list_start = block;
                squashfs_i(inode)->offset = offset;
+               squashfs_i(inode)->parent = 0;
                inode->i_data.a_ops = &squashfs_aops;
 
                TRACE("File inode %x:%x, start_block %llx, block_list_start "
@@ -212,6 +213,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
                squashfs_i(inode)->block_list_start = block;
                squashfs_i(inode)->offset = offset;
+               squashfs_i(inode)->parent = 0;
                inode->i_data.a_ops = &squashfs_aops;
 
                TRACE("File inode %x:%x, start_block %llx, block_list_start "
@@ -292,6 +294,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                inode->i_mode |= S_IFLNK;
                squashfs_i(inode)->start = block;
                squashfs_i(inode)->offset = offset;
+               squashfs_i(inode)->parent = 0;
 
                if (type == SQUASHFS_LSYMLINK_TYPE) {
                        __le32 xattr;
@@ -329,6 +332,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
                rdev = le32_to_cpu(sqsh_ino->rdev);
                init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
+               squashfs_i(inode)->parent = 0;
 
                TRACE("Device inode %x:%x, rdev %x\n",
                                SQUASHFS_INODE_BLK(ino), offset, rdev);
@@ -353,6 +357,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
                rdev = le32_to_cpu(sqsh_ino->rdev);
                init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
+               squashfs_i(inode)->parent = 0;
 
                TRACE("Device inode %x:%x, rdev %x\n",
                                SQUASHFS_INODE_BLK(ino), offset, rdev);
@@ -373,6 +378,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                        inode->i_mode |= S_IFSOCK;
                set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
                init_special_inode(inode, inode->i_mode, 0);
+               squashfs_i(inode)->parent = 0;
                break;
        }
        case SQUASHFS_LFIFO_TYPE:
@@ -392,6 +398,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                inode->i_op = &squashfs_inode_ops;
                set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
                init_special_inode(inode, inode->i_mode, 0);
+               squashfs_i(inode)->parent = 0;
                break;
        }
        default:
index 2c82d6f2a4561b4b61a7e30285b89375bdd1cbe8..8e497ac07b9a836f9e91e22ca54de006abf7558b 100644 (file)
@@ -16,6 +16,7 @@ struct squashfs_inode_info {
        u64             xattr;
        unsigned int    xattr_size;
        int             xattr_count;
+       int             parent;
        union {
                struct {
                        u64             fragment_block;
@@ -27,7 +28,6 @@ struct squashfs_inode_info {
                        u64             dir_idx_start;
                        int             dir_idx_offset;
                        int             dir_idx_cnt;
-                       int             parent;
                };
        };
        struct inode    vfs_inode;