@@ -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
@@ -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__);
@@ -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;
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@snu.ac.kr> Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr> Kernel delay patch: ================================================================== ================================================================== 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 ==================================================================