[BUG] WARNING: refcount bug in sync_timeline_signal

Jaeyoung Chung <[email protected]>
Newsgroups org.kernel.vger.linux-media,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Hello,

We found a "WARNING: refcount bug in sync_timeline_signal" 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_SW_SYNC=y
    CONFIG_SYNC_FILE=y
    CONFIG_DMA_SHARED_BUFFER=y
    CONFIG_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/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c
index 8df20b0218a9..d30ce9d915ba 100644
--- a/drivers/dma-buf/sw_sync.c
+++ b/drivers/dma-buf/sw_sync.c
@@ -8,6 +8,7 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
+#include <linux/delay.h>
 #include <linux/panic.h>
 #include <linux/slab.h>
 #include <linux/sync_file.h>
@@ -215,6 +216,10 @@ static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
 
 	spin_lock_irq(&obj->lock);
 
+	if (strncmp(current->comm, "syzrepro1", 9) == 0) {
+		mdelay(50);
+	}
+
 	obj->value += inc;
 
 	list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
==================================================================

C reproducer:
==================================================================
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.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 <time.h>
#include <unistd.h>

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

#define SW_SYNC_IOC_CREATE_FENCE 0xc0285700u
#define SW_SYNC_IOC_INC 0x40045701u
#define SWSYNC "/sys/kernel/debug/sync/sw_sync"
#define BIAS_US 2000

struct fence_data {
	uint32_t value;
	char name[32];
	int32_t fence;
};

static volatile int g_round, g_stop, g_tl_fd = -1, g_fence_fd = -1;
static volatile int g_armed, g_done0, g_done1;

static void spin_us(long us)
{
	struct timespec a, b;

	clock_gettime(CLOCK_MONOTONIC, &a);
	do {
		clock_gettime(CLOCK_MONOTONIC, &b);
		__asm__ __volatile__("pause" ::: "memory");
	} while ((b.tv_sec - a.tv_sec) * 1000000L + (b.tv_nsec - a.tv_nsec) / 1000L < us);
}

static void pin_cpu(int cpu)
{
	cpu_set_t set;

	CPU_ZERO(&set);
	CPU_SET(cpu, &set);
	sched_setaffinity(0, sizeof(set), &set);
}

static int wait_round(int *last)
{
	while (g_round == *last && !g_stop)
		sched_yield();
	if (g_stop)
		return 0;
	*last = g_round;
	return 1;
}

/* T1: advances the timeline, signalling the pt */
static void *thr_inc(void *a)
{
	unsigned int v = 1;
	int last = 0;

	(void)a;
	prctl(PR_SET_NAME, "syzrepro1", 0, 0, 0);
	pin_cpu(0);
	while (wait_round(&last)) {
		g_armed = 1;
		__sync_synchronize();
		ioctl(g_tl_fd, SW_SYNC_IOC_INC, &v);
		__sync_synchronize();
		g_done1 = 1;
	}
	return NULL;
}

/* T0: closes the fence mid-signal, dropping the pt's last reference */
static void *thr_close(void *a)
{
	int last = 0;

	(void)a;
	prctl(PR_SET_NAME, "syzrepro0", 0, 0, 0);
	pin_cpu(1);
	while (wait_round(&last)) {
		while (!g_armed && !g_stop)
			__asm__ __volatile__("pause" ::: "memory");
		if (g_stop)
			break;
		spin_us(BIAS_US);
		close(g_fence_fd);
		__sync_synchronize();
		g_done0 = 1;
	}
	return NULL;
}

static int open_sw_sync(void)
{
	int fd = open(SWSYNC, O_RDWR);

	if (fd >= 0 || errno != ENOENT)
		return fd;
	mkdir("/sys/kernel/debug", 0755);
	mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
	return open(SWSYNC, O_RDWR);
}

int main(void)
{
	pthread_t t0, t1;
	long i;

	close(SYSCHK(open_sw_sync()));
	pthread_create(&t1, NULL, thr_inc, NULL);
	pthread_create(&t0, NULL, thr_close, NULL);

	for (i = 0; i < 200 && !g_stop; i++) {
		struct fence_data d = { .value = 1, .name = "syzrepro_pt", .fence = -1 };
		int tl = SYSCHK(open_sw_sync()), w;

		SYSCHK(ioctl(tl, SW_SYNC_IOC_CREATE_FENCE, &d));
		g_tl_fd = tl;
		g_fence_fd = d.fence;
		g_armed = g_done0 = g_done1 = 0;
		__sync_synchronize();
		g_round = i + 1;

		for (w = 0; w < 5000 && !(g_done0 && g_done1); w++) {
			struct timespec ts = { 0, 1000000 };

			nanosleep(&ts, NULL);
		}
		close(tl);
	}

	g_stop = 1;
	__sync_synchronize();
	g_round++;
	pthread_join(t1, NULL);
	pthread_join(t0, NULL);
	return 0;
}
==================================================================

Crash log:
==================================================================
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x76/0xd0 lib/refcount.c:25, CPU#0: syzrepro1/402
Modules linked in:
CPU: 0 UID: 0 PID: 402 Comm: syzrepro1 Tainted: G             L      7.2.0-dirty #2 PREEMPT 
Tainted: [L]=SOFTLOCKUP
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:refcount_warn_saturate+0x76/0xd0 lib/refcount.c:25
Code: 3d ff e4 bb 04 67 48 0f b9 3a eb 4d 85 db 74 2f 83 fb 01 75 38 48 8d 3d f8 e4 bb 04 67 48 0f b9 3a eb 36 48 8d 3d fa e4 bb 04 <67> 48 0f b9 3a eb 28 48 8d 3d fc e4 bb 04 67 48 0f b9 3a eb 1a 48
RSP: 0018:ffff88810cd47d68 EFLAGS: 00010046
RAX: 0000000000000000 RBX: 0000000000000002 RCX: dffffc0000000000
RDX: 0000000000000001 RSI: 0000000000000004 RDI: ffffffff9be2c840
RBP: ffff888102a5dc40 R08: ffff888102a5d73b R09: 1ffff1102054bae7
R10: dffffc0000000000 R11: ffffed102054bae8 R12: dffffc0000000000
R13: ffff888102a5d740 R14: ffff888102a5d738 R15: ffff888102a5d738
FS:  00007ae687b8f6c0(0000) GS:ffff88817d75f000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000578dd65f7e58 CR3: 000000010b72c000 CR4: 00000000000006f0
Call Trace:
 <TASK>
 __refcount_add include/linux/refcount.h:-1 [inline]
 __refcount_inc include/linux/refcount.h:366 [inline]
 refcount_inc include/linux/refcount.h:383 [inline]
 kref_get include/linux/kref.h:45 [inline]
 dma_fence_get include/linux/dma-fence.h:317 [inline]
 sync_timeline_signal+0x383/0x550 drivers/dma-buf/sw_sync.c:229
 sw_sync_ioctl_inc drivers/dma-buf/sw_sync.c:410 [inline]
 sw_sync_ioctl+0x140/0x980 drivers/dma-buf/sw_sync.c:475
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:597 [inline]
 __se_sys_ioctl+0xb6/0x100 fs/ioctl.c:583
 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:0x7ae687c90d6b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ae687b8edd0 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007ae687b8ee3c RCX: 00007ae687c90d6b
RDX: 00007ae687b8ee38 RSI: 0000000040045701 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 00007ffcdb19abc7
R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffff80
R13: 0000000000000000 R14: 00007ffcdb19aad0 R15: 00007ae68738f000
 </TASK>
----------------
Code disassembly (best guess):
   0:	3d ff e4 bb 04       	cmp    $0x4bbe4ff,%eax
   5:	67 48 0f b9 3a       	ud1    (%edx),%rdi
   a:	eb 4d                	jmp    0x59
   c:	85 db                	test   %ebx,%ebx
   e:	74 2f                	je     0x3f
  10:	83 fb 01             	cmp    $0x1,%ebx
  13:	75 38                	jne    0x4d
  15:	48 8d 3d f8 e4 bb 04 	lea    0x4bbe4f8(%rip),%rdi        # 0x4bbe514
  1c:	67 48 0f b9 3a       	ud1    (%edx),%rdi
  21:	eb 36                	jmp    0x59
  23:	48 8d 3d fa e4 bb 04 	lea    0x4bbe4fa(%rip),%rdi        # 0x4bbe524
* 2a:	67 48 0f b9 3a       	ud1    (%edx),%rdi <-- trapping instruction
  2f:	eb 28                	jmp    0x59
  31:	48 8d 3d fc e4 bb 04 	lea    0x4bbe4fc(%rip),%rdi        # 0x4bbe534
  38:	67 48 0f b9 3a       	ud1    (%edx),%rdi
  3d:	eb 1a                	jmp    0x59
  3f:	48                   	rex.W
==================================================================
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.