# DF-1199 — radeon_atombios_get_asic_ss_info integer underflow (OOB read loop)

## Verdict
**REPRODUCED (harness) — real bug confirmed by source trace + userspace replica.**
Impact class: integer underflow → ~12 GB out-of-bounds read loop over kernel
memory (panic on first unmapped page, or large info-leak of the BIOS/heap region
until it faults). No local-unprivileged trigger on the audit guest (no AMD GPU);
trigger requires a malicious VBIOS. `uid=0` chain N/A — hardware/firmware-attacker
class; this is a read/DoS primitive (no write), so no escalation chain regardless.

## Mechanism (confirmed `path:line`)
`radeon_atombios_get_asic_ss_info()` (`sys/dev/drm/radeon/radeon_atombios.c`)
computes a loop count from the BIOS-supplied table size:

- `size` is declared `uint16_t` (`radeon_atombios.c:1512`), populated by
  `atom_parse_data_header(... &size ...)` (`radeon_atombios.c:1528`) from the
  BIOS table header's `usStructureSize`.
- `num_indices` is declared `int` (`radeon_atombios.c:1516`).
- `radeon_atombios.c:1555-1556` (frev==2), `1577-1578` (frev==3):
  `num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);`

`sizeof(ATOM_COMMON_TABLE_HEADER)` = 4 (`atombios.h:200-206`: `USHORT`+`UCHAR`+
`UCHAR`); `sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2)` = 12. The subtraction is
performed in `size_t` (unsigned, 64-bit) because `sizeof` yields `size_t`, so
when `size < 4` the `uint16_t size` is promoted and the subtraction wraps to
`0xFFFFFFFF...FFC`. Dividing by 12 gives `0x5555555555555555`, which truncates
to `int` = **1431655765** (positive). The loop
`for (i = 0; i < num_indices; i++)` then iterates ~1.4 billion times, reading 12
bytes per iteration (`ss_assign` pointer arithmetic, `radeon_atombios.c:1572-1573`)
past the BIOS table into kernel memory — a DoS (panic on the first unmapped page)
or a large read of kernel/BIOS memory.

(For frev==1 the element size is 10 and the count would be negative as `int`, so
the loop is skipped — frev 2 and 3 are the affected paths.)

## Harness proof (`run.log`)
`harness.c` reproduces the exact arithmetic with the real struct sizes:

```
size=    0 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    1 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    2 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    3 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    4 -> num_indices=0
size=   16 -> num_indices=1
size=   40 -> num_indices=3
Finding's cited value: size=2 -> num_indices=1431655765 (== 1431655765 ? YES)
size_t trace: (2 - 4) = 0xfffffffffffffffe (18446744073709551614);  /12 = 0x1555555555555555; trunc->int = 1431655765
```

## Why not a live-kernel trigger / no uid0 chain
`radeon` attaches only to AMD/ATI Radeon PCIe GPUs (none on the guest). `size`
comes from the GPU VBIOS table header parsed at probe — not reachable from any
unprivileged syscall. Hardware/firmware-attacker class. Additionally this is a
**read-only** primitive (no write), so there is no memory-corruption escalation
chain to pursue even with hardware present — the impact ceiling is DoS / info
leak of the BIOS-adjacent kernel memory region.

## Fix (`fix.diff`)
Validate `size >= sizeof(ATOM_COMMON_TABLE_HEADER)` once, before the `switch
(frev)`, returning `false` if not (`radeon_atombios.c:1533`). This closes the
underflow for all three frev cases (1/2/3) with one guard. Matches the finding's
proposed fix (validate `size >= sizeof(HEADER)` before subtraction).

## Fix validation
`radeon.ko` rebuilt from patched source (DF-1198/1199 applied) compiled with
`-Werror` and linked; `radeon_atombios.o` rebuilt. `fix_status: not_testable`
(no AMD GPU for a live trigger; validated applies + compiles + closes the path).
