# VERDICT — DF-4286

**Status: inconclusive (source-certain lifetime defect; driver cannot attach on this guest)**
**Impact: root-racy use-after-free of acpi_thinkpad softc (read/write + AML evaluation with freed handles)**
**Confidence: likely**

## Proof chain

1. `thinkpad_led()` [acpi_thinkpad.c:390-404]: on every led(4) toggle,
   `if (sc->led_busy) return; sc->led_busy = 1; ...;
   AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_led_task, sc);`
   — fire-and-forget; nothing holds a reference on the driver/softc for
   the queued callback.
2. `thinkpad_led_task()` [406-416]: `ACPI_SERIAL_BEGIN(thinkpad);
   acpi_thinkpad_sysctl_set(sc, THINKLIGHT, sc->led_state);
   ACPI_SERIAL_END(thinkpad); sc->led_busy = 0;`
   — dereferences sc extensively (including `AcpiEvaluateObject` with
   `sc->light_handle` loaded from freed memory).
3. `acpi_thinkpad_detach()` [634-663]: `led_destroy(sc->led_dev)`
   [659-660] waits only for the led(4) cdev refs
   (sys/dev/misc/led/led.c:319-340 — destroy_dev + kfree of ledsc);
   the driver softc is freed by the normal device detach machinery
   right after detach returns. A task queued before led_destroy, or
   mid-flight, still executes afterwards → **UAF**.

Contrast: the sensors(9) side of this driver is safe —
`sensor_task_unregister` [sys/kern/kern_sensors.c:238-250] blocks on the
sensor-thread lock which the thread holds across `st->func`, so
`acpi_thinkpad_refresh` cannot run post-detach. The LED path lacks the
equivalent.

## Trigger requirements

- Hardware: ThinkPad ACPI device (probe requires IBM0068/LEN0068/LEN0268
  HID, acpi_thinkpad.c:375, 448-456) with a thinklight setter
  (`light_set_supported`, 1006-1037).
- Privilege: root on both sides (led cdev is 0600 root:wheel
  [led.c:304-305]; unload needs root).
- Race: toggle loop on /dev/led/thinklight vs kldunload acpi_thinkpad /
  devctl detach; AML evaluation gives a wide window.

## Why not runtime-verified

The audit guest has no IBM0068/LEN0068/LEN0268 ACPI node (not a ThinkPad;
verified the module builds on the guest: `make` in
/usr/src/sys/dev/acpica/acpi_thinkpad compiles the unmodified source
cleanly, acpi_thinkpad.ko links). Attach never happens → the race cannot
be staged on this machine. Classified inconclusive/hardware-gated per
audit house style (cf. DF-0281).

## Fix

`fix.diff`: drain in-flight `thinkpad_led_task` in detach by waiting on
`sc->led_busy` after `led_destroy()` (which stops new queueings). Task
clears the flag at acpi_thinkpad.c:415.

## Bottom line

Genuine unsynchronized async-callback lifetime bug (UAF class) on the
ThinkPad LED path; root-only, hardware-gated, race-window practical.
Low severity; worth fixing alongside the other acpi_* detach hygiene.

## 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.
