Missing !conn_state->crtc guard in intel_panel_set_backlight: unprivileged hw.backlight_level sysctl write NULL-derefs to_intel_crtc(NULL)->pipe in vlv_set_backlight (VLV/CHV)
Summary
intel_panel_set_backlight is the only live caller-fed path into panel->backlight.set() without a conn_state->crtc check - its ACPI sibling carries exactly that guard with a comment documenting crtc-less states. On DragonFly the 'any time' caller is the DFly-specific sysctl hw.backlight_level (CTLFLAG_RW|CTLFLAG_ANYBODY) writable by any local user. On VLV/CHV the set hook executes to_intel_crtc(NULL)->pipe - container_of NULL member read -> page-fault panic. Preconditions: panel->backlight.enabled seeded true from the BIOS PWM-enable bit while the connector's atomic state has no crtc. The read side is fully defended; the set-side asymmetry is a porting oversight. Guard-mirror fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4161 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.7 KB | β raw | |
| VERDICT.md | β | 5.4 KB | β raw | |
| trigger.sh | β | 1.1 KB | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| env.txt | β | 926 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 3.7 KB | view raw |
DF-4161 β missing !conn_state->crtc guard in intel_panel_set_backlight β vlv_set_backlight NULL deref via unprivileged hw.backlight_level sysctl
File: sys/dev/drm/i915/intel_panel.c (pass 2)
What
intel_panel_set_backlight() (intel_panel.c:1148-1170) is the only live caller-fed
entry into panel->backlight.set() that does NOT check conn_state->crtc. Its ACPI
sibling intel_panel_set_backlight_acpi() (:620-654) carries exactly that guard
(:634) with the comment "Lack of crtc may occur during driver init because
connection_mutex isn't held across the entire backlight setup + modeset readout,
and the BIOS can issue the requests at any time." On DragonFly the "request at
any time" caller is the DFly-specific sysctl hw.backlight_level
(sysctl_backlight_handler, :1811-1841, CTLFLAG_RW | CTLFLAG_ANYBODY β writable
by ANY local user).
On VLV/CHV (Valleyview/Cherryview, non-DSI connectors) the selected .set hook is
vlv_set_backlight() (:577-586):
enum i915_pipe pipe = to_intel_crtc(conn_state->crtc)->pipe; /* :581 β NULL deref */
while the read side is defended (intel_get_pipe_from_connector returns
INVALID_PIPE for a NULL crtc, intel_display.c:14158-14168, and _vlv_get_backlight
WARN_ONs and returns 0, :497-503). No other .set implementation derefs
conn_state->crtc, which is why the guard's absence only bites VLV/CHV.
Preconditions (hardware state)
- VLV/CHV i915 with LVDS/eDP panel,
pm_method= register PWM (not DSI-pwm). panel->backlight.enabled == truewithconnector->base.state->crtc == NULL.enabledis seeded from the BIOS handoff at setup (panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE, :1662) β independent of any DRM atomic commit. A panel the BIOS left lit that was never (or is currently not) bound to a CRTC in the connector state satisfies both.presentis true after any successful setup (:1875).- The sysctl write must change the value (
val != user_level), be>= 0and<= max_brightness(:1834) β trivially arranged by any local user.
Trigger (to run on VLV/CHV hardware)
./trigger.sh
expects hw.backlight_level to exist; writes a fresh value as an unprivileged
user. On a vulnerable kernel the expected result is a kernel page fault at
to_intel_crtc(NULL)->pipe inside vlv_set_backlight (near-NULL read) β panic.
Status on this audit guest
Hardware-gated: the QEMU guest has virtio VGA only (no 0x8086 display device,
hw.drm/hw.backlight_level unknown oids β see env.txt). Per the DF-4022 family
convention the finding is source-certain / runtime-inconclusive.
Fix
See fix.diff: mirror the _acpi guard in intel_panel_set_backlight() and add a
defensive WARN_ON(!conn_state->crtc) in vlv_set_backlight().
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)
-
Unprivileged entry β
intel_panel_setup_backlight()registershw.backlight_levelasCTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY(intel_panel.c:1884-1889) withsysctl_backlight_handleras 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). -
Handler validation is insufficient for VLV β
sysctl_backlight_handler(:1811-1841) capturesconn_state = connector->base.state(:1815), readsmax_brightnessunderbacklight_lock, accepts anyvalwith0 <= val <= max_brightness(:1834), then takesconnection_mutexand callsintel_panel_set_backlight(conn_state, val, max_brightness)(:1836). The connection_mutex does NOT imply the connector state has a crtc β mutex β crtc. -
Missing guard β
intel_panel_set_backlight()(:1148-1170) checks onlypanel->backlight.present(:1156). Contrastintel_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. -
enabled gate passes β
panel->backlight.enabledwas 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). -
Deref β
panel->backlight.set(conn_state, level)dispatches tovlv_set_backlight()which executesto_intel_crtc(conn_state->crtc)->pipe(:581).to_intel_crtc(NULL)is acontainer_ofaround NULL; reading thepipemember 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
_acpivariant 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_backlighthardcodes 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_unregisterlikewise (:1288-1295). - PWM divider math: every
*_hz_to_pwmis only reached viaget_backlight_max_vbt()which forcespwm_freq_hz != 0(:1446-1453);pwm_freq_hz * mul(u16 * β€128) andclockfit u32/int β no div0, no overflow. - DSI PWM path: DFly's
pwm_get()stub returns NULL (linux/pwm.h:68-72) which passes theIS_ERR()check (:1754), but the follow-uppwm_config(NULL,...)stub returns -EINVAL (:34-39 of the same header) sopwm_setup_backlightfails closed (:1768-1775). No NULL-pwm escape. - scaling arithmetic:
scale()clamps inputs and uses u64 intermediates (:389-408);mincan never exceedmax(get_backlight_min_vbtclamps 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 combinationlevel * 0xfeβ€ 32767255254 fits u32 (:561). - sysctl node lifetime: nodes live in the DRM device sysctl ctx, which
drm_device_detach()frees viadrm_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.
Fix verification
not_testablefix.diff (guard at :1156 + WARN_ON at vlv_set_backlight) applies clean via git apply --check; cannot be runtime-validated without VLV/CHV hardware.
findings/poc/DF-4161/fix.diff
Confirmed kernel references
Detail
Exploit chain
On VLV/CHV hardware: any local user writes hw.backlight_level (value != current, 0<=val<=max) while the BIOS-lit panel's connector state has no crtc -> near-NULL read -> deterministic panic. DoS only; no write primitive, no uid0 route.
Evidence (decisive lines)
VERDICT.md (path:line chain + pass-2 negative results); env.txt (hardware absence + md5 cfe495b2207583b24f9c7c5714b38389 == audited tree); trigger.sh (exact unprivileged command); fix.diff (guard + WARN_ON, git apply --check clean)
PoC changes
No seed PoC. trigger.sh authored this pass (documents the unprivileged sysctl command + skip condition for guests).
Verified recommended fix
Mirror the _acpi guard: add 'if (!conn_state->crtc) return;' to intel_panel_set_backlight (+WARN_ON belt-and-braces in vlv_set_backlight), see fix.diff.
Verdict
Source-level certain deref chain, runtime hardware-gated (inconclusive per family convention). sysctl_backlight_handler (intel_panel.c:1811-1841) accepts writes from ANY local user (CTLFLAG_RW|CTLFLAG_ANYBODY, registered at :1884-1889) and calls intel_panel_set_backlight(conn_state, val, max) at :1836. intel_panel_set_backlight (:1148-1170) checks ONLY panel->backlight.present (:1156) -- it is missing the '!conn_state->crtc' guard its ACPI sibling intel_panel_set_backlight_acpi carries at :634 with a comment documenting that crtc-less states occur in practice. With panel->backlight.enabled seeded true from the BIOS handoff (vlv_setup_backlight :1662, enabled = ctl2 & BLM_PWM_ENABLE, independent of any atomic commit) and a connector state carrying no crtc, the dispatch reaches vlv_set_backlight (:577) which executes to_intel_crtc(conn_state->crtc)->pipe at :581 -> container_of(NULL) member read -> page-fault panic. Read side is fully defended (intel_get_pipe_from_connector returns INVALID_PIPE, intel_display.c:14158-14168; _vlv_get_backlight WARN_ONs, :497-503) -- the set-side asymmetry is the oversight. VLV/CHV (non-DSI) only: no other .set hook derefs conn_state->crtc. Guest: virtio VGA only, zero 0x8086 PCI devices, hw.drm/hw.backlight_level unknown oids (env.txt) -> not runtime-testable. Distinct from DF-1411 (modeset-race UAF on the same sysctl) and DF-1412 (bxt VBT controller OOB MMIO); neither re-filed.
No comments yet.