[BUG] kernel BUG in jffs2_sum_write_sumnode
From: Jaeyoung Chung
Date: Mon Aug 24 2026 - 11:52:04 EST
Hello,
We found a "kernel BUG in jffs2_sum_write_sumnode" on Linux v7.2.
The issue was found by our own race fuzzer. We have not analyzed the root cause,
so we do not have a proposed fix to offer.
To reproduce the race reliably, we applied the delay patch below to the
kernel and ran the C reproducer as root inside an x86_64 QEMU guest. The
crash log we observed, the delay patch and the reproducer are all included
below.
The following kernel config options are required to reproduce the issue:
CONFIG_JFFS2_FS=y
CONFIG_JFFS2_SUMMARY=y
CONFIG_MTD=y
CONFIG_MTD_MTDRAM=y
CONFIG_MTD_BLOCK=y
CONFIG_MTDRAM_TOTAL_SIZE=128
CONFIG_MTDRAM_ERASE_SIZE=4
CONFIG_KASAN=y
We hope this report is useful. Please let us know if any further
information would help.
Reported-by: Eulgyu Kim <eulgyukim@xxxxxxxxx>
Reported-by: Jaeyoung Chung <jjy600901@xxxxxxxxx>
Kernel delay patch:
==================================================================
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 1b833bbffcf5..7517c66c86e9 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -18,6 +18,8 @@
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
#include <linux/stat.h>
#include "nodelist.h"
#include "compr.h"
@@ -603,6 +605,9 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
ref_offset(raw));
alloclen = rawlen = ref_totlen(c, c->gcblock, raw);
+ if (c->summary && c->nextblock && !c->summary->sum_num) {
+ mdelay(10);
+ }
/* Ask for a small amount of space (or the totlen if smaller) because we
don't want to force wastage of the end of a block if splitting would
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 3fb9f9807b66..7785bde6b795 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -14,6 +14,8 @@
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/string.h>
#include <linux/sched/signal.h>
#include <linux/string_choices.h>
#include "nodelist.h"
@@ -84,6 +86,10 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
minsize = PAD(minsize);
jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
+ if (c->summary && c->nextblock && !c->summary->sum_num &&
+ strncmp(current->comm, "syzrepro1", 9) == 0) {
+ mdelay(80);
+ }
mutex_lock(&c->alloc_sem);
jffs2_dbg(1, "%s(): alloc sem got\n", __func__);
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 06e494797724..b7ac51a0cc95 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -17,6 +17,7 @@
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
+#include <linux/delay.h>
#include <linux/compiler.h>
#include "nodelist.h"
#include "summary.h"
@@ -207,6 +208,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
}
/* update collected summary information for the current nextblock */
jffs2_sum_move_collected(c, s);
+ if (!c->summary->sum_num || !c->summary->sum_list_head) {
+ mdelay(10);
+ }
jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
__func__, jeb->offset);
c->nextblock = jeb;
==================================================================
C reproducer:
==================================================================
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#define SYSCHK(x) ({ long __r = (long)(x); if (__r == -1L) { perror(#x); exit(1); } __r; })
#define SECT 4096u
#define NBLK 32u
#define FLASH (SECT * NBLK)
#define GATE_FREE 160u
#define OTHER_FREE 128u
#define MNTPOINT "/syzrepro_mnt"
#define MTDBLK "/dev/mtdblock0"
static volatile int g_mounted, g_stop;
static unsigned g_wseq;
static unsigned int crc_tab[256];
static void crc_init(void)
{
unsigned int i, k, c;
for (i = 0; i < 256; i++) {
for (c = i, k = 0; k < 8; k++)
c = (c & 1) ? ((c >> 1) ^ 0xEDB88320u) : (c >> 1);
crc_tab[i] = c;
}
}
static unsigned int jcrc32(unsigned int crc, const void *p, size_t n)
{
const unsigned char *b = p;
while (n--)
crc = crc_tab[(crc ^ *b++) & 0xff] ^ (crc >> 8);
return crc;
}
static int img_read(unsigned char *img)
{
int fd = open(MTDBLK, O_RDONLY);
ssize_t r;
if (fd < 0)
return -1;
ioctl(fd, BLKFLSBUF, 0);
r = pread(fd, img, FLASH, 0);
close(fd);
return r == (ssize_t)FLASH ? 0 : -1;
}
static int img_write(const unsigned char *img)
{
int fd = open(MTDBLK, O_RDWR);
ssize_t w;
if (fd < 0)
return -1;
w = pwrite(fd, img, FLASH, 0);
fsync(fd);
ioctl(fd, BLKFLSBUF, 0);
close(fd);
sync();
return w == (ssize_t)FLASH ? 0 : -1;
}
static int build_fs(unsigned char *img, unsigned char *data)
{
char name[256], path[600];
unsigned i;
int made = 0;
memset(img, 0xff, FLASH);
if (img_write(img) || mount("mtd0", MNTPOINT, "jffs2", 0, NULL))
return -1;
for (i = 0; i < 48; i++) {
int fd;
ssize_t w;
memset(name, 'a' + (i % 26), 200);
name[200] = 0;
snprintf(path, sizeof(path), MNTPOINT "/%s%02u", name, i);
fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0)
break;
w = write(fd, data, 4096);
if (w == 4096)
w = write(fd, data + 4096, 4096);
fsync(fd);
close(fd);
if (w != 4096)
break;
made++;
}
sync();
umount2(MNTPOINT, MNT_DETACH);
return made > 0 ? 0 : -1;
}
/*
* Wipe one eraseblock down to a single length-12 node so that, once mounted,
* jffs2_sum_write_sumnode() is entered with nothing to write.
*/
static int scribble(unsigned char *img)
{
unsigned b, k, tail[NBLK], junk = NBLK, best = 0, bestt = 0;
unsigned char *p;
unsigned int hdr;
for (b = 0; b < NBLK; b++) {
for (k = 0; k < SECT && img[b * SECT + SECT - 1 - k] == 0xff; k++)
;
tail[b] = k;
if (k > bestt) {
bestt = k;
best = b;
}
if (k >= SECT - 16 && junk == NBLK)
junk = b;
}
if (junk == NBLK)
junk = best;
for (b = 0; b < NBLK; b++)
if (b != junk && tail[b] > OTHER_FREE)
memset(img + b * SECT + SECT - tail[b], 0, tail[b] - OTHER_FREE);
p = img + junk * SECT;
memset(p, 0, SECT);
p[0] = 0x85; p[1] = 0x19; p[2] = 0x03; p[3] = 0x20; p[4] = 12;
hdr = jcrc32(0, p, 8);
p[8] = hdr; p[9] = hdr >> 8; p[10] = hdr >> 16; p[11] = hdr >> 24;
memset(p + SECT - GATE_FREE, 0xff, GATE_FREE);
return img_write(img) ? -1 : (int)junk;
}
static void *writer(void *arg)
{
int idx = (int)(long)arg;
unsigned char *buf = malloc(8192);
char nm[16], path[128];
snprintf(nm, sizeof(nm), "syzrepro%d", idx);
prctl(PR_SET_NAME, nm, 0, 0, 0);
memset(buf, 0x5a, 8192);
while (!g_stop) {
int fd;
if (!g_mounted) {
usleep(2000);
continue;
}
snprintf(path, sizeof(path), MNTPOINT "/w%d_%u", idx, (g_wseq++) & 31u);
fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
usleep(3000);
continue;
}
write(fd, buf, 4096);
write(fd, buf, 4096);
fsync(fd);
close(fd);
usleep(1000);
}
free(buf);
return NULL;
}
int main(void)
{
unsigned char *img = malloc(FLASH), *data = malloc(8192);
pthread_t th;
int i, it;
prctl(PR_SET_NAME, "syzrepro0", 0, 0, 0);
crc_init();
for (i = 0; i < 8192; i++)
data[i] = (i >> 4) & 0x0f;
mkdir(MNTPOINT, 0755);
mknod(MTDBLK, S_IFBLK | 0600, makedev(31, 0));
umount2(MNTPOINT, MNT_DETACH);
pthread_create(&th, NULL, writer, (void *)1L);
for (it = 0; it < 25 && !g_stop; it++) {
g_mounted = 0;
umount2(MNTPOINT, MNT_DETACH);
usleep(50000);
if (build_fs(img, data) || img_read(img) || scribble(img) < 0 ||
mount("mtd0", MNTPOINT, "jffs2", 0, NULL)) {
usleep(200000);
continue;
}
usleep(1200000);
g_mounted = 1;
usleep(1200000);
g_mounted = 0;
usleep(50000);
umount2(MNTPOINT, MNT_DETACH);
usleep(50000);
}
g_stop = 1;
pthread_join(th, NULL);
umount2(MNTPOINT, MNT_DETACH);
return 0;
}
==================================================================
Crash log:
==================================================================
kernel BUG at fs/jffs2/summary.c:868!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 2 UID: 0 PID: 404 Comm: syzrepro1 Not tainted 7.2.0-dirty #3 PREEMPT
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:jffs2_sum_write_sumnode+0x1bb6/0x1bc0 fs/jffs2/summary.c:868
Code: 14 89 d9 80 e1 07 80 c1 03 38 c1 7c 08 48 89 df e8 ef a1 61 ff 8b 33 48 c7 c7 e0 c7 31 87 48 c7 c2 fb a0 ff 87 e8 8a 17 94 fe <0f> 0b cc cc cc cc cc cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90
RSP: 0018:ffff888109cd74a0 EFLAGS: 00010246
RAX: 0000000000000044 RBX: ffff888106c93a00 RCX: 5e7574cf0ce16600
RDX: 0000000000000001 RSI: 0000000000000008 RDI: ffff888109cd7360
RBP: ffff888109cd7588 R08: ffff888109cd7367 R09: 1ffff1102139ae6c
R10: dffffc0000000000 R11: ffffed102139ae6d R12: ffff88810357dd84
R13: ffff88810bbc1aa0 R14: dffffc0000000000 R15: ffff88810bbc1990
FS: 000078a3c6f956c0(0000) GS:ffff888190840000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005bafe23d9b20 CR3: 0000000108312000 CR4: 00000000000006f0
Call Trace:
<TASK>
jffs2_do_reserve_space+0x1c9/0xe50 fs/jffs2/nodemgmt.c:398
jffs2_reserve_space_gc+0x45/0xb0 fs/jffs2/nodemgmt.c:232
jffs2_garbage_collect_dirent+0x209/0x320 fs/jffs2/gc.c:882
jffs2_garbage_collect_live+0x42a/0x2970 fs/jffs2/gc.c:574
jffs2_garbage_collect_pass+0x141b/0x1af0 fs/jffs2/gc.c:466
jffs2_reserve_space+0x4e5/0xae0 fs/jffs2/nodemgmt.c:177
jffs2_do_create+0x52/0xbe0 fs/jffs2/write.c:454
jffs2_create+0x1b4/0x300 fs/jffs2/dir.c:205
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0xe3c/0x29b0 fs/namei.c:4860
do_file_open+0x19d/0x360 fs/namei.c:4892
do_sys_openat2+0x9a/0x100 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0xf8/0x130 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf7/0x370 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x78a3c7091090
Code: 48 89 44 24 20 75 93 44 89 54 24 0c e8 a9 d7 f8 ff 44 8b 54 24 0c 89 da 48 89 ee 41 89 c0 bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 77 38 44 89 c7 89 44 24 0c e8 fc d7 f8 ff 8b 44
RSP: 002b:000078a3c6f94d80 EFLAGS: 00000293 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 0000000000000241 RCX: 000078a3c7091090
RDX: 0000000000000241 RSI: 000078a3c6f94e20 RDI: 00000000ffffff9c
RBP: 000078a3c6f94e20 R08: 0000000000000000 R09: 0000000000000075
R10: 00000000000001a4 R11: 0000000000000293 R12: 000078a3c6f94e20
R13: 0000000000000001 R14: 00007ffe30446b90 R15: 000078a3c6795000
==================================================================