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

1. **`fe6169288666`** "drm/atomic-helper: Make atomic_enable/disable crtc
   callbacks optional" (v5.2 window, 2019-03):
   * `disable_outputs()` — upstream changed the final fallback
     `else funcs->dpms(crtc, DRM_MODE_DPMS_OFF);` to
     `else if (funcs->dpms)`. Local still calls it unconditionally:
     `sys/dev/drm/drm_atomic_helper.c:1007-1014`.
   * `commit_modeset_enables()` — upstream changed
     `else funcs->commit(crtc);` to `else if (funcs->commit)`. Local:
     `sys/dev/drm/drm_atomic_helper.c:1253-1260`.
   A driver whose `drm_crtc_helper_funcs` provides none of
   `prepare`/`atomic_disable`/`disable`/`dpms` (resp. neither `atomic_enable`
   nor `commit`) gets a NULL indirect call → kernel panic on any CRTC
   disable (resp. enable) through the generic commit path.

2. **`26564be56d73`** "drm/atomic-helper: Make crtc helper funcs optional"
   (2019-09): `mode_fixup()` — upstream `if (!funcs || !funcs->mode_fixup)`;
   local `sys/dev/drm/drm_atomic_helper.c:461-463` dereferences
   `crtc->helper_private` unconditionally (`if (!funcs->mode_fixup)`).
   Same class in `disable_outputs()` itself (`funcs = crtc->helper_private;`
   then `funcs->prepare` read at `:1007`) — a NULL `helper_private` panics
   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 NULL `funcs->dpms`/`funcs->commit` fallbacks — but i915 never reaches
  the generic helpers: it has its own `intel_atomic_commit` /
  `intel_atomic_commit_tail` (`intel_display.c:12787`) which do not call
  `drm_atomic_helper_commit_modeset_disables/enables` or `disable_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_funcs` variants 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).
