syzbot
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: 1d10h, last: 1d10h
β¨ 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
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:
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)
- 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.
- The unwinder in
dump_stack_lvl() began printing frames from current RSP up through timerqueue_linked_add.
- 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).
- 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.
- 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.
Sample crash report:
* Struck through repros no longer work on HEAD.