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