[BUG] general protection fault in jffs2_xattr_delete_inode

Jaeyoung Chung <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.drivers.mtd
Message-ID <[email protected]>
Hello,

We found a "general protection fault in jffs2_xattr_delete_inode" 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_MTD=y
    CONFIG_MTD_MTDRAM=y
    CONFIG_MTDRAM_TOTAL_SIZE=128
    CONFIG_MTDRAM_ERASE_SIZE=4
    CONFIG_MTD_BLOCK=y
    CONFIG_JFFS2_FS=y
    CONFIG_JFFS2_FS_XATTR=y
    CONFIG_DEBUG_FS=y
    CONFIG_FAULT_INJECTION=y
    CONFIG_FAILSLAB=y
    CONFIG_FAULT_INJECTION_DEBUG_FS=y
    CONFIG_KASAN=y

We hope this report is useful. Please let us know if any further
information would help.

Reported-by: Eulgyu Kim <[email protected]>
Reported-by: Jaeyoung Chung <[email protected]>

Kernel delay patch:
==================================================================
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 6ada8369a762..32c2aaae80d5 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -25,6 +25,7 @@
 #include <linux/vmalloc.h>
 #include <linux/vfs.h>
 #include <linux/crc32.h>
+#include <linux/delay.h>
 #include "nodelist.h"
 
 static int jffs2_flash_setup(struct jffs2_sb_info *c);
@@ -433,6 +434,9 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
 
 	c = JFFS2_SB_INFO(sb);
 
+	if (strncmp(current->comm, "syzrepro1", 9) == 0) {
+		mdelay(10);
+	}
 	inode = new_inode(sb);
 
 	if (!inode)
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index cda9a361368e..9e7886c5b95a 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/crc32.h>
 #include <linux/pagemap.h>

==================================================================

C reproducer:
==================================================================
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>

#define SYSCHK(x) ({ long __r = (long)(x); if (__r == -1L) { perror(#x); exit(1); } __r; })

#define JM   "/root/jm"
#define DIR0 JM "/d0"
#define DIR1 JM "/d1"
#define DBG  "/sys/kernel/debug"

static volatile int stop_all;

static void knob(const char *path, const char *val)
{
	int fd = SYSCHK(open(path, O_WRONLY));

	write(fd, val, strlen(val));
	close(fd);
}

static void *th_victim(void *arg)
{
	char p[128];
	unsigned i = 0;
	int fd;

	prctl(PR_SET_NAME, "syzrepro0", 0, 0, 0);
	while (!stop_all) {
		snprintf(p, sizeof(p), DIR0 "/v%u", i++ & 31u);
		fd = open(p, O_RDWR | O_CREAT | O_EXCL, 0600);
		if (fd >= 0)
			close(fd);
		else if (errno == ENOSPC || errno == EIO)
			sched_yield();
		unlink(p);
	}
	return NULL;
}

static void *th_inject(void *arg)
{
	char p[128], nb[16];
	unsigned nth = 1, i = 0;
	int ffd, len, fd;

	prctl(PR_SET_NAME, "syzrepro1", 0, 0, 0);
	ffd = SYSCHK(open("/proc/thread-self/fail-nth", O_RDWR));

	while (!stop_all) {
		snprintf(p, sizeof(p), DIR1 "/t%u", i++ & 15u);
		unlink(p);

		len = snprintf(nb, sizeof(nb), "%u", nth);
		if (write(ffd, nb, len) != len)
			break;

		fd = syscall(SYS_openat, AT_FDCWD, p, O_RDWR | O_CREAT | O_EXCL, 0777);
		write(ffd, "0", 1);

		if (fd >= 0) {
			close(fd);
			unlink(p);
		} else if (errno == ENOSPC || errno == EIO) {
			sched_yield();
		}
		if (++nth > 32)
			nth = 1;
	}
	close(ffd);
	return NULL;
}

static void *th_churn(void *arg)
{
	long idx = (long)arg;
	char nm[16], *p;
	int k, fd;

	snprintf(nm, sizeof(nm), "syzrepro%ld", idx);
	prctl(PR_SET_NAME, nm, 0, 0, 0);

	p = malloc(4008);
	p[0] = '/';
	memset(p + 1, 'a' + (int)(idx & 7), 3998);
	p[3999] = 0;

	while (!stop_all) {
		for (k = 0; k < 256; k++) {
			fd = syscall(SYS_openat, AT_FDCWD, p, O_RDONLY, 0);
			if (fd >= 0)
				close(fd);
		}
	}
	free(p);
	return NULL;
}

int main(void)
{
	pthread_t th[4];
	char p[128];
	int i, fd;

	mkdir(DBG, 0755);
	if (mount("none", DBG, "debugfs", 0, NULL) != 0 && errno != EBUSY)
		SYSCHK(-1);
	knob(DBG "/failslab/ignore-gfp-wait", "N");
	knob(DBG "/failslab/verbose", "1");
	knob(DBG "/failslab/probability", "0");
	knob(DBG "/failslab/cache-filter", "N");

	mkdir(JM, 0777);
	if (mount("mtd0", JM, "jffs2", 0, NULL) != 0 &&
	    mount("/dev/mtdblock0", JM, "jffs2", 0, NULL) != 0)
		SYSCHK(-1);
	mkdir(DIR0, 0777);
	mkdir(DIR1, 0777);

	for (i = 0; i < 4; i++) {
		snprintf(p, sizeof(p), DIR1 "/w%d", i);
		fd = open(p, O_RDWR | O_CREAT | O_EXCL, 0600);
		if (fd >= 0)
			close(fd);
		unlink(p);
	}

	pthread_create(&th[0], NULL, th_victim, NULL);
	pthread_create(&th[1], NULL, th_inject, NULL);
	pthread_create(&th[2], NULL, th_churn, (void *)2L);
	pthread_create(&th[3], NULL, th_churn, (void *)3L);

	sleep(165);
	stop_all = 1;
	for (i = 0; i < 4; i++)
		pthread_join(th[i], NULL);
	return 0;
}
==================================================================

Crash log:
==================================================================
name failslab, interval 1, probability 0, space 0, times 0
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 0, space 0, times 0
FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 0, space 0, times 0
Oops: general protection fault, probably for non-canonical address 0xec8c888c8c8c8c91: 0000 [#1] SMP KASAN PTI
KASAN: maybe wild-memory-access in range [0x6464646464646488-0x646464646464648f]
CPU: 3 UID: 0 PID: 402 Comm: syzrepro1 Not tainted 7.2.0-dirty #2 PREEMPT 
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:jffs2_xattr_delete_inode+0x38/0x300 fs/jffs2/xattr.c:602
Code: 41 55 41 54 53 48 83 ec 50 48 89 3c 24 48 85 f6 74 2a 49 89 f6 49 bd 00 00 00 00 00 fc ff df 48 8d 5e 28 48 89 d8 48 c1 e8 03 <42> 0f b6 04 28 84 c0 0f 85 90 02 00 00 83 3b 00 74 14 48 83 c4 50
RSP: 0018:ffff888104ad7918 EFLAGS: 00010203
RAX: 0c8c8c8c8c8c8c91 RBX: 646464646464648c RCX: 0000000000000001
RDX: 0000000000000001 RSI: 6464646464646464 RDI: ffff888108260000
RBP: dffffc0000000000 R08: ffff888104ad7987 R09: 1ffff1102095af30
R10: dffffc0000000000 R11: ffffed102095af31 R12: 1ffff11022349cfc
R13: dffffc0000000000 R14: 6464646464646464 R15: 1ffff11022349ce8
FS:  00007863627186c0(0000) GS:ffff88815e8ac000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000786362717f78 CR3: 000000010795c000 CR4: 00000000000006f0
Call Trace:
 <TASK>
 jffs2_do_clear_inode+0x4d/0x2f0 fs/jffs2/readinode.c:1418
 evict+0x353/0x700 fs/inode.c:825
 jffs2_new_inode+0x443/0xce0 fs/jffs2/fs.c:-1
 jffs2_create+0x87/0x300 fs/jffs2/dir.c:182
 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:0x78636301e829
Code: 08 89 e8 5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d a7 15 0d 00 f7 d8 64 89 01 48
RSP: 002b:0000786362717df8 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 0000000000000007 RCX: 000078636301e829
RDX: 00000000000000c2 RSI: 0000786362717e10 RDI: 00000000ffffff9c
RBP: 0000000000000001 R08: 0000000000000075 R09: 0000000000000037
R10: 00000000000001ff R11: 0000000000000246 R12: 0000786362717e10
R13: 0000000000000027 R14: 0000000000000004 R15: 0000786362717e00
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:jffs2_xattr_delete_inode+0x38/0x300 fs/jffs2/xattr.c:602
Code: 41 55 41 54 53 48 83 ec 50 48 89 3c 24 48 85 f6 74 2a 49 89 f6 49 bd 00 00 00 00 00 fc ff df 48 8d 5e 28 48 89 d8 48 c1 e8 03 <42> 0f b6 04 28 84 c0 0f 85 90 02 00 00 83 3b 00 74 14 48 83 c4 50
RSP: 0018:ffff888104ad7918 EFLAGS: 00010203
RAX: 0c8c8c8c8c8c8c91 RBX: 646464646464648c RCX: 0000000000000001
RDX: 0000000000000001 RSI: 6464646464646464 RDI: ffff888108260000
RBP: dffffc0000000000 R08: ffff888104ad7987 R09: 1ffff1102095af30
R10: dffffc0000000000 R11: ffffed102095af31 R12: 1ffff11022349cfc
R13: dffffc0000000000 R14: 6464646464646464 R15: 1ffff11022349ce8
FS:  00007863627186c0(0000) GS:ffff88815e8ac000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000786362717f78 CR3: 000000010795c000 CR4: 00000000000006f0
----------------
Code disassembly (best guess):
   0:	41 55                	push   %r13
   2:	41 54                	push   %r12
   4:	53                   	push   %rbx
   5:	48 83 ec 50          	sub    $0x50,%rsp
   9:	48 89 3c 24          	mov    %rdi,(%rsp)
   d:	48 85 f6             	test   %rsi,%rsi
  10:	74 2a                	je     0x3c
  12:	49 89 f6             	mov    %rsi,%r14
  15:	49 bd 00 00 00 00 00 	movabs $0xdffffc0000000000,%r13
  1c:	fc ff df
  1f:	48 8d 5e 28          	lea    0x28(%rsi),%rbx
  23:	48 89 d8             	mov    %rbx,%rax
  26:	48 c1 e8 03          	shr    $0x3,%rax
* 2a:	42 0f b6 04 28       	movzbl (%rax,%r13,1),%eax <-- trapping instruction
  2f:	84 c0                	test   %al,%al
  31:	0f 85 90 02 00 00    	jne    0x2c7
  37:	83 3b 00             	cmpl   $0x0,(%rbx)
  3a:	74 14                	je     0x50
  3c:	48 83 c4 50          	add    $0x50,%rsp
==================================================================
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.