KASAN: use-after-free Read in timerqueue_linked_add

Status: moderation: reported on 2026/11/05 11:23
Subsystems: kernel
Labels: prio:normal
[Documentation on labels]
Reported-by: syzbot+988a7bb5a458cd4d0161@syzkaller.appspotmail.com
First crash: 1d12h, last: 1d12h
✨ AI-generated root cause summary:

A KASAN use-after-free occurs in __tq_linked_less() when traversing base->active during timerqueue_linked_add(), reading the 8-byte expires field of an on-stack or vmalloc-allocated struct hrtimer whose memory page has already been freed. During the execution of timerqueue_linked_add() in IRQ context, the kernel trapped into kasan_report(), which invoked dump_stack_lvl(); because the 32 KB IRQ stack was already nearly full, stack frames consumed by printing the backtrace hit the IRQ stack guard page (ffffc900006f0ff8), triggering a double fault and panic that truncated the call trace. Consequently, the specific caller responsible for freeing the timer's backing memory without deactivating/canceling it from base->active is not preserved in the report.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 05441d697f7f:

Technical Root Cause Analysis

1. Functions, Structs, and Fields Involved

  • Files: lib/timerqueue.c, include/linux/rbtree.h, kernel/time/hrtimer.c
  • Functions: timerqueue_linked_add(), rb_add_linked(), __rb_add(), __tq_linked_less()
  • Structs & Fields:
    • struct timerqueue_linked_node: contains struct rb_node_linked node and ktime_t expires
    • struct timerqueue_linked_head: contains struct rb_root_linked rb_root
    • struct hrtimer: embeds struct timerqueue_linked_node node
    • struct hrtimer_clock_base: holds struct timerqueue_linked_head active

2. Crash Mechanism & Invariant Violation

The crash report flags a KASAN use-after-free read of size 8 at address ffffc90003947c00:

  • In timerqueue_linked_add() (lib/timerqueue.c:96), a timer node is inserted into the active hrtimer tree via rb_add_linked() (include/linux/rbtree.h:281), which calls __rb_add() (include/linux/rbtree.h:222).
  • As __rb_add() descends the red-black tree, it evaluates the ordering predicate __tq_linked_less() (lib/timerqueue.c:91):
    static __always_inline bool __tq_linked_less(struct rb_node *a, const struct rb_node *b)
    {
        return __node_2_tq_linked(a)->expires < __node_2_tq_linked(b)->expires;
    }
    
  • Dereferencing __node_2_tq_linked(parent)->expires accesses offset 0x18 (or base offset of expires) within the timer node. The target address ffffc90003947c00 resides in the vmalloc / CONFIG_VMAP_STACK address space (aligned near the top of an 8-page / 32 KB allocation ffffc90003940000..ffffc90003948000).
  • KASAN reports this as use-after-free (triggered by shadow byte value 0xFF / KASAN_PAGE_FREE), indicating that the memory page hosting a struct hrtimerβ€”either allocated as part of a task stack (struct hrtimer_sleeper) or an external vmalloc objectβ€”was freed while the timer remained queued in base->active.
  • When an active hrtimer is enqueued, the timer subsystem requires that the timer object must remain valid and not be freed before it is either dequeued via hrtimer_try_to_cancel() / __remove_hrtimer() or expires and completes execution. Freeing the underlying stack frame or memory page while leaving the node linked in base->active violates this lifecycle invariant.

3. Reason for Call Trace Truncation & Secondary Crash

The reported call trace abruptly terminates after timerqueue_linked_add+0x365/0x430:

Call Trace:
 <IRQ>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x13d/0x4b0 mm/kasan/report.c:482
 kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
 __tq_linked_less lib/timerqueue.c:91 [inline]
 __rb_add include/linux/rbtree.h:231 [inline]
 rb_add_linked include/linux/rbtree.h:281 [inline]
 timerqueue_linked_add+0x365/0x430 lib/timerqueue.c:96
BUG: IRQ stack guard page was hit at ffffc900006f0ff8 (stack is ffffc900006f1000..ffffc900006f9000)
  1. When __tq_linked_less() triggered the KASAN check in IRQ context, kasan_report() called print_report() -> print_address_description() (mm/kasan/report.c:378), which immediately calls dump_stack_lvl(KERN_ERR) before printing any object descriptions.
  2. The unwinder in dump_stack_lvl() began printing frames from current RSP up through timerqueue_linked_add.
  3. The 32 KB per-CPU IRQ stack (ffffc900006f1000..ffffc900006f9000) was already nearly exhausted before timerqueue_linked_add() was reached. The additional stack frames consumed by the KASAN reporting and stack-unwinding machinery pushed RSP down to ffffc900006f0ff8 (8 bytes below the bottom of the IRQ stack).
  4. Pushing to ffffc900006f0ff8 hit the unmapped IRQ stack guard page, triggering a double fault (#DF). Handled on the IST exception stack by handle_stack_overflow() (arch/x86/kernel/traps.c:547), the kernel emitted BUG: IRQ stack guard page was hit at ffffc900006f0ff8 and panicked immediately.
  5. Consequently, the unwinder could not finish dumping the caller of timerqueue_linked_add() or the subsequent KASAN allocation/free history, leaving the external caller that allocated and prematurely freed the on-stack struct hrtimer unrecorded in the report. Investigation of kernel/time/hrtimer.c confirmed that internal enqueue/dequeue logic in enqueue_hrtimer() and remove_and_enqueue_same_base() properly manages the tree and holding cpu_base->lock.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
bf3818c0 assessment-security DenialOfService: βœ… Exploitable: βœ… FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: βœ… NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KASAN: use-after-free Read in timerqueue_linked_add 2026/11/04 01:38 2026/11/04 01:38 2026/11/04 02:13 d02c3949

Sample crash report:
==================================================================
BUG: KASAN: use-after-free in __tq_linked_less lib/timerqueue.c:91 [inline]
BUG: KASAN: use-after-free in __rb_add include/linux/rbtree.h:231 [inline]
BUG: KASAN: use-after-free in rb_add_linked include/linux/rbtree.h:281 [inline]
BUG: KASAN: use-after-free in timerqueue_linked_add+0x365/0x430 lib/timerqueue.c:96
Read of size 8 at addr ffffc90003947c00 by task syz.2.49/6088

CPU: 3 UID: 0 PID: 6088 Comm: syz.2.49 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
 <IRQ>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x13d/0x4b0 mm/kasan/report.c:482
 kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
 __tq_linked_less lib/timerqueue.c:91 [inline]
 __rb_add include/linux/rbtree.h:231 [inline]
 rb_add_linked include/linux/rbtree.h:281 [inline]
 timerqueue_linked_add+0x365/0x430 lib/timerqueue.c:96
BUG: IRQ stack guard page was hit at ffffc900006f0ff8 (stack is ffffc900006f1000..ffffc900006f9000)

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/04 01:38 upstream 05441d697f7f 5af5b6c6 .config console log report info [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu-upstream KASAN: use-after-free Read in timerqueue_linked_add
* Struck through repros no longer work on HEAD.