# VERDICT — DF-3885

**status: inconclusive** (kernel path hardware-gated) / impact assessed: dos /
confidence: likely

## Why the blowup is real

1. `drm_pick_crtcs()` (drm_fb_helper.c:2422-2495) recurses once at :2440 and
   once per CRTC at :2483-2484 → T(N) = (C+1)·T(N-1) with C = crtc_count
   whenever `modes[n] != NULL` for all n. There is no call budget anywhere.
2. `drm_setup_crtcs()` calls it at :2602 with `fb_helper->lock` held
   (lockdep_assert at :2565), from `drm_fb_helper_hotplug_event()` (:2870)
   and `__drm_fb_helper_initial_config_and_unlock` (:2717).
3. Connector count is unbounded at runtime:
   `__drm_fb_helper_add_one_connector()` (:140-175) kreallocs and appends on
   every connector registration. MST drivers register connectors per hub
   port: intel_dp_mst.c:497, radeon_fb.c:440, amdgpu_dm_mst_types.c:414.
   Combined with the unbounded topology enumeration in
   drm_dp_mst_topology.c (see DF-3881 — same root: no depth/count cap on hub
   claims), a malicious hub reaches 20-40 mode-carrying connectors.
4. Lock consequence: `fb_helper->lock` stays held; `lastclose`
   (drm_fb_helper.c:3005-3009), `set_par` (:1804), `blank` (:691 → dpms →
   :673), `ioctl` (:1585) all block — the DRM master close path and console
   blanking wedge behind a permanently-running worker.

## Measured on the guest

`pick_crtcs_model.c` (cc -O2, guest `cc 8.3`): 5^12 = 305,175,781 calls in
747 ms — a clean ×5 per added connector (run.log). The first version of the
model ran to 15 connectors: **5^15 ≈ 3×10^10 calls outran the 120 s SSH
command timeout in optimized userland C** — the kernel version does strictly
more work per call (kcalloc at :2444, memcpy, pointer chasing). 20 connectors
≈ 9.5×10^13 calls ⇒ days. Model output and the timed-out run both recorded.

## Why `inconclusive`, not `reproduced`

Demonstrating the kernel behavior needs the DRM device to exist (connector
add + hotplug event). The QEMU guest has no GPU — `/dev/dri` absent, no drm
kld (env.txt) — so `drm_pick_crtcs()` cannot be entered at all, let alone
with an inflated connector count. The userspace model proves the complexity
curve, not the kernel lock-hold. Per the honest-status table this is
`inconclusive` (feature absent on guest) with impact assessed as `dos`.

## Negative results recorded (same area, killed)

- per-CRTC `modeset->connectors[max_conn_count]` overflow (`:2633` +
  init at `:886-889`, i915 max_conn_count=4): **unreachable** — every
  MST-capable in-tree driver is multi-CRTC (clone assignment is skipped for
  `crtc_count > 1` at :2474), and i915's `intel_fb_initial_config` bails on
  clone configs (intel_fbdev.c:439-444). Single-CRTC + >4 connectors needs an
  MST-capable single-pipe part that doesn't exist in this tree.
- `BIT_ULL(connector_count)` wrap at :2342: logic-only for counts ≥ 64.
- deferred-io fault path, sys/cfb dirty wrappers, setcmap/pan_display/
  check_var bodies: compiled out (`#if 0`) in this DragonFly port — no
  attack surface there.
- fbdev user copies: `fb_{read,write}` wrappers are `#if 0`'d; the live
  fbdev glue (register_framebuffer at :2750) has no copyout of kernel
  memory.

## Fix

`fix.diff` — budget-threaded recursion (2^20 invocations, keep best-so-far on
exhaustion). Applies cleanly to the pristine tree (patch --dry-run).
Kernel-side fix validation impossible on this guest (no GPU):
`fix_status: not_testable`.

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