[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