# DF-4839 — BUG_ON(num_connectors == 0) in drm_primary_helper_update() — Info / speculative (static-only)

## Claim

`drm_primary_helper_update()` (sys/dev/drm/drm_plane_helper.c:215-288) is the
default `update_plane` handler for the helper-created primary planes of every
driver that registers CRTCs with `drm_crtc_init()` — in this tree that is
**radeon** (`radeon_display.c`), **amdgpu** (`dce_v8_0.c`, `dce_v10_0.c`,
`dce_v11_0.c`, `dce_virtual.c`), and any module using the transitional KPI.
It is reached from the unprivileged-DRM-master `DRM_IOCTL_MODE_SETPLANE` path:

```
drm_mode_setplane()                     drm_plane.c:774
  setplane_internal()                   drm_plane.c:735   (all modeset locks held)
  __setplane_internal()                 drm_plane.c:647
    plane->funcs->update_plane == drm_primary_helper_update   drm_plane.c:690
      drm_plane_helper_check_update()   drm_plane_helper.c:246  (visible == true
                                    requires crtc->enabled, drm_atomic_helper.c:766-770)
      get_connectors_for_crtc(crtc,NULL,0)                    drm_plane_helper.c:264
      BUG_ON(num_connectors == 0);                            drm_plane_helper.c:265   <-- panic
```

`get_connectors_for_crtc()` (drm_plane_helper.c:73-101) counts connectors via
`connector->encoder && connector->encoder->crtc == crtc`. The BUG_ON fires
whenever `crtc->enabled == true` (checked moments earlier through the
`crtc_state.enable` built at drm_plane_helper.c:156-160) but **no** connector is
currently encoder-bound to that CRTC.

## Why this is only Info/speculative

On a correctly-functioning driver an enabled CRTC always has >= 1 bound
connector (the legacy set_config path refuses to enable a CRTC with zero
connectors), so the BUG_ON cannot be reached by well-formed ioctls alone.
However the corrupt state is exactly what the *already-filed*
DF-4469 (drm_crtc_helper.c racy `save_connector_encoders` sizing → OOB write
into `connector->encoder` on the fail path) and the fail-path encoder-unbind
behavior documented around DF-4470/DF-4471 can produce: an enabled CRTC left
with zero live connector bindings after a failed modeset. A second SetPlane
ioctl on that CRTC's primary plane then converts the latent inconsistency into
a hard kernel panic (BUG_ON) — a one-syscall local DoS for a DRM master.

Upstream evolution supports treating this as hardening rather than a bug fix:
this transitional-helper path (and its BUG_ON) has no surviving modern-kernel
equivalent; the whole primary-helper update path was retired as drivers moved
to atomic. Keeping a `BUG_ON` on a state invariant that the *core's own* fail
paths can violate is defense-in-depth debt.

## Disposition

- **Not reproduced live**: the audit QEMU guest has no DRM device
  (`ls /dev/dri* /dev/drm*` → ENOENT; only `vgapci0` QEMU std-VGA, no drm
  driver attaches), so no SetPlane ioctl can be issued at all. Static
  trace only.
- Severity **Info**, confidence **speculative** (needs DF-4469-class state
  corruption as setup, or any driver defect leaving an enabled
  connector-less CRTC).
- Fix: return `-EINVAL` instead of BUG_ON (see fix.diff). Trivially safe;
  no kernel-build validation performed because there is no reproduction
  baseline on this guest (`fix_status: not_applicable`).

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