// https://syzkaller.appspot.com/bug?id=740a26398af68fdd8eeb8f31d213b6cf6ba67e58 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include struct jffs2_unknown_node { uint16_t magic; uint16_t nodetype; uint32_t totlen; uint32_t hdr_crc; } __attribute__((packed)); uint32_t jffs2_crc32(uint32_t crc, const void *buf, size_t size) { const uint8_t *p = buf; while (size--) { crc ^= *p++; for (int i = 0; i < 8; i++) { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320; else crc >>= 1; } } return crc; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting JFFS2 reproducer...\n"); long page_size = 4096; int num_blocks = 64; size_t img_size = num_blocks * page_size; uint8_t *img = malloc(img_size); if (!img) { printf("[-] Failed to malloc: %s\n", strerror(errno)); exit(1); } memset(img, 0xff, img_size); /* Block 0: Cleanmarker + Padding */ uint8_t *b0 = img; struct jffs2_unknown_node *cm0 = (struct jffs2_unknown_node *)b0; cm0->magic = 0x1985; cm0->nodetype = 0x2003; /* JFFS2_NODETYPE_CLEANMARKER */ cm0->totlen = 12; cm0->hdr_crc = jffs2_crc32(0, cm0, 8); struct jffs2_unknown_node *pad = (struct jffs2_unknown_node *)(b0 + 12); pad->magic = 0x1985; pad->nodetype = 0x2004; /* JFFS2_NODETYPE_PADDING */ pad->totlen = 3944; pad->hdr_crc = jffs2_crc32(0, pad, 8); /* Blocks 1 to 63: Cleanmarker only */ for (int i = 1; i < num_blocks; i++) { uint8_t *b = img + i * page_size; struct jffs2_unknown_node *cm = (struct jffs2_unknown_node *)b; cm->magic = 0x1985; cm->nodetype = 0x2003; cm->totlen = 12; cm->hdr_crc = jffs2_crc32(0, cm, 8); } printf("[+] Image constructed in memory\n"); int fd = open("/tmp/jffs2.img", O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { printf("[-] Failed to open /tmp/jffs2.img: %s\n", strerror(errno)); exit(1); } if (write(fd, img, img_size) != img_size) { printf("[-] Failed to write to /tmp/jffs2.img: %s\n", strerror(errno)); exit(1); } close(fd); free(img); printf("[+] Image written to /tmp/jffs2.img\n"); int ctl = open("/dev/loop-control", O_RDWR); if (ctl < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } int devnr = ioctl(ctl, LOOP_CTL_GET_FREE); close(ctl); if (devnr < 0) { printf("[-] Failed to ioctl LOOP_CTL_GET_FREE: %s\n", strerror(errno)); exit(1); } printf("[+] Got free loop device: %d\n", devnr); char loopname[64]; sprintf(loopname, "/dev/loop%d", devnr); int loopfd = open(loopname, O_RDWR); if (loopfd < 0) { printf("[-] Failed to open %s: %s\n", loopname, strerror(errno)); exit(1); } fd = open("/tmp/jffs2.img", O_RDWR); if (fd < 0) { printf("[-] Failed to open /tmp/jffs2.img: %s\n", strerror(errno)); exit(1); } if (ioctl(loopfd, LOOP_SET_FD, fd) < 0) { printf("[-] Failed to ioctl LOOP_SET_FD: %s\n", strerror(errno)); exit(1); } close(loopfd); close(fd); printf("[+] Attached /tmp/jffs2.img to %s\n", loopname); int b2m = open("/sys/module/block2mtd/parameters/block2mtd", O_WRONLY); if (b2m < 0) { printf("[-] Failed to open block2mtd: %s\n", strerror(errno)); exit(1); } char param[128]; sprintf(param, "%s,%ld,testjffs\n", loopname, page_size); if (write(b2m, param, strlen(param)) < 0) { printf("[-] Failed to write block2mtd: %s\n", strerror(errno)); exit(1); } close(b2m); printf("[+] Set up block2mtd with %s", param); usleep(500000); if (mkdir("/mnt/jffs2", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /mnt/jffs2: %s\n", strerror(errno)); exit(1); } int mtd_num = -1; FILE *fp = fopen("/proc/mtd", "r"); if (fp) { char line[256]; while (fgets(line, sizeof(line), fp)) { if (strstr(line, "testjffs")) { sscanf(line, "mtd%d:", &mtd_num); } } fclose(fp); } if (mtd_num < 0) { printf("[-] Failed to find mtd device for testjffs\n"); exit(1); } char mtdname[64]; sprintf(mtdname, "/dev/mtdblock%d", mtd_num); if (mount(mtdname, "/mnt/jffs2", "jffs2", MS_NOATIME, "") < 0) { printf("[-] Failed to mount %s on /mnt/jffs2: %s\n", mtdname, strerror(errno)); exit(1); } printf("[+] Mounted %s on /mnt/jffs2\n", mtdname); printf("[*] Triggering the bug...\n"); if (symlink("this_is_a_very_long_target_string_to_make_minsize_large_1234", "/mnt/jffs2/sym") < 0) { printf("[-] symlink failed: %s\n", strerror(errno)); } else { printf("[+] symlink succeeded\n"); } sync(); sleep(3); printf("[+] Done.\n"); return 0; }