# DF-4161 — VERDICT

**Status: inconclusive (source-level certain, runtime hardware-gated).**
**Expected impact if hardware present: panic — local unprivileged DoS on VLV/CHV.**

## Reproduced?

No. The audit guest has no Intel GPU (virtio VGA only; `sysctl hw.drm` → unknown
oid; zero 0x8086 PCI devices — env.txt), so `intel_panel_setup_backlight()` /
`sysctl_backlight_handler()` are never reached. The deref chain is proven at
source level end-to-end:

## The chain (path:line)

1. **Unprivileged entry** — `intel_panel_setup_backlight()` registers
   `hw.backlight_level` as `CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY`
   (intel_panel.c:1884-1889) with `sysctl_backlight_handler` as the callback.
   ANY local user may write it (this is the same unprivileged entry DF-1411
   analyzed for its modeset-UAF; this finding is the *different*, stable-state
   NULL-crtc defect).

2. **Handler validation is insufficient for VLV** — `sysctl_backlight_handler`
   (:1811-1841) captures `conn_state = connector->base.state` (:1815), reads
   `max_brightness` under `backlight_lock`, accepts any `val` with
   `0 <= val <= max_brightness` (:1834), then takes `connection_mutex` and calls
   `intel_panel_set_backlight(conn_state, val, max_brightness)` (:1836). The
   connection_mutex does NOT imply the connector state has a crtc — mutex ≠ crtc.

3. **Missing guard** — `intel_panel_set_backlight()` (:1148-1170) checks only
   `panel->backlight.present` (:1156). Contrast `intel_panel_set_backlight_acpi()`
   (:620-654) which checks `!panel->backlight.present || !conn_state->crtc`
   (:634) with the explicit comment that crtc-less requests occur in practice.
   The static function is the porting leftover of the upstream hook that carries
   the same guard; the guard was dropped in this tree.

4. **enabled gate passes** — `panel->backlight.enabled` was seeded at setup from
   the BIOS PWM-enable bit (`vlv_setup_backlight`, :1643-1662:
   `panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE`) and is independent of any
   atomic commit; it stays true when the panel was lit at handoff and the
   connector never got a CRTC (or a readout left it unassigned).

5. **Deref** — `panel->backlight.set(conn_state, level)` dispatches to
   `vlv_set_backlight()` which executes `to_intel_crtc(conn_state->crtc)->pipe`
   (:581). `to_intel_crtc(NULL)` is a `container_of` around NULL; reading the
   `pipe` member at its small struct offset faults → kernel panic.

The read path is defended end-to-end (`intel_get_pipe_from_connector` returns
INVALID_PIPE for crtc==NULL, intel_display.c:14158-14168; `_vlv_get_backlight`
WARN_ONs, intel_panel.c:497-503) — the asymmetry confirms the set path is an
oversight, not a design decision.

## Exploit chain

Not a corruption primitive: deterministic near-NULL read → panic. Unprivileged
local DoS on VLV/CHV laptops in the crtc-less+enabled state. No route to uid0.

## Why not false-positive

* The deref instruction sequence is unconditional in `vlv_set_backlight`.
* The sibling `_acpi` variant carries the exact guard with a comment describing
  real-world occurrences of the state.
* DF-1411 (modeset-race UAF on the same sysctl) is a separate defect and was
  excluded from this analysis.

## Fix validation

fix.diff authored against the read-only tree (guard + WARN_ON in
vlv_set_backlight). Not runtime-testable on this guest (no i915 hardware);
`git apply --check` clean.

## Negative results this pass (intel_panel.c, classes hunted and killed)

* other controller setups: `cnp_setup_backlight` hardcodes controller=0
  (intel_panel.c:1719) — no VBT index; lpt/pch/i9xx/i965 use no controller
  field; bxt VBT 0-15 controller is DF-1412 (excluded, not re-filed).
* intel_backlight_device sysfs registration is dead code on DFly
  (`#if 0`, :1265-1281); `intel_backlight_device_unregister` likewise (:1288-1295).
* PWM divider math: every `*_hz_to_pwm` is only reached via
  `get_backlight_max_vbt()` which forces `pwm_freq_hz != 0` (:1446-1453);
  `pwm_freq_hz * mul` (u16 * ≤128) and `clock` fit u32/int — no div0, no
  overflow.
* DSI PWM path: DFly's `pwm_get()` stub returns NULL (linux/pwm.h:68-72) which
  passes the `IS_ERR()` check (:1754), but the follow-up
  `pwm_config(NULL,...)` stub returns -EINVAL (:34-39 of the same header) so
  `pwm_setup_backlight` fails closed (:1768-1775). No NULL-pwm escape.
* scaling arithmetic: `scale()` clamps inputs and uses u64 intermediates
  (:389-408); `min` can never exceed `max` (`get_backlight_min_vbt` clamps to
  [0..64] then scales into [0..max], :1482-1489); register writes are masked
  RMW (`& ~BACKLIGHT_DUTY_CYCLE_MASK`), so no adjacent-field bleed; i9xx
  combination `level * 0xfe` ≤ 32767*255*254 fits u32 (:561).
* sysctl node lifetime: nodes live in the DRM device sysctl ctx, which
  `drm_device_detach()` frees via `drm_sysctl_cleanup()` BEFORE driver/connector
  teardown (drm_dragonfly.c:250-256 → sysctl_ctx_free, drm_sysctl.c:109-117) —
  no sysctl-after-connector-free window at detach.
* `vlv_disable_backlight`'s crtc deref (:717) is only reached from encoder
  disable paths where old_conn_state always carries the crtc being disabled.

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