# DF-1177 — VERDICT

**Verdict: REPRODUCED (source+harness, module-build-validated).** Not `uid=0`
on the audit guest — this is a **memory-corruption primitive (OOB write into
VRAM BO) plus a NULL-deref DoS**, but the realistic trigger paths require root,
a malicious hypervisor, OR (for the NULL-deref) a Raven APU with missing
optional mec2 firmware. The OOB-write privilege boundary is absent on this
guest; the NULL-deref is a real DoS on misconfigured hardware.

## Bug confirmation

`rv_init_cp_jump_table()` at `sys/dev/drm/amd/amdgpu/gfx_v9_0.c:1068-1128`
iterates `me = 0..4` over five firmware blobs (CE/PFP/ME/MEC/MEC2) and copies
their jump tables into the `cp_table` BO:

```c
/* :1077 */  dst_ptr = adev->gfx.rlc.cp_table_ptr;
/* :1078 */  for (me = 0; me < 5; me++) {
                ... table_size = le32_to_cpu(hdr->jt_size);   /* u32 from disk */
/* :1121 */      for (i = 0; i < table_size; i++) {
/* :1122 */          dst_ptr[bo_offset + i] =
/* :1123 */              le32_to_cpu(fw_data[table_offset + i]);
                }
                bo_offset += table_size;
             }
```

### Bug 1 — `jt_size` unbounded → OOB write past cp_table BO + OOB read past firmware

The `cp_table` BO is allocated at `:1178-1183` with size
`ALIGN(96*5*4, 2048) + (64*1024)` = `67584` bytes = **16896 u32 dwords**.
`jt_size` (`amdgpu_ucode.h:83`, `uint32_t` from each firmware header) is used
as the inner loop bound with no validation against the remaining BO capacity
(`16896 - bo_offset`), and `jt_offset + jt_size` is not validated against
`fw->datasize`. A large `jt_size` therefore writes past the BO mapping into
adjacent VRAM and reads past the firmware buffer.

### Bug 2 — `me == 4` dereferences `mec2_fw->data` unconditionally → NULL deref

The `me == 4` branch at `:1111-1118` reads `adev->gfx.mec2_fw->data` with no
NULL check. `mec2_fw` is set to NULL at `:744` when `request_firmware` for the
optional mec2 firmware fails:

```c
/* :730-745 */
err = request_firmware(&adev->gfx.mec2_fw, fw_name, adev->dev);
if (!err) { ... } else {
    err = 0;
    adev->gfx.mec2_fw = NULL;     /* optional firmware absent */
}
```

`rv_init_cp_jump_table` runs unconditionally with `max_me = 5`, so any Raven
APU (`asic_type == CHIP_RAVEN`, gated at `:1176`) with missing optional mec2
firmware dereferences NULL at `:1113` → **immediate kernel panic**.

## Reproducibility on the audit guest

`amdgpu` is `optional` (`sys/conf/files:2492+`); not in `X86_64_GENERIC`;
QEMU guest has no AMD GPU. The `amdgpu.ko` module cannot initialize without
AMD hardware, so a live trigger is not possible on this guest.

A userspace harness (`harness.c`) reconstructs the 16896-dword cp_table loop
and demonstrates both bugs:

```
=== Case 1: large jt_size in CE firmware -> OOB write past cp_table ===
  CE jt_size = 25088 (cp_table = 16896 dwords) -> overflows by 8192 dwords
  BUG: OOB write faulted (signal 11) -- kernel equivalent: page fault past cp_table BO mapping

=== Case 2: me==4 dereferences mec2_fw->data with mec2_fw==NULL ===
  BUG: NULL deref of mec2_fw (signal 11) -- kernel equivalent: panic at gfx_v9_0.c:1113
```

## Module build validation

The fix.diff was applied in-guest to `/usr/src/sys/dev/drm/amd/amdgpu/gfx_v9_0.c`
and `make` compiled the patched `gfx_v9_0.c` cleanly
(`/usr/obj/usr/src/sys/dev/drm/amd/amdgpu/gfx_v9_0.o` produced, no warnings,
`-Werror`). The full amdgpu.ko link step fails on an **unrelated** pre-existing
issue (`color_gamma.o: file is empty`); not related to our patch. Source was
reverted after validation.

## Exploit chain

`blocked by valid hard blocker (root-only reachability for the OOB write;
hardware/operations-config for the NULL deref)`:

- **OOB write (Bug 1)**: triggerable only via crafted firmware. Firmware lives
  in root-owned `/lib/firmware/` and is loaded by the kernel only at GPU probe
  time, which itself requires AMD hardware absent on the QEMU guest. Realistic
  trigger paths: (a) root → kernel (no boundary to cross), (b) malicious
  hypervisor presenting forged firmware, (c) misconfigured system with
  world-writable `/lib/firmware`. No unprivileged local path on the audit
  guest.

- **NULL deref (Bug 2)**: triggerable on **any** Raven APU system where the
  optional mec2 firmware file is absent at boot. This is an operational
  misconfiguration DoS rather than an attack, but it is real and would panic
  the kernel on every boot of such a system.

Concrete next iteration if an unprivileged firmware-write path existed: the
OOB write lands in the cp_table VRAM BO. The destination is GPU VRAM, not
host kernel heap, so a uid=0 escalation would require a separate primitive
that turns a VRAM corruption into a host-kernel corruption (out of scope for
a direct chain). For the NULL deref there is no chain — it is a one-shot DoS.

## Fix

`fix.diff`:
1. Skips `me == 4` when `mec2_fw == NULL` (prevents Bug 2).
2. Adds an `else { continue; }` to the me-switch for safety.
3. Before each copy loop, validates `jt_size <= 16896 - bo_offset` and
   `jt_offset + jt_size <= fw->datasize/4` (prevents Bug 1).

`git apply --check` passes; patched source compiles cleanly into
`gfx_v9_0.o` on the guest.

## Threat model

Same class as DF-1134 (gfx_v7) and DF-1164 (gfx_v8). Severity Medium: OOB
write requires privileged firmware placement; NULL deref is a real DoS on
misconfigured Raven APU systems.
