[PATCH] jffs2: fix BUG_ON in jffs2_start_garbage_collect_thread on reconfigure

Dmitriy Chumachenko Dmitry.Chumachenko at cyberprotect.ru
Mon Apr 20 02:52:20 PDT 2026


During fuzz testing, the following issue was discovered.

kernel BUG at fs/jffs2/background.c:40!
invalid opcode: 0000 [#1] PREEMPT SMP KASAN PTI
CPU: 0 PID: 5060 Comm: syz-executor108 Not tainted 6.8.0-syzkaller-08951-gfe46a7dd189e #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/27/2024
RIP: 0010:jffs2_start_garbage_collect_thread+0x1f5/0x200 fs/jffs2/background.c:40

Call Trace:
 <TASK>
 jffs2_do_remount_fs+0x15b/0x1d0 fs/jffs2/fs.c:415
 reconfigure_super+0x445/0x880 fs/super.c:1071
 vfs_cmd_reconfigure fs/fsopen.c:267 [inline]
 vfs_fsconfig_locked fs/fsopen.c:296 [inline]
 __do_sys_fsconfig fs/fsopen.c:476 [inline]
 __se_sys_fsconfig+0xab5/0xec0 fs/fsopen.c:349
 do_syscall_64+0xfb/0x240
 entry_SYSCALL_64_after_hwframe+0x6d/0x75

When reconfiguring a mount without explicitly setting mount flags,
fc->sb_flags and fc->sb_flags_mask are both zero. jffs2_do_remount_fs()
skips stopping the GC thread because the superblock is read-only, but
starts a new one because fc->sb_flags lacks SB_RDONLY. The superblock
remains read-only, so on the next reconfigure the same path triggers
BUG_ON(c->gc_task) since the previous thread is still running.

Fix this by computing the effective new superblock flags using the same
formula as reconfigure_super() so the start decision reflects the
actual future state of the superblock.

Found by Linux Verification Center (linuxtesting.org) with Syzkaller.

Fixes: ec10a24f10c8 ("vfs: Convert jffs2 to use the new mount API")
Signed-off-by: Dmitriy Chumachenko <Dmitry.Chumachenko at cyberprotect.ru>
---
 fs/jffs2/fs.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index d175cccb7c55..abfe6eead880 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -396,6 +396,8 @@ void jffs2_dirty_inode(struct inode *inode, int flags)
 int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+	unsigned long s_flags_new = (sb->s_flags & ~fc->sb_flags_mask) |
+				    (fc->sb_flags & fc->sb_flags_mask);
 
 	if (c->flags & JFFS2_SB_FLAG_RO && !sb_rdonly(sb))
 		return -EROFS;
@@ -411,7 +413,7 @@ int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc)
 		mutex_unlock(&c->alloc_sem);
 	}
 
-	if (!(fc->sb_flags & SB_RDONLY))
+	if (!(s_flags_new & SB_RDONLY))
 		jffs2_start_garbage_collect_thread(c);
 
 	fc->sb_flags |= SB_NOATIME;
-- 
2.49.0




More information about the linux-mtd mailing list