Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Squashfs: fix uninit-value in squashfs_get_parent

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>

authored by

Phillip Lougher and committed by
Andrew Morton
74058c0a f322a97a

+8 -1
+7
fs/squashfs/inode.c
··· 169 169 squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); 170 170 squashfs_i(inode)->block_list_start = block; 171 171 squashfs_i(inode)->offset = offset; 172 + squashfs_i(inode)->parent = 0; 172 173 inode->i_data.a_ops = &squashfs_aops; 173 174 174 175 TRACE("File inode %x:%x, start_block %llx, block_list_start " ··· 217 216 squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block); 218 217 squashfs_i(inode)->block_list_start = block; 219 218 squashfs_i(inode)->offset = offset; 219 + squashfs_i(inode)->parent = 0; 220 220 inode->i_data.a_ops = &squashfs_aops; 221 221 222 222 TRACE("File inode %x:%x, start_block %llx, block_list_start " ··· 298 296 inode->i_mode |= S_IFLNK; 299 297 squashfs_i(inode)->start = block; 300 298 squashfs_i(inode)->offset = offset; 299 + squashfs_i(inode)->parent = 0; 301 300 302 301 if (type == SQUASHFS_LSYMLINK_TYPE) { 303 302 __le32 xattr; ··· 336 333 set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); 337 334 rdev = le32_to_cpu(sqsh_ino->rdev); 338 335 init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); 336 + squashfs_i(inode)->parent = 0; 339 337 340 338 TRACE("Device inode %x:%x, rdev %x\n", 341 339 SQUASHFS_INODE_BLK(ino), offset, rdev); ··· 361 357 set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); 362 358 rdev = le32_to_cpu(sqsh_ino->rdev); 363 359 init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); 360 + squashfs_i(inode)->parent = 0; 364 361 365 362 TRACE("Device inode %x:%x, rdev %x\n", 366 363 SQUASHFS_INODE_BLK(ino), offset, rdev); ··· 382 377 inode->i_mode |= S_IFSOCK; 383 378 set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); 384 379 init_special_inode(inode, inode->i_mode, 0); 380 + squashfs_i(inode)->parent = 0; 385 381 break; 386 382 } 387 383 case SQUASHFS_LFIFO_TYPE: ··· 402 396 inode->i_op = &squashfs_inode_ops; 403 397 set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); 404 398 init_special_inode(inode, inode->i_mode, 0); 399 + squashfs_i(inode)->parent = 0; 405 400 break; 406 401 } 407 402 default:
+1 -1
fs/squashfs/squashfs_fs_i.h
··· 16 16 u64 xattr; 17 17 unsigned int xattr_size; 18 18 int xattr_count; 19 + int parent; 19 20 union { 20 21 struct { 21 22 u64 fragment_block; ··· 28 27 u64 dir_idx_start; 29 28 int dir_idx_offset; 30 29 int dir_idx_cnt; 31 - int parent; 32 30 }; 33 31 }; 34 32 struct inode vfs_inode;