# DF-4944 — hw_translate_dce80.c: id_to_offset GPIO_PAD branch compares the derived MASK against the pin INDEX bound (valid pads 5..30 rejected; en≥32 hits UB shift and is accepted)

File audited (pass 2): `sys/dev/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c`
Lines: 378-382.

```c
case GPIO_ID_GPIO_PAD:
        info->offset = mmGPIOPAD_A;
        info->mask = (1 << en);
        result = (info->mask <= GPIO_GPIO_PAD_MAX);   /* mask vs index! */
break;
```

`GPIO_GPIO_PAD_MAX` is the largest valid pin **index** (30,
gpio_types.h:156), but the code compares the derived **mask** (1<<en) against
it. Two consequences, both reproduced on the DF guest:

## What to run / expect

```
./build.sh && ./run.sh     # harness_dce80.c T4 section (run.log)
```

```
id_to_offset(GPIO_PAD,en=5)  = false mask=0x20 (pad 5 is VALID per enum but rejected: 1<<5=32 > 30)
id_to_offset(GPIO_PAD,en=30) = false mask=0x40000000 (pad 30 VALID, rejected)
id_to_offset(GPIO_PAD,en=32) = true mask=0x0 (en=32 is INVALID; UB shift 1<<32 -> ACCEPTED with bogus mask)
id_to_offset(GPIO_PAD,en=64) = true mask=0x0 (invalid, also accepted via UB)
```

- Over-restriction: valid pads 5..30 (enum `GPIO_GPIO_PAD_5..30`) are
  rejected because their masks (32..2^30) exceed 30. Functional regression:
  GPIO-pad pins 5..30 could never obtain pin info.
- UB acceptance: `1 << en` with `en >= 32` is undefined (C11 6.5.7); on the
  guest's gcc 8.3/x86-64 the UB manifests as `mask == 0`, and `0 <= 30` is
  true — **invalid** pad indices 32/64 are accepted with a degenerate mask.
  (Other toolchains may yield 1<<0=1; either way an invalid en passes.)

## Reachability (why Info severity)

`id_to_offset` is reachable only via `dal_gpio_get_pin_info`
(gpio_base.c:140-147), which has **no callers anywhere in sys/** (dead
public API — grep: definition + header declaration only). Additionally
`dal_gpio_service_open` has no `GPIO_ID_GPIO_PAD` case (gpio_service.c:243-274),
so GPIO_PAD pins cannot even be opened on dce80 today. The defect is real and
proven, but latent: it becomes live the moment anything starts calling
`dal_gpio_get_pin_info` (e.g. future BSP code), at which point a wrong-mask
`gpio_pin_info` would describe register writes to the wrong GPIO bit.

## After fix.diff

`result = (en <= GPIO_GPIO_PAD_MAX)` before computing the mask: pads 5..30
accepted with correct masks, en≥32 rejected — fixrun.log T4:

```
id_to_offset(GPIO_PAD,en=5)  = true mask=0x20
id_to_offset(GPIO_PAD,en=30) = true mask=0x40000000
id_to_offset(GPIO_PAD,en=32) = false
id_to_offset(GPIO_PAD,en=64) = false
```
