# DF-1320 — Unbounded MST stream_count → stack overflow (amdgpu)

## Verdict
**INCONCLUSIVE (real bug, needs hardware + driver absent from guest).**
Source trace confirms the stack overflow; it cannot be reproduced on this
QEMU audit guest because **amdgpu is not in `X86_64_GENERIC`, is not a
loadable module, and there is no AMD GPU / MST hardware**.  The fix was
validated to apply cleanly (`git apply --check`) against
`sys/dev/drm/amd/display/dc/core/dc_link.c`.

## Mechanism

`update_mst_stream_alloc_table` copies a DRM-proposed payload table into a
fixed-size stack workspace:

```
sys/dev/drm/amd/display/dc/core/dc_link.c:2327  struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { 0 };
                                                                          // MAX_CONTROLLER_NUM = 6
sys/dev/drm/amd/display/dc/core/dc_link.c:2335  ASSERT(proposed_table->stream_count - ... < 2);  // compiles to no-op in production
sys/dev/drm/amd/display/dc/core/dc_link.c:2339  for (i = 0; i < proposed_table->stream_count; i++) {   // NO bounds check vs 6
sys/dev/drm/amd/display/dc/core/dc_link.c:2348      work_table[i] = *dc_alloc;       // *** OOB stack write for stream_count>6 ***
sys/dev/drm/amd/display/dc/core/dc_link.c:2355      work_table[i].vcp_id = ...;      // *** and here ***
sys/dev/drm/amd/display/dc/core/dc_link.c:2359      work_table[i].stream_enc = stream_enc;  // *** kernel pointer written ***
```

`MAX_CONTROLLER_NUM = 6` (`sys/dev/drm/amd/display/include/link_service_types.h:38`).
Each `link_mst_stream_allocation` is ~16 bytes (vcp_id + slot_count +
stream_enc pointer), so the 6-entry `work_table` is ~96 bytes on the
stack.  `proposed_table->stream_count` is built upstream by
`get_payload_table`:

```
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:150  for (i = 0; i < mst_mgr->max_payloads; i++) {   // max_payloads up to 63
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:157      if (... LOCAL || REMOTE) {
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:167          proposed_table->stream_count++;          // can reach up to 63
```

A malicious MST branch device (hub/dock) that advertises >6 payloads in
LOCAL/REMOTE state with non-zero `num_slots` drives `stream_count`
above 6, and the stack `work_table` overflows.  The overflow content
includes the kernel `stream_enc` pointer plus attacker-influenced
`vcp_id`/`slot_count`.  The `ASSERT` at 2335 is a no-op in production.

This is an **unauthenticated** physical-layer attack: hotplug a
malicious MST hub → payload negotiation → `dm_helpers_dp_mst_update_payload_allocator`
→ `update_mst_stream_alloc_table`.

## Reachability on this guest

Identical to DF-1319: amdgpu is absent from the kernel, no module
loaded, no AMD GPU, no MST hardware, no `/dev/dri`.  Phase-4(d): real
bug, unreachable on this guest.

## Exploit chain

Stack buffer overflow (write-capable) on a display-driver code path
driven by physical MST-layer attacker input — not a userspace-reachable
path on the audit guest.  Realistic impact ceiling on an amdgpu host:
stack corruption in `update_mst_stream_alloc_table` → potential kernel
RCE via a malicious MST peripheral.  No userspace escalation chain
applies.

## PoC changes

No runnable userspace PoC (MST-peripheral attack).  Folder was empty.
Authored `build.sh`/`run.sh` documenting the hardware-required
reproduction and validating `fix.diff` applies cleanly.

## Fix

`fix.diff` clamps `proposed_table->stream_count` to `MAX_CONTROLLER_NUM`
at the top of `update_mst_stream_alloc_table` (after the existing
`ASSERT`), invoking `BREAK_TO_DEBUGGER()` (already used in the file at
`:351`) on the over-count before truncating.  Surplus payloads a
malicious hub claims are dropped, and the fixed-size `work_table` can no
longer be overrun.  Applies cleanly (`git apply --check`).

## Fix validation

`fix_status: not_testable` — amdgpu not in the kernel build on this
guest, so neither bug nor fix is runtime/compile-exercisable here.
Validated by clean `git apply --check` and source inspection (the clamp
uses the existing `MAX_CONTROLLER_NUM` constant and `BREAK_TO_DEBUGGER`
macro).
