# DF-4337 — atom.c has no bios_size: every VBIOS offset dereference in the ATOM interpreter/parsers is unbounded

**Severity:** High **Confidence:** certain **File:** sys/dev/drm/radeon/atom.c

## What

`struct atom_context` (atom.h:125-143) carries `void *bios` but **no length**.
The bios heap allocation is exactly as large as the source image — and the
sources self-declare sizes that can be far smaller than the 16-bit offset
space the ATOM tables use:

- `radeon_bios.c:radeon_read_platform_bios` allocates `bios[2]*512` bytes
  (512..256KB; a 512-byte image is accepted if `bios[2]==1`);
- `radeon_acpi_vfct_bios` allocates `vhdr->ImageLength`;
- `radeon_read_bios` allocates the vga_pci ROM mapping size.

Every ATOM dereference uses raw 16-bit offsets from the image with no check
against that allocation:

- `atom_parse` (atom.c:1298-1326): ROM-table ptr, cmd/data table ptrs, IIO
  ptr, message-string ptr are all dereferenced unvalidated;
- `atom_parse_data_header` (atom.c:1366-1385) / `atom_parse_cmd_header`
  (atom.c:1387-1402): master-table entry and table header read unvalidated;
  `*data_start`/`*size` are handed to dozens of callers (radeon_atombios.c,
  atombios_encoders.c, *_dpm.c) who then walk structs at that offset;
- `atom_execute_table_locked` (atom.c:1163-1214): `base` and the interpreter
  fetch loop itself (`op = CU8(ptr++)`) never check `ptr` against the image —
  a crafted jump/switch-walk walks the execution pointer out of the image and
  the kernel keeps *executing* whatever heap bytes follow as ATOM opcodes;
- `ATOM_ARG_ID` (atom.c:279) `val = U32(idx + gctx->data_block)`: idx(u16) +
  data_block(u16) reaches 128KB past the buffer base;
- `atom_op_calltable` (atom.c:633), `atom_op_setdatablock` (atom.c:848),
  `atom_asic_init` (atom.c:1334-1341), `atom_allocate_fb_scratch`
  (atom.c:1411-1418): same class.

## How to reproduce (logic-level; kernel runtime needs ATOM hardware)

The guest has no ATI/AMD GPU (env.txt), so the kernel path cannot be
exercised. The exact dereference expressions were compiled verbatim into a
userland mirror with the bios buffer ending at a `PROT_NONE` guard region;
any read past the allocation faults deterministically.

```
./build.sh   # on the audit guest (or any DF box): cc -O1 -g ...
./run.sh     # case data_header -> Segmentation fault (exit 139)
./run_fixed.sh  # fixed logic -> rejected cleanly, exit 0
```

## Fix

`fix.diff` (validated `git apply --check`): adds `bios_size` to
`atom_context`, plumbs the allocation length from every `radeon_bios.c`
reader through `radeon_device` into `atom_parse`, and gates every offset
dereference behind a new `atom_in_bios()` helper — including the interpreter
fetch loop, both master-table header parsers, and the ID/calltable/
setdatablock reads.

The **amdgpu twin** (`sys/dev/drm/amd/amdgpu/atom.c`, `amdgpu_atom_parse*`)
is byte-for-byte the same code and needs the same port (see VERDICT.md for
the downstream-neutralization analysis: DF-4104 neutralized; DF-4152/DF-4289
partially (inner walks still need per-site bounds); DF-4168 and DF-4048 not
affected by this fix).
