Re: [PATCH] squashfs: fix leaked page locks when page actor allocation fails

From: Phillip Lougher

Date: Fri Nov 06 2026 - 16:53:23 EST


On 27/09/2026 07:05, Nguyen Ngoc Thang wrote:

Hi Phillip,

syzbot reports a hang in evict() during umount of a squashfs mount
(INFO: task hung in evict (2)):

https://syzkaller.appspot.com/bug?extid=65b1e2d8f2d618a93e96

Root cause: squashfs_readpage_block() grabs and locks the sibling pages
of the block with grab_cache_page_nowait(), and only afterwards allocates
the page actor. If squashfs_page_actor_init_special() fails, the code
does "goto out", which only kfree()s the page array. The locked, referenced
sibling pages are never unlocked or put. A later truncate_inode_pages_range()
from evict() blocks on the leaked folio lock forever.

The ordering was introduced by f268eedddf35 ("squashfs: extend "page actor"
to handle missing pages"); before it the actor was allocated first.

Fix: use "goto mark_errored", which already unlocks and puts every page
except the target page (owned by the caller, which unlocks it via
folio_end_read()). res is still -ENOMEM at that point.

Testing: QEMU (KVM, KASAN, lockdep, IMA tcb policy) running the syzbot C
reproducer, with fail-nth swept over 1..400 in openat(). The failslab
hit lands in squashfs_page_actor_init_special() from
squashfs_readpage_block() via ima_calc_file_hash().
- before: hung task in evict()/truncate_inode_pages_range() after ~12s,
same trace as the syzbot report
- after: 170s of runs, 174 injected failures at that site, no hang

The patch is below / attached.

----- patch -----
From f686b39f997dc20222f455c5ac6421f6a5d6c8d8 Mon Sep 17 00:00:00 2001
From: Nguyen Ngoc Thang <ngocthang2710.1999@xxxxxxxxx>
Date: Sun, 27 Sep 2026 12:40:12 +0700
Subject: [PATCH] squashfs: fix leaked page locks when page actor allocation
fails

squashfs_readpage_block() grabs and locks the sibling pages of the
target block before it allocates the page actor. If that allocation
fails it jumps to "out", which only frees the page array, so the locked
pages are never unlocked or released.

A later truncate_inode_pages_range() on the inode, e.g. from evict() at
umount, then waits forever on the leaked folio lock and the task hangs.

Go through mark_errored instead, which unlocks and puts every page
except the target, which the caller handles.

Fixes: f268eedddf35 ("squashfs: extend "page actor" to handle missing pages")
Reported-by: syzbot+65b1e2d8f2d618a93e96@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=65b1e2d8f2d618a93e96
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999@xxxxxxxxx>
---
fs/squashfs/file_direct.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index 2c3e809d6891..177357443543 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -70,7 +70,7 @@ int squashfs_readpage_block(struct folio *folio, u64 block, int bsize,
actor = squashfs_page_actor_init_special(msblk, page, pages, expected,
start_index << PAGE_SHIFT);
if (actor == NULL)
- goto out;
+ goto mark_errored;
/* Decompress directly into the page cache buffers */
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);

Sorry for the delay, I'm currently snowed under with AI reports and bugs;

But, it looks good.

Reviewed-by: Phillip Lougher <phillip@xxxxxxxxxxxxxxx <https://squashfs.org.uk>>