# DF-4200 — NULL `encoder_funcs->detect` call in amdgpu_connector_dp_detect

**File:** `sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1385-1388` (pass 2)
**Severity:** Low (hardware/VBIOS-gated kernel NULL-function-pointer panic)
**Status:** not reproduced at runtime — hardware-gated (no amdgpu ASIC on
the audit guest); source-level certainty.

## Root cause

```c
} else if (amdgpu_connector->dac_load_detect) {          /* :1385 */
	const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
	ret = encoder_funcs->detect(encoder, connector);     /* :1387 — no NULL check */
}
```

This is the DP-**bridge** branch (`amdgpu_connector_encoder_get_dp_bridge_encoder_id()
!= ENCODER_OBJECT_ID_NONE`, i.e. a Travis/Nutmeg DP-to-VGA/HDMI bridge
laptop). `encoder` comes from `amdgpu_connector_best_single_encoder()`
(:1335) which returns the **first** possible encoder, and possible encoders
are attached in `encoder_list` order (`amdgpu_encoders.c:45-56`), i.e. in
ATOMBIOS object-table order. The **external bridge encoders** — exactly the
TRAVIS/NUTMEG devices that make this branch reachable — are initialized
with `dce_v{8,10,11}_0_ext_helper_funcs`, which deliberately contain **no
`.detect` member** ("no detect for TMDS/LVDS yet", dce_v10_0.c:3411-3418).
If the external bridge is enumerated before the internal UNIPHY encoder,
`best_single_encoder` hands back the ext encoder and :1387 calls NULL →
instant kernel panic on the first forced probe where the aux/DDC probe at
:1383 fails (nothing attached / adapter without DDC) and `dac_load_detect`
is true — and it defaults to **true** for DP-bridge VGA and DVI-I
connectors (amdgpu_connectors.c:1599, :1646).

The sibling path in the same file proves the omission: `dvi_detect` guards
the identical pattern with `if (encoder_funcs->detect)`
(amdgpu_connectors.c:1083). Upstream Linux carries the same unguarded call.

## Reach

Unprivileged-with-display-ownership: any hotplug/poll cycle
(`drm_kms_helper_poll`, HPD IRQ work amdgpu_irq.c:80-94) or a
`DRM_IOCTL_MODE_GETCONNECTOR` probe from the display server — no DRM master
needed for the probe itself.

## Evidence

- `trigger_df4200.c` — self-contained (no libdrm) probe trigger; builds and
  runs on the guest; guest has no AMD GPU (`env.txt`: no `/dev/dri`, no
  AMD/ATI PCI devices) so the amdgpu path is unreachable there.
- `fix.diff` — one-guard fix mirroring :1083.

## Fix

```diff
 			else if (amdgpu_connector->dac_load_detect) { /* try load detection */
 				const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-				ret = encoder_funcs->detect(encoder, connector);
+
+				if (encoder_funcs->detect)
+					ret = encoder_funcs->detect(encoder, connector);
 			}
```
