drm_atomic_helper: unguarded CRTC helper-callback dereferences β funcs->dpms/funcs->commit fallbacks and crtc->helper_private derefs lack the NULL guards of upstream fe6169288666/26564be56d73
Summary
The DFly DRM sync predates two upstream robustness fixes: disable_outputs() falls through to an unconditional funcs->dpms(crtc, DRM_MODE_DPMS_OFF) when prepare/atomic_disable/disable are all NULL, commit_modeset_enables() calls funcs->commit(crtc) unconditionally when atomic_enable is NULL, and mode_fixup()/disable_outputs() dereference crtc->helper_private without a !funcs guard. A driver registering a CRTC with a partial drm_crtc_helper_funcs table and using drm_atomic_helper_commit() gets a NULL indirect call (kernel panic) on the first CRTC disable/enable. Hardening divergence only: exhaustively verified that no in-tree driver can trigger it (i915/amdgpu_dm have their own commit tails; classic DCE tables set the fields; radeon is legacy-helper based). Worst case for a future/out-of-tree driver is a NULL function-pointer call = panic (DoS), not attacker-controlled IP.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3792 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| fix.diff | β | 1.3 KB | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw |
DF-3792 β Missing upstream fix fe6169288666 (+26564be56d73): unguarded
CRTC helper-callback dereferences in disable_outputs() /
commit_modeset_enables() / mode_fixup()
DragonFlyBSD sys/dev/drm/drm_atomic_helper.c (~v5.0-rc vintage) does not
carry two upstream robustness fixes for the CRTC helper-function table:
-
fe6169288666"drm/atomic-helper: Make atomic_enable/disable crtc callbacks optional" (v5.2 window, 2019-03): *disable_outputs()β upstream changed the final fallbackelse funcs->dpms(crtc, DRM_MODE_DPMS_OFF);toelse if (funcs->dpms). Local still calls it unconditionally:sys/dev/drm/drm_atomic_helper.c:1007-1014. *commit_modeset_enables()β upstream changedelse funcs->commit(crtc);toelse if (funcs->commit). Local:sys/dev/drm/drm_atomic_helper.c:1253-1260. A driver whosedrm_crtc_helper_funcsprovides none ofprepare/atomic_disable/disable/dpms(resp. neitheratomic_enablenorcommit) gets a NULL indirect call β kernel panic on any CRTC disable (resp. enable) through the generic commit path. -
26564be56d73"drm/atomic-helper: Make crtc helper funcs optional" (2019-09):mode_fixup()β upstreamif (!funcs || !funcs->mode_fixup); localsys/dev/drm/drm_atomic_helper.c:461-463dereferencescrtc->helper_privateunconditionally (if (!funcs->mode_fixup)). Same class indisable_outputs()itself (funcs = crtc->helper_private;thenfuncs->prepareread at:1007) β a NULLhelper_privatepanics before any callback dispatch.
Reachability in this tree (verified: no in-tree trigger)
- i915 β
intel_helper_funcs(sys/dev/drm/i915/intel_display.c:10939) sets only.atomic_begin/.atomic_flush/.atomic_check, i.e. it would hit the NULLfuncs->dpms/funcs->commitfallbacks β but i915 never reaches the generic helpers: it has its ownintel_atomic_commit/intel_atomic_commit_tail(intel_display.c:12787) which do not calldrm_atomic_helper_commit_modeset_disables/enablesordisable_outputs(). - amdgpu (DC) β
amdgpu_dm_crtc_helper_funcs(amdgpu_dm.c:3403) sets.disable; and amdgpu_dm has its own commit tail (amdgpu_dm_atomic_commit_tail,amdgpu_dm.c:4733) that also never calls the generic disable/enable loops. - amdgpu (classic DCE) β all
dce*_crtc_helper_funcsvariants set.dpms,.disable,.commit(e.g.dce_v8_0_crtc_helper_funcs,dce_v8_0.c:2562). - radeon β legacy CRTC-helper driver (does not use the atomic commit path that reaches these helpers).
So today this is a hardening divergence: any out-of-tree / future driver
that registers a CRTC with a partial helper table and uses
drm_atomic_helper_commit() panics the kernel on the first modeset. Upstream
considered the missing guards a real bug and fixed them; the DFly port simply
pre-dates the fix.
Reproduce (hypothetical driver β NOT possible on this guest)
Not executable on this guest: no DRM-capable GPU (see env.txt in
findings/poc/DF-3791/), and no in-tree driver triggers it. Source-level
proof only (diff against upstream fix commits).
Fix
Apply fix.diff (backport of the three upstream guard changes).
DF-3792 VERDICT
Status: inconclusive (runtime verification impossible on this guest β no
DRM device; also no in-tree trigger exists). Severity: Info (hardening).
Confidence: certain that the divergence exists (direct diff against
upstream fix commits fe6169288666 and 26564be56d73).
Narrative
Pass-2 diff of sys/dev/drm/drm_atomic_helper.c (4021 LOC) against upstream
v4.19/v5.0/v5.1/v5.2/v5.4 references shows the local file is an ~v5.0-rc
snapshot minus several later fixes. Three unguarded callback dereferences
were fixed upstream after this snapshot:
disable_outputs()CRTC loop β localsys/dev/drm/drm_atomic_helper.c:1007-1014:prepare/atomic_disable/disableall NULL β falls intofuncs->dpms(crtc, DRM_MODE_DPMS_OFF)with dpms possibly NULL β call through NULL β panic. Upstreamfe6169288666(v5.2 window) changed the tail toelse if (funcs->dpms).commit_modeset_enables()CRTC loop β local:1253-1260:atomic_enableNULL βfuncs->commit(crtc)with commit possibly NULL. Same upstream fix addedelse if (funcs->commit).mode_fixup()β local:461-463readsfuncs->mode_fixupwithfuncs = crtc->helper_privatepossibly NULL (upstream26564be56d73added!funcs ||). Same NULL-funcs exposure at:1007(new_crtc_state->enable && funcs->prepare).
These are NULL function-pointer calls (page-fault/panic), not attacker-
controlled IP β no exploitation path beyond DoS, and then only for a driver
that registers a CRTC with a partial drm_crtc_helper_funcs table and uses
the generic drm_atomic_helper_commit() machinery.
In-tree reachability: exhaustively checked, none
- i915
intel_helper_funcs(intel_display.c:10939-10944) is exactly the partial table that would trigger it β but i915 uses its ownintel_atomic_commit/intel_atomic_commit_tail(intel_display.c:12787) which never calldisable_outputs()/commit_modeset_enables(). - amdgpu_dm has its own commit tail (
amdgpu_dm.c:4733) and its CRTC helper table sets.disable(amdgpu_dm.c:3403-3407). - classic amdgpu DCE tables all set
.dpms/.disable/.commit(e.g.dce_v8_0.c:2562-2571), and classic amdgpu is legacy-helper based. - radeon is legacy-crtc-helper based (never reaches the atomic commit path).
Conclusion: Info β the guards protect against future/out-of-tree drivers; upstream treats them as required robustness (both commits are merged fixes).
Why inconclusive at runtime
Guest has QEMU std VGA only (pciconf: chip=0x11111234), no virtio_gpu
driver in the DFly tree, drm.ko not loaded, /dev/dri absent β no DRM
device exists to exercise any path in this file (full survey in
findings/poc/DF-3791/env.txt).
Exploit chain
None (NULL callback dispatch = panic at worst; no control over the called address).
Recommended fix
Apply fix.diff (backport of the three upstream guards).
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_testableNo DRM device / no triggering driver on the guest; guards are a straight backport of upstream-accepted fixes.
findings/poc/DF-3792/fix.diff (git-apply-able vs this tree, authored post-verification, never applied to sys/).
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md; README.md (in-tree reachability survey); fix.diff; env evidence in findings/poc/DF-3791/env.txt
PoC changes
No PoC produced: triggering requires a DRM driver with a partial crtc helper table; none exists in-tree and no DRM device exists on the guest.
Verified recommended fix
Backport the three NULL guards (see fix.diff in this pack).
Verdict
Source-level certain, runtime-inconclusive, Info severity: sys/dev/drm/drm_atomic_helper.c:1011-1014 calls funcs->dpms() unconditionally when prepare/atomic_disable/disable are all NULL, :1257-1260 calls funcs->commit() unconditionally when atomic_enable is NULL, and :461-463/:1007 dereference crtc->helper_private without a !funcs guard β all three guards were added upstream by fe6169288666 and 26564be56d73 (v5.2 window) and are missing from this ~v5.0-rc snapshot. In-tree drivers cannot trigger it (i915 and amdgpu_dm use their own commit tails; classic DCE/radeon set the hooks), so impact is limited to out-of-tree/future drivers with partial crtc helper tables: a NULL function-pointer call, i.e. kernel panic/DoS, not attacker-controlled IP. Guest has no DRM device at all (QEMU std VGA, no driver, /dev/dri absent), so nothing could be executed.
No comments yet.