# DF-4485 — `vo_get_brightness_levels()` leaks the `_BCL` ACPI evaluation buffer on every successful call

## Impact
**Low.** Kernel memory leak in `M_ACPICA` (unbounded in the number of
firmware-initiated REPROBE notifies): each successful `_BCL` evaluation
during attach/rebind leaks the `ACPI_BUFFER` returned by `AcpiEvaluateObject`.
The leak size per event is controlled by the ACPI table (package size), so a
crafted table leaks arbitrarily large chunks per notify → kernel memory
exhaustion.

## Where (sys/dev/acpica/acpi_video/acpi_video.c)
- :992-1003 — success path: `*levelp = levels; return (n);` — returns
  WITHOUT freeing `bcl_buf.Pointer` (the ACPI_OBJECT package). Only the
  failure paths reach the `out:` label that frees it (:1005-1007).
- Compare `vid_enum_outputs()` :960-963 which frees `dod_buf.Pointer` on
  every path including success.

## Reproduction (guest-verified)
SSDT `ssdt_dfb.asl`: `_BCL` returns a 64-entry package (~3.3 KB eval buffer)
and always succeeds; `_DCS` fires `Notify(VGT0, 0x81)` (REPROBE) on every read
from the 2nd on, so each sysctl read of `hw.acpi.video.lcd0.active` rebinds
both outputs and leaks both `_BCL` eval buffers.

`run_df4485.sh` (log: `run.log`):
```
kldload acpi_video
vmstat -m | grep acpica        # baseline: 51 allocs, 10.2K in use
sleep 10; vmstat -m ...        # control: unchanged (51 / 10.2K) — idle churn = 0
40 × sysctl read lcd0.active   # 39 REPROBEs × 2 outputs = 78 leaked buffers
vmstat -m | grep acpica        # after: 209 allocs, 267K in use
```
**+158 allocations / +257 KB** attributable to the reads; ≈3.3 KB per leaked
`_BCL` buffer, exactly the 64-entry package size. Guest stayed up, rebinds
kept working (`levels` still correct after the loop).

## Verdict summary
`reproduced` — deterministic, measurable kernel memory growth under
firmware-controlled notify frequency (impact: memory-exhaustion DoS).
Attacker model: crafted ACPI table (boot/loader privilege) or firmware bug;
hence Low severity.

## Fix
`fix.diff` (validated on the guest): free `bcl_buf.Pointer` on the success
path of `vo_get_brightness_levels()` before returning.
