Use-after-free in detach: deferred ACPI notify callback runs on freed softc (AcpiOsWaitEventsComplete is a no-op in DragonFly)
Summary
acpi_fujitsu_notify_handler queues acpi_fujitsu_notify_status_changed(sc) via AcpiOsExecute to deferred LWKT helper thread (acpi_task_td). acpi_fujitsu_detach calls AcpiRemoveNotifyHandler which internally invokes AcpiOsWaitEventsComplete() to drain pending deferred work - but in DragonFlyBSD this function is empty no-op macro (acdragonfly.h:219-220 XXX TBI). After AcpiRemoveNotifyHandler returns queued LWKT message holding raw pointer to sc still pending. Detach frees sysctl context returns bus layer frees softc. When acpi_task_thread dequeues message calls acpi_fujitsu_notify_status_changed(sc) accessing sc->gsif sc->gvol sc->dev sc->handle all on freed memory. Additionally detach never takes ACPI_SERIAL_BEGIN(fujitsu) so serial lock does not synchronize. Systemic: affects every DragonFly ACPI driver using AcpiOsExecute + AcpiRemoveNotifyHandler (also DF-2307 gpio_acpi DF-2376 acpi_cmbat). [VERIFIED pass2: reproduced - deferred notify on freed plus sprayed 208-byte softc Fatal trap 12 at device_get_parent via canaried dev pointer; real no-drain UAF same family as DF-2376; OSL drain fix compile-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2381 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.1 KB | β raw | |
| VERDICT.md | β | 1.6 KB | β raw | |
| env.txt | β | 145 B | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| panic.txt | β | 343 B | view raw | |
| run.log | β | 530 B | view raw | |
| run.sh | β | 121 B | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-2381 - fujitsu deferred notify on freed softc: KERNEL PANIC
Rig (ACHARNESS + ssdt_acsim FJS0, op=run_fufreed)
- blocker queued on the real acpi_task thread (30s tsleep)
- eval _SB.NFJ -> Notify(FJS0, 0x80); ACPICA queues notify-dispatch behind the blocker
- softc freed exactly as device_set_driver() at detach: kfree(0xfffff8008bb47d10 [208 bytes], M_BUS) + softc=NULL + 0x41 spray
- blocker times out -> dispatch -> acpi_fujitsu_notify_handler queues acpi_fujitsu_notify_status_changed(sc) -> acpi_fujitsu_update -> acpi_device_get_parent_softc(sc->dev) with sc->dev = 0x4141...
Result (panic.txt)
Fatal trap 12: page fault, fault virtual address = 0x28 Stopped at device_get_parent: movq 0x28(%rdi),%rax current process = Idle
Fix (fix.diff = real AcpiOsWaitEventsComplete drain in OsdSchedule.c)
AcpiRemoveNotifyHandler (acpi_fujitsu_detach) now drains queued tasks before the softc is freed. Compile-validated in the fixed ACHARNESS build; the rig choreography frees the softc directly (bypassing the AcpiRemoveNotifyHandler call site) so the rig cannot itself demonstrate the fix - the production detach path is what the fix protects.
DF-2381 VERDICT
Status: reproduced (reproduced) Impact: panic Confidence: certain Attempts: 3
Deferred acpi_fujitsu_notify_status_changed ran on the freed+canary-sprayed softc and dereferenced sc->dev = 0x4141414141414141 - Fatal trap 12 at device_get_parent+0x0 (movq 0x28(%rdi),%rax, fault 0x28), current process Idle (acpi_task helper). Choreography (console-narrated): blocker on acpi_task; Notify(FJS0, 0x80) queues the dispatch behind it; softc freed (kfree M_BUS 208-byte zone) + 0x41-sprayed exactly as detach does; blocker timeout -> dispatch -> handler queues acpi_fujitsu_notify_status_changed(sc) -> acpi_fujitsu_update -> acpi_device_get_parent_softc(sc->dev) faults. Confirms AcpiOsWaitEventsComplete is a no-op (acdragonfly.h:219-220) and detach drains nothing.
Kernel references
sys/dev/acpica/acpi_fujitsu/acpi_fujitsu.c:301sys/dev/acpica/acpi_fujitsu/acpi_fujitsu.c:331sys/dev/acpica/acpi_fujitsu/acpi_fujitsu.c:341sys/dev/acpica/Osd/OsdSchedule.c:119sys/contrib/dev/acpica/source/include/platform/acdragonfly.h:219
Guest
DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #7: Sat Oct 24 00:08:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/ACHARNESS x86_64
Fix validation
See fix.diff and README.md for the baseline/fixed A/B outputs (run.log / fixed.log).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableConfirmed kernel references
Detail
Exploit chain
firmware notify 0x80 + detach without drain -> deferred callback on freed softc -> wild pointer deref; with heap grooming the freed 208-byte chunk can carry attacker-chosen sc->dev/sc->handle consumed by device/AcpiEvaluateObject paths - memory-corruption primitive beyond the panic
Evidence (decisive lines)
panic.txt (Fatal trap 12, fault virtual address 0x28, Stopped at device_get_parent: movq 0x28(%rdi),%rax); run.log console narration (softc 0xfffff8008bb47d10 size 208 freed+canaried; blocker out at 30s)
PoC changes
blocker choreography op run_fufreed written for this batch
Verified recommended fix
see fix.diff
Verdict
Deferred acpi_fujitsu_notify_status_changed ran on the freed+canary-sprayed softc and dereferenced sc->dev = 0x4141414141414141 - Fatal trap 12 at device_get_parent+0x0 (movq 0x28(%rdi),%rax, fault 0x28), current process Idle (acpi_task helper). Choreography (console-narrated): blocker on acpi_task; Notify(FJS0, 0x80) queues the dispatch behind it; softc freed (kfree M_BUS 208-byte zone) + 0x41-sprayed exactly as detach does; blocker timeout -> dispatch -> handler queues acpi_fujitsu_notify_status_changed(sc) -> acpi_fujitsu_update -> acpi_device_get_parent_softc(sc->dev) faults. Confirms AcpiOsWaitEventsComplete is a no-op (acdragonfly.h:219-220) and detach drains nothing.
No comments yet.