# DF-4942 — hw_translate_dce80.c: GPIOPAD decode leaks en≤30 into the 8-entry DDC register arrays (OOB read → wild MMIO programming)

File audited (pass 2): `sys/dev/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c`
Lines: 164-167 (mmGPIOPAD_A case), 172-196 (DDC cases with no `*id`), consumer chain in `gpio_service.c:408-457`, `hw_factory_dce80.c:84-104,114-137`.

## What to run

```
./build.sh          # compiles harness_dce80.c (verbatim copy of offset_to_id/id_to_offset,
                    #  real DCE8 register constants) and the fixed variant
./run.sh            # runs both; see run.log (baseline) and fixrun.log (after fix.diff)
```

## Expected (baseline, reproduced on the DF 6.5-DEVELOPMENT guest, gcc 8.3)

```
== T2: GPIOPAD offset succeeds with en up to 30 (DF-4942 producer) ==
offset_to_id(mmGPIOPAD_A,1<<20) = true id=GPIO_ID_GPIO_PAD(4) en=20
  -> BUG(DF-4942): success with en=20 >= 8 entries: define_ddc_registers reads
     ddc_data_regs[20] OOB (hw_factory_dce80.c:122), regs then drive MMIO
```

T3 (negative control) in the same output proves the GPIOPAD bound *inside*
offset_to_id is sound (31/UNKNOWN=32/multi-bit rejected) — the defect is the
cross-domain hand-off, not the internal bound.

## Expected (after fix.diff)

```
  -> FIXED: translate still decodes en=20, but fixed consumer guard
     (id != GPIO_ID_DDC_DATA) rejects: accepted=0 -> no ddc_data_regs[20] OOB
0 regressions after fix (expect 0)
```

## Full kernel chain (code-trace; not executable in QEMU — no AMD ASIC)

1. VBIOS `ATOM_GPIO_I2C_INFO.asGPIO_Info[line].usClkA_RegisterIndex` (arbitrary u16, `bios_parser.c:1881-1882`) → `dal_gpio_create_ddc(svc, 0x183 /*mmGPIOPAD_A*/, 1<<shift, …)` (`dc_link_ddc.c:272-276`).
2. `offset_to_id(0x183, 1<<shift)` → **true**, `*id=GPIO_ID_GPIO_PAD`, `*en=shift∈[0..30]` (hw_translate_dce80.c:164-167).
3. `dal_gpio_create_ddc` **ignores `id`** and creates `GPIO_ID_DDC_DATA/CLOCK` gpio objects with `en` (gpio_service.c:418-437).
4. First DDC use → `dal_gpio_service_open` → `define_ddc_registers(pin, en)` → `ddc_data_regs[en]` / `ddc_clk_regs[en]` — arrays of **8** entries (hw_factory_dce80.c:84-104) → for en 8..30 an **OOB read of const .rodata**, pointer-stored into `ddc->regs`/`ddc->base.regs` (hw_factory_dce80.c:122-127).
5. `pin->funcs->open`/`set_config` → `REG_UPDATE`/`REG_GET` on `regs->…` (hw_ddc.c/hw_gpio.c reg_helper) → MMIO R/W at garbage register offsets → panic/MCE or wrong-register corruption of display-block state.

## Impact ceiling

Deterministic OOB (no uninitialized-stack dependency) given one crafted u16 in
the VBIOS image at amdgpu attach. Not reachable by an unprivileged user on
stock hardware (VBIOS is GPU firmware); realistic preconditions: re-flashed
VBIOS, corrupted firmware, or a host-supplied ROM in GPU-passthrough setups.
Impact: kernel panic / device-state corruption (DoS class). No RAM write
primitive (addresses land in MMIO space).
