# DF-3988 VERDICT — source-level certain, runtime hardware-gated (inconclusive)

## Root cause (path:line)

`dc_link_dp_set_test_pattern` — dc_link_dp.c:2588-2740:

```c
2595: struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx;
2596: struct pipe_ctx *pipe_ctx = &pipes[0];
2605: for (i = 0; i < MAX_PIPES; i++) {
2606:         if (pipes[i].stream->sink->link == link) {   /* no NULL check */
2607:                 pipe_ctx = &pipes[i];
2608:                 break;
2609:         }
2610: }
```

`pipe_ctx[].stream` is NULL for every inactive pipe: after boot with no
display commit, for all pipes during DPMS-off/idle, and for holes left in
the array after unplug/re-plug re-arrangements (e.g. stream on pipe1 while
pipe0 is free). The function is reachable from a sink-initiated automated
test with NO stream on the tested link:

  HPD short pulse → `dc_link_handle_hpd_rx_irq` :2045-2054
  (AUTOMATED_TEST bit) → `handle_automated_test` :1990 (LINK_TEST_PATTRN)
  → `dp_test_send_link_test_pattern` :1957 / or :1994 (PHY_TEST_PATTERN)
  → `dp_test_send_phy_test_pattern` :1909 → `dc_link_dp_set_test_pattern`.

A malicious sink/dongle (or a compliance test box hitting during a
no-commit window, or any tested link whose pipe sits after an inactive
hole) reaches :2606 with `pipes[0].stream == NULL` → `NULL->sink` →
kernel panic.

Second defect in the same function, :2653:

```c
pipes->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc);
```

This uses `pipes` (i.e. `&pipes[0]`) — NOT the matched `pipe_ctx` — for the
object deref (wrong pipe: blanks pipe 0's stream encoder regardless of
which pipe carries the tested link), and dereferences its stream_enc
without a NULL check (inactive pipe 0 → second NULL deref), while passing
`pipe_ctx`'s (possibly different) stream_enc as the argument. Discovered
while auditing the automated-test surface for pass 2; both defects are in
the sink-reachable test-pattern path.

## Distinguishing from DF-1319/DF-3987

DF-1319 = lane_count stack overflow in LT helpers. DF-3987 = poisoned
cur_link_settings indexed past lane arrays. This finding is a NULL-deref /
wrong-object deref on the pipe array — independent of lane_count, reachable
even with byte-perfect DPCD data (a compliant test appliance triggering a
link test pattern with no active stream is enough).

## Verification on the guest (honest)

- trigger.c models the exact scan shape (NULL hole at [0], match at [1]):
  builds and runs on the guest (run.log, RC=0, NULL-DEREF reported at i=0).
- Real chain hardware-gated: zero 0x1002 PCI devices, amdgpu.ko not loaded
  (env.txt). Status inconclusive per family convention.
- Guest src md5 matches audited tree (env.txt).

## Threat model / impact

Malicious DP sink/dongle (physical) OR benign compliance-test traffic at an
unlucky instant (no active stream / pipe hole) → deterministic NULL-deref
panic of the kernel thread handling the HPD work. Local DoS; no info leak,
no write primitive. Severity Medium (matches DF-1319 family threat model,
lower impact class).

## Fix

fix.diff: NULL-check `pipes[i].stream != NULL && pipes[i].stream->sink !=
NULL` in the scan; blank the matched `pipe_ctx` with a stream_enc NULL
check. git-apply clean; not runtime-validable on this guest.

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