β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4486

REPROBE destroy path leaves the per-output ACPI notify handler registered against freed memory (AcpiRemoveNotifyHandler(NULL) is a no-op)

Summary

The REPROBE handler NULLs vo->handle before rebinding, so vo_destroy calls AcpiRemoveNotifyHandler(NULL,...) which ACPICA rejects with AE_BAD_PARAMETER (ignored) - the installed handler stays registered on the real node with context=vo, and kfree(vo) runs. Later notifies dispatch on freed vo: UAF read + a 4-byte write-after-free (vo_brightness, value limited to 0..100). vo_bind also never removes the old handler (AE_ALREADY_EXISTS rejected). Destroy-with-NULL-handle reachability proven on guest by DF-4484's choreography. vo_matched-flag fix validated as part of DF-4484's diff.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4486 Β· 2 files
FileTypeDescriptionSize
VERDICT.md β€” 3.1 KB ↓ raw
verdict.json β€” 2.2 KB view raw
VERDICT.md
↓ download raw

DF-4486 β€” stale per-output notify handler after REPROBE destroy

Root cause (code-certain, no runtime claim): - sys/dev/acpica/acpi_video/acpi_video.c:385-395 β€” the VID_NOTIFY_REPROBE (0x81) handler NULLs vo->handle for every output, rebinds those matched by the namespace walk, and destroys the unmatched ones. - sys/dev/acpica/acpi_video/acpi_video.c:627-631 β€” acpi_video_vo_destroy() removes the per-output notify handler via AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, ...). For an unmatched output vo->handle is NULL at this point. - sys/contrib/dev/acpica/source/components/events/evxface.c:399-402 β€” AcpiRemoveNotifyHandler() returns AE_BAD_PARAMETER for a NULL Device and does nothing. The return value is ignored by the caller. - Result: the handler installed at first bind (acpi_video.c:611-613, AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler, vo)) stays registered on the REAL (old) device node with context = vo, and kfree(vo, M_ACPIVIDEO) then runs (acpi_video.c:654). Any later firmware notify delivered on that node runs acpi_video_vo_notify_handler() with a freed vo: - :679 if (vo->handle != handle) β€” UAF read of freed memory; - :684-737 on brightness notifies β€” reads vo->vo_numlevels / vo->vo_levels[i] from freed memory and WRITES vo->vo_brightness = new_level (:737) into the freed chunk (4-byte write-after-free, value limited to a listed level 0..100). - Additionally acpi_video_vo_bind() (:594-615) never removes the old handler before installing on the rebind handle; ACPICA rejects the second install with AE_ALREADY_EXISTS (evxface.c:309-311) so the stale registration survives even a successful rebind when the handle is unchanged.

Why "untested" (not runtime-verified): making the stale-handler dispatch visible requires reallocating the freed M_ACPIVIDEO chunk with controlled content (so vo->handle == handle passes) β€” M_ACPIVIDEO is only allocated by this driver's acpi_video_vo_init(), so an in-guest demonstration would need a second controllable allocation of that exact malloc type, which the driver does not offer. The destroy-with-NULL-handle path itself is proven reachable on the guest (DF-4484's REPROBE choreography destroys unmatched outputs the same way; the panic trace in DF-4484/panic.txt shows vo_destroy running on that path).

Fix: part of DF-4484's fix.diff β€” track reprobe matching with a vo_matched flag instead of NULLing vo->handle (so vo_destroy always removes the handler from the real node), and remove the old handler in vo_bind before re-install.

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Covered by DF-4484's fix.diff (same patch); not independently runtime-tested.

DF-4484/fix.diff
per-fix-DF-4486

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (static analysis with path:line); evxface.c:399-402 NULL-handle rejection

Verified recommended fix

Track reprobe matching with a vo_matched flag instead of NULLing vo->handle; remove the old notify handler in vo_bind before re-install

Verdict

Code-certain use-after-free of the notify-handler context: REPROBE (acpi_video.c:385-395) NULLs vo->handle before rebind, so acpi_video_vo_destroy removes the per-output handler via AcpiRemoveNotifyHandler(NULL,...) which returns AE_BAD_PARAMETER without doing anything (evxface.c:399-402); kfree(vo) then runs with the handler still registered on the real device node. Later notifies dispatch acpi_video_vo_notify_handler on freed vo: UAF read at :679 (vo->handle compare) and a 4-byte write-after-free at :737 (vo->vo_brightness, value 0..100) under brightness notifies. vo_bind also never removes the old handler before re-install (:611-613, AE_ALREADY_EXISTS per evxface.c:309-311). Not runtime-demonstrated: visibility requires reallocating the freed M_ACPIVIDEO chunk with controlled content (only vo_init allocates that type). Fixed by the same fix.diff as DF-4484 (vo_matched flag keeps handle valid at destroy).