[BUG] kernel BUG in jffs2_sum_write_sumnode

Jaeyoung Chung jjy600901 at snu.ac.kr
Mon Aug 24 08:43:33 PDT 2026


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 at snu.ac.kr>
Reported-by: Jaeyoung Chung <jjy600901 at snu.ac.kr>

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
==================================================================





More information about the linux-mtd mailing list