# DF-5224 — bios_get_image() bounds check uses wrap-prone `offset + size` arithmetic (hardening)

File: `sys/dev/drm/amd/display/dc/bios/bios_parser_helper.c:40` (pass 2, GLM 5.3)
Severity: **Info** (hardening) · Confidence: speculative (hazard is real at code level; **no current caller can trigger it** — proven below)

## What

```c
uint8_t *bios_get_image(struct dc_bios *bp, uint32_t offset, uint32_t size)
{
	if (bp->bios && offset + size < bp->bios_size)   /* <-- wrap-prone */
		return bp->bios + offset;
	else
		return NULL;
}
```

This 90-line helper file's **only** memory-safety-relevant statement is this
bounds check. It is the security boundary for every `GET_IMAGE(type, offset)`
dereference in the DC atombios parsers (~60 sites in `bios_parser.c` +
`bios_parser2.c`, plus the direct call at `bios_parser.c:2022`). The addition
`offset + size` is performed in wrapping 32-bit arithmetic: for
`offset = 0xFFFFFFF2, size = 16` the sum wraps to `2 < bios_size` and the
function returns `bp->bios + 0xFFFFFFF2` — a wild kernel pointer that callers
immediately cast to an atom-table struct and dereference.

## Why it is NOT reachable today (killed, with evidence)

Every offset expression reaching this function is bounded by two
little-endian `uint16_t` VBIOS fields summed, plus small constants:

- data-table offsets: `bp->master_data_tbl_offset + tbl->list.X.data_offset`
  — both sides uint16 (`atomfirmware.h:381-424`,
  `atom_master_list_of_data_tables_v2_1` is all `uint16_t`)
- object/record offsets: `bp->object_info_tbl_offset + le16(field)`
  (`bios_parser.c:137,164,234,297,887,1527,1988,2015`)
- ROM bootstrap offsets: fixed constants (`OFFSET_TO_ATOM_ROM_HEADER_POINTER
  = 0x48`, `atomfirmware.h:210`) or `uint16_t` image reads
  (`bios_parser.c:2884-2923`)

So `offset ≤ 0xFFFF + 0xFFFF ≈ 0x1FFFE`. Sizes are `sizeof(struct)` (largest
atom struct here ~4 KB) or, for the one direct call
(`bios_parser.c:2022`), `*number * sizeof(uint16_t)` where `number` is a
`uint8_t` image byte (max 255, product computed in 64-bit via `sizeof`, max
510). Worst case `offset + size ≈ 0x21000` — **cannot wrap `uint32_t`**.
`bios_size` itself is `bios[2] * 512 ≤ 130560` (`bios_parser.c:2878`).

The `<` comparator is conservative-correct (admits only strictly-interior
windows), and `bp->bios` is NULL-guarded.

## NOT a duplicate of DF-4168

DF-4168 (Medium, `bios_parser2.c:1930`) covers the *reference point* being
wrong: `bios_size` is derived from the image's self-declared size byte
(`bios[2]*512`) instead of the allocated buffer length, so even
non-wrapping offsets can read past the real allocation. DF-5224 is about the
*check's own arithmetic*: with DF-4168 fixed (honest bios_size), the
wrap-prone form still silently admits `offset` near `UINT32_MAX` from any
future/wrong caller. The two compose; they are distinct defects at distinct
sites. (Also distinct from DF-3942/DF-1299, which are missing NULL/limit
checks in the *callers*, not in this helper.)

## Fix (hardening, upstream-portable)

See `fix.diff` — rewrite in overflow-safe form. Behavior is byte-identical
for every in-range call that passes today; only wrap-candidate inputs change
from "wild pointer" to "NULL".

## Reproduce

Not runtime-reproducible on the audit guest: the trigger requires an AMD
ASIC whose VBIOS content produces a 32-bit offset near `UINT32_MAX`, and no
current parser expression can emit such an offset. Verified at source level
only; that is appropriate for an Info hardening finding per the audit
contract. Run `verify.sh` for the mechanical caller-bounds audit backing the
negative reachability claim.
