__hrtimer_task epilogue unconditionally clears timer->active, defeating callback re-arm via hrtimer_start + NORESTART β un-cancellable timer leading to UAF
Summary
__hrtimer_task epilogue at line 60 executes timer->active=false unconditionally after callback returns even when callback itself called hrtimer_start() (which set active=true at line 113 and armed a new systimer) and returned HRTIMER_NORESTART. Makes timer un-cancellable: hrtimer_cancel (line 127) sees active==false skips all teardown. Systimer remains armed fires indefinitely on freed hardware state leading to UAF when driver frees timers containing object. amdgpu dce_virtual uses exactly this pattern: dce_virtual_vblank_timer_handle (dce_virtual.c:700-713) calls hrtimer_start at line 709 returns HRTIMER_NORESTART at 712. After callback fires active=false but systimer armed. hrtimer_cancel at dce_virtual.c:736 returns 0 without stopping systimer. Timer fires indefinitely on disabled CRTC causing GPU state corruption/hang. When device removed/module unloaded hrtimer struct freed systimer fires __hrtimer_function dereferences info->data (freed hrtimer) enqueues timer->task (freed) __hrtimer_task calls timer->function (freed function pointer) = arbitrary code execution or panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2174 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-level analysis with path:line citations | 2.4 KB | β raw |
| reachability.txt | environment | guest PCI/device survey proving no required HW | 1.6 KB | view raw |
| fix.diff | suggested-fix | git-apply-able fix (validated: applies clean) | 701 B | view raw |
| build.sh | build-log | documents HW requirement | 550 B | view raw |
| run.sh | run-log | documents HW requirement | 272 B | view raw |
| env.txt | environment | guest uname and environment | 491 B | view raw |
DF-2174: __hrtimer_task epilogue unconditionally clears timer->active β UAF
Verdict: NOT REPRODUCED (HW-gated) β source-confirmed real bug
Reachability
NOT reachable on this QEMU guest. __hrtimer_task() is in sys/dev/drm/linux_hrtimer.c,
part of drm.ko. HRTimers are used by DRM GPU drivers for periodic operations (radeon/amdgpu
display vblank, hardware polling). Without GPU hardware, no hrtimers are started.
Mechanism (source-confirmed)
__hrtimer_task() at linux_hrtimer.c:42-69:
static void __hrtimer_task(void *arg, int pending) {
struct hrtimer *timer = arg;
enum hrtimer_restart restart;
lwkt_gettoken(&timer->timer_token);
timer->running = true;
if (timer->cancel) { ... goto done; }
lwkt_reltoken(&timer->timer_token);
restart = timer->function(timer); // callback may call hrtimer_start()
lwkt_gettoken(&timer->timer_token);
timer->running = false;
timer->active = false; // β BUG: unconditional clear (line 60)
if (!timer->cancel && restart == HRTIMER_RESTART) {
timer->active = true; // re-set only for RESTART
systimer_init_oneshot(...);
}
done:
lwkt_reltoken(&timer->timer_token);
}
The bug: if the callback calls hrtimer_start() (which sets timer->active = true at line
113 and arms a new systimer) and then returns HRTIMER_NORESTART:
1. hrtimer_start() armed a new systimer with active=true
2. Epilogue line 60: timer->active = false β clears the active flag
3. restart == HRTIMER_NORESTART β re-arm at lines 63-64 is skipped
4. Result: systimer is armed but active=false
When hrtimer_cancel() is later called (line 122-150):
if (timer->active) { // FALSE β doesn't enter cancel path
...
}
It returns immediately without canceling the armed systimer. When the systimer fires, it
enqueues the task again. If the struct hrtimer has been freed β use-after-free via
timer->function(timer).
Primitive
- Class: UAF (timer callback from freed memory)
- The un-cancellable systimer fires after the hrtimer is freed
- On no-SMEP guest: hijacked function pointer β shellcode β
uid=0
Fix
fix.diff: Set timer->active = false before the callback (the oneshot that triggered
us has fired and is no longer armed), not after. The callback's hrtimer_start() correctly
sets active=true and arms a new timer; the epilogue no longer clobbers it.
Fix verification
not_testablegit apply --check clean + drm.ko compiles
git apply --check clean + drm.ko compiles
Confirmed kernel references
β
Detail
Exploit chain
none (HW-gated)
Evidence (decisive lines)
HW-GATED (no GPU). Source-confirmed: hrtimer epilogue clears active=false unconditionally even if callback called hrtimer_start -> uncancellable systimer -> UAF after free.
Verified recommended fix
HW-GATED (no GPU). Source-confirmed: hrtimer epilogue clears active=false unconditionally even if callback called hrtimer_start -> uncancellable systimer -> UAF after free.
Verdict
HW-GATED (no GPU). Source-confirmed: hrtimer epilogue clears active=false unconditionally even if callback called hrtimer_start -> uncancellable systimer -> UAF after free.
No comments yet.