syzbot
kernel BUG in sync_timeline_signal
Status:
upstream: reported C repro on 2026/11/04 06:38
Subsystems:
media
dri
Labels:
prio:normal
[Documentation on labels]
Reported-by: syzbot+cba1a1f459230395a076@syzkaller.appspotmail.com
First crash: 4d06h, last: 1d10h
✨ AI-generated root cause summary:
In drivers/dma-buf/sw_sync.c, a race condition exists between dma_fence_put() and sync_timeline_signal(). When the last reference to a sync_pt fence is dropped, its refcount reaches 0 before dma_fence_release() -> timeline_fence_release() acquires obj->lock to remove the sync_pt from obj->pt_list. If sync_timeline_signal() runs concurrently under obj->lock, it iterates obj->pt_list and calls dma_fence_get() on the fence whose refcount is already 0, violating refcount invariants and causing a use-after-free / refcount addition-on-0 kernel crash.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Functions, Structs, Fields, and Files Involved
- Source File:
drivers/dma-buf/sw_sync.c
- Key Structs & Fields:
struct sync_timeline: pt_list (list of active points), pt_tree (rbtree of points), lock (spinlock protecting timeline state).
struct sync_pt: base (struct dma_fence), link (struct list_head), node (struct rb_node).
struct dma_fence: refcount (struct kref / refcount_t).
- Key Functions:
2. Trigger Mechanism & Violated Invariants
-
Object Lifecycle & Refcounting Imbalance:
- When a sync point is created via
sync_pt_create() in sw_sync_ioctl_create_fence(), dma_fence_init(&pt->base, ...) initializes the fence refcount to 1.
- The sync point
pt is inserted into obj->pt_tree and obj->pt_list. However, the timeline object sync_timeline does not take a reference on pt->base (to avoid circular references, since pt references obj).
- The fence reference is handed over to userspace via a
sync_file file descriptor.
-
The Race Window on Destruction:
- When userspace closes the fence file descriptor (or any caller drops the last fence reference via
dma_fence_put()), kref_put(&fence->refcount, dma_fence_release) executes refcount_dec_and_test(&kref->refcount).
- The refcount transitions from
1 to 0 without holding obj->lock.
- Only later, inside
dma_fence_release() -> ops->release() (timeline_fence_release()), does the releasing thread acquire dma_fence_lock_irqsave(fence, flags) (which locks &obj->lock) to remove pt->link from obj->pt_list and pt->node from obj->pt_tree.
- While
sync_pt_create() accounts for this window when searching obj->pt_tree by calling dma_fence_get_rcu(&other->base) (line 281), sync_timeline_signal() does not.
-
Violation in sync_timeline_signal():
- If another thread executes
sw_sync_ioctl_inc() -> sync_timeline_signal(obj, inc) while pt->base.refcount == 0 (before timeline_fence_release() can acquire obj->lock):
list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
if (!timeline_fence_signaled(&pt->base))
break;
dma_fence_get(&pt->base); // <-- Line 224: Unconditional refcount_inc() on 0
- Calling
dma_fence_get(&pt->base) calls refcount_inc() on a refcount that has already dropped to 0, which violates refcount_t invariants and triggers refcount_warn_saturate() with REFCOUNT_ADD_UAF.
3. Runtime Consequences in Production Builds
- Denial of Service: In a production kernel built with
CONFIG_REFCOUNT_FULL (standard on modern Linux kernels), attempting addition on a refcount of zero immediately triggers refcount_warn_saturate / kernel BUG, panicking the system.
- Memory Corruption / Use-After-Free: Even if refcount checking were non-fatal, the fence is already in the midst of its destruction path (
dma_fence_release() and subsequent dma_fence_free() via kfree_rcu()). Resurrecting or referencing the dying pt leads to concurrent accesses, use-after-free, and list/tree corruption.
- Information Leak: None. No uninitialized memory or out-of-bounds kernel memory contents are exposed to userspace.
▶
▼
Last patch testing requests (1)
Sample crash report:
refcount_t: addition on 0; use-after-free.
------------[ cut here ]------------
kernel BUG at lib/refcount.c:34!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 1 UID: 0 PID: 6018 Comm: syz-executor369 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/15/2026
RIP: 0010:refcount_warn_saturate.cold+0x12/0x14 lib/refcount.c:34
Code: 89 ea be 01 00 00 00 48 c7 c7 40 c1 c3 90 e8 0a 5a 01 00 e9 72 e3 02 04 e8 c0 54 d7 00 48 c7 c7 a0 4a 81 8c e8 04 85 eb ff 90 <0f> 0b e8 ac 54 d7 00 48 c7 c7 40 4b 81 8c e8 f0 84 eb ff 90 0f 0b
RSP: 0018:ffffc90003e57cb8 EFLAGS: 00010086
RAX: 000000000000002a RBX: ffff88802d271570 RCX: 0000000000000000
RDX: 000000000000002a RSI: ffffffff81ebf219 RDI: fffff520007caf88
RBP: 0000000000000002 R08: 0000000000000000 R09: 0000000000000000
R10: ffffc90003e57a7f R11: 0000000000000001 R12: 0000000000000000
R13: dffffc0000000000 R14: ffff88802d271300 R15: ffffc90003e57d18
FS: 00007f83e74d26c0(0000) GS:ffff88812257d000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f83e75a93c8 CR3: 00000000357c8000 CR4: 00000000003526f0
Call Trace:
<TASK>
__refcount_add include/linux/refcount.h:289 [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:323 [inline]
sync_timeline_signal+0x6d9/0x8a0 drivers/dma-buf/sw_sync.c:224
sw_sync_ioctl_inc drivers/dma-buf/sw_sync.c:405 [inline]
sw_sync_ioctl+0x7b4/0x13f0 drivers/dma-buf/sw_sync.c:470
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f83e7507d5b
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:00007f83e74d2100 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007f83e7507d5b
RDX: 00007f83e74d2170 RSI: 0000000040045701 RDI: 0000000000000003
RBP: 00000000000003d0 R08: 0000000000000000 R09: 00007f83e74d26c0
R10: 0000000000000008 R11: 0000000000000246 R12: 00007f83e74d2170
R13: 00000000000000f3 R14: 00007ffcce841440 R15: 00007ffcce841528
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:refcount_warn_saturate.cold+0x12/0x14 lib/refcount.c:34
Code: 89 ea be 01 00 00 00 48 c7 c7 40 c1 c3 90 e8 0a 5a 01 00 e9 72 e3 02 04 e8 c0 54 d7 00 48 c7 c7 a0 4a 81 8c e8 04 85 eb ff 90 <0f> 0b e8 ac 54 d7 00 48 c7 c7 40 4b 81 8c e8 f0 84 eb ff 90 0f 0b
RSP: 0018:ffffc90003e57cb8 EFLAGS: 00010086
RAX: 000000000000002a RBX: ffff88802d271570 RCX: 0000000000000000
RDX: 000000000000002a RSI: ffffffff81ebf219 RDI: fffff520007caf88
RBP: 0000000000000002 R08: 0000000000000000 R09: 0000000000000000
R10: ffffc90003e57a7f R11: 0000000000000001 R12: 0000000000000000
R13: dffffc0000000000 R14: ffff88802d271300 R15: ffffc90003e57d18
FS: 00007f83e74d26c0(0000) GS:ffff88812257d000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f83e75a93c8 CR3: 00000000357c8000 CR4: 00000000003526f0
* Struck through repros no longer work on HEAD.