DF-4200 / verdict.json
{ "finding_id": "DF-4200", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "amdgpu_connector_dp_detect calls encoder_funcs->detect without a NULL check at amdgpu_connectors.c:1387. The DP-bridge branch is entered for Travis/Nutmeg bridge connectors; best_single_encoder (:1335) returns the FIRST possible encoder, which is the external bridge encoder whenever the VBIOS object table enumerates it first (amdgpu_encoders.c:45-56 attaches in list order) \u2014 and dce_v{8,10,11}_0_ext_helper_funcs deliberately have NO .detect (no detect for TMDS/LVDS yet, dce_v10_0.c:3411-3418). Failing aux/DDC probe (:1383) + default-true dac_load_detect for DP-bridge VGA/DVI-I connectors (:1599,:1646) then makes :1387 a call through a NULL function pointer = kernel panic on probe. The sibling dvi_detect guards the identical pattern with if (encoder_funcs->detect) at :1082-1083 \u2014 the guard here was omitted. Not reproduced at runtime: audit guest has no AMD GPU (no /dev/dri, no AMD/ATI PCI \u2014 env.txt); trigger built and ran on the guest, hardware-gated. Same unguarded call exists in upstream Linux.", "exploit_chain": "unprivileged hotplug/poll cycle (or DRM_IOCTL_MODE_GETCONNECTOR probe) on a Travis/Nutmeg DP-bridge laptop whose VBIOS enumerates the bridge encoder first, with nothing attached (DDC probe fails) -> NULL fn-ptr call -> kernel panic", "evidence": "VERDICT.md (line-by-line chain incl. the three dce ext_helper_funcs lacking .detect); trigger_df4200.c + run.log (guest run, hardware-gated); fix.diff (guard mirroring :1083); env.txt", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1335", "sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1383", "sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1385-1388", "sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1082-1083", "sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1599", "sys/dev/drm/amd/amdgpu/dce_v10_0.c:3411-3418", "sys/dev/drm/amd/amdgpu/amdgpu_encoders.c:45-56" ], "poc_changes": "trigger written from scratch (raw ioctls, self-contained struct defs, no libdrm); __u32/__u64 typedefs added after first guest compile", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 10, "guest_dirty": 0, "build_cmd": "./build.sh (guest cc -O2 -Wall)", "run_cmd": "./run.sh", "code_hash": "38e70424e71a3d1e992070b1e1bc3a55666dc7f923be8958a3dbcc57a211228b", "notes": "Hardware-gated: guest has no amdgpu device (env.txt). Pass-2 checklist (f) MST hooks: absent from this file (killed). Checklist (b) hotplug/detect EDID races: serialized by dev->mode_config.mutex (amdgpu_irq.c:88, drm_helper probe paths) \u2014 killed. Checklist (c) DP link-status copies: live in atombios_dp.c, not this file \u2014 out of scope. Checklist (d) connector-add error paths beyond DF-1441: drm_connector_init/register return codes unchecked but DF-4195 covers the drm_connector.c side; no new amdgpu-side defect beyond knowns.", "recommended_fix": "Guard the call like the sibling dvi_detect path: if (encoder_funcs->detect) ret = encoder_funcs->detect(encoder, connector); (see fix.diff)", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applies cleanly to a pristine copy of the real amdgpu_connectors.c (verified, cumulative with DF-4201/4202 diffs) and mirrors the in-file guard at :1083; kernel-build validation not possible on this guest (no AMD GPU).", "fix_evidence": "fix.diff; local patch --dry-run + applied-copy verification noted in VERDICT.md" } |