# DF-3881 — unbounded MST topology depth (LCT) → OOB stack/heap writes + kernel-stack exhaustion

**File:** `sys/dev/drm/drm_dp_mst_topology.c` (pass 2)
**Class:** kernel memory corruption (CWE-787) driven by a malicious DisplayPort MST hub
**Severity:** High / confidence: certain (source-level; hardware-gated on this guest)

## What

Nothing in `drm_dp_mst_topology.c` bounds the Link Count Total (`lct`) of a
discovered MST branch chain. `lct` grows by exactly +1 per nested branch
device (`drm_dp_calculate_rad()` returns `parent_lct + 1`,
`drm_dp_port_setup_pdt()` installs that as the child's lct), and each newly
discovered MST_BRANCHING port triggers a nested `drm_dp_send_link_address()`
(`drm_dp_add_port()` → `drm_dp_port_setup_pdt()` → `drm_dp_send_link_address(port->mstb)`),
so a hub that *claims* to be a chain of N nested branches drives `lct` to N+1
with only N sideband round trips. Every buffer indexed off `lct` is fixed-size:

| sink | buffer | first OOB | what is written |
|---|---|---|---|
| `drm_dp_calculate_rad()` :1052-1066, called from `drm_dp_port_setup_pdt()` :1074 | stack `u8 rad[6]` | parent_lct ≥ 13 | `memcpy(rad, parent->rad, idx+1)` with idx+1 = 7..8 (1-2 bytes past) **and** `rad[idx] |= port_num << shift` with idx up to 127 (4-bit port number at attacker-chosen stack offset) |
| `drm_dp_add_mst_branch_device()` :848-850 | heap `mstb->rad[8]` (`drm_dp_mst_helper.h:125`) | lct ≥ 17 | `memcpy(mstb->rad, rad, lct/2)` — up to 127 bytes into a freshly kzmalloc'd `drm_dp_mst_branch` (heap overflow with stack-garbage + hub-derived bytes) |
| `set_hdr_from_dst_qlock()` :1484-1485 | stack `hdr->rad[8]` (`drm_dp_mst_helper.h:150`) | lct ≥ 17 | `memcpy(hdr->rad, mstb->rad, lct/2)` on every down-req to the deep device |
| `drm_dp_mst_dump_mstb()` :2958-2964 | stack `char prefix[10]` | lct ≥ 10 | `prefix[i]='\t'` for i < lct (root-only debugfs sink, radeon_dp_mst.c:775) |
| `drm_dp_send_link_address()` :1639 → `drm_dp_add_port()` :1196-1198 | kernel thread stack | depth ≈ 54 | unbounded synchronous recursion, one `send_link_address+add_port` frame-set per level, 16KB DFBSD kernel stack |

## Threat model

Attacker = malicious/compromised DP hub, dock or monitor firmware (same actor
as DF-1264/DF-1265). No host privilege required; the topology is enumerated
automatically on HPD. Each level costs the hub one LINK_ADDRESS reply it
fabricates locally, so a single $ device can simulate an arbitrarily deep
chain. A 12-hub-deep claim already corrupts the `drm_dp_port_setup_pdt()`
stack frame; ~16-hub-deep corrupts the heap object; ~54-deep exhausts the
kernel stack (each level's wait is synchronous in the topology worker).

## PoC

Hardware-gated: the QEMU guest has no GPU (`/dev/dri` absent, no drm module
loaded — see `env.txt`), so the kernel path cannot be driven here.
`mst_lct_model.c` is a faithful arithmetic model of the four sinks; build with
`./build.sh`, run with `./run.sh` (as unprivileged user). Expected: the table
of first-OOB depths (13 / 17 / 17 / 10) and the stack-exhaustion depth
estimate — see `run.log`.

## Fix

Bound the topology depth at the point where it is computed and where the RAD
copies happen (`fix.diff`): reject `lct > 16` in `drm_dp_add_mst_branch_device()`
(which also stops the recursion, because `port_setup_pdt()` only recurses when
`port->mstb` was created), refuse to compute a RAD for `parent_lct > 12` in
`drm_dp_calculate_rad()` (rad[6] holds at most 12 nibbles), and size
`prefix[]` for the maximum legal lct in `drm_dp_mst_dump_mstb()`.
