All VBIOS table parsers dereference attacker-controlled offsets without bounds validation -- OOB heap read
Summary
Every function in amdgpu_atomfirmware.c casts ctx->bios + data_offset to a struct pointer and reads fields without checking that data_offset + sizeof(struct) falls within adev->bios_size. data_offset is uint16_t read directly from VBIOS image via amdgpu_atom_parse_data_header (atom.c:1389) which performs NO bounds validation. BIOS buffer attacker-controllable in size (ACPI VFCT path amdgpu_bios.c:423 vhdr->ImageLength; ROM path amdgpu_bios.c:172 bios[2]<<9). Crafted VBIOS with small declared length but internal table offsets near buffer tail causes OOB reads of kernel heap memory adjacent to adev->bios kmalloc allocation. 6 of 8 callers pass NULL for size so cannot validate; 2 that receive &size never compare against sizeof(union). Specific sinks: get_clock_info line 263-264 reads firmware_info->v31 at offset 16; get_gfx_info line 345-346 reads up to gc_max_scratch_slots_per_cu at offset ~63; get_vram_type line 226-230 reads vram_info->v23.vram_module[0].memory_type at offset ~47 (sizeof union ~856 bytes, BIOS needs that many bytes from data_offset never checked). Attacker: controls VBIOS image via (a) compromised VM host VFIO/PCI passthrough, (b) crafted ACPI VFCT, (c) physical GPU ROM reflash. VBIOS parsed during amdgpu_device_init before any GPU use. Impact: (1) OOB read crossing unmapped page -> kernel panic (reliable DoS); (2) garbage OOB heap bytes populate gfx config fields (max_shader_engines/max_cu_per_sh/max_sh_per_se) exposed via debugfs and AMDGPU_INFO_DEV_INFO ioctl (limited info leak); (3) same garbage used as loop bounds and shift operands in gfx init causing cascading OOB register writes or UB. PR:H bounds at Medium.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2027 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | choke-point bounds-check rationale across atom.c + all amdgpu_atomfirmware parsers | 4.3 KB | β raw |
| fix.diff | suggested-fix | add bios_size to atom_context + bounds-check parse_data_header; supersedes per-parser proposal | 1.7 KB | view raw |
| fix_build.log | build-log | combined kernel build rc=0, 0 warnings/errors -Werror; atom.c/amdgpu_atomfirmware.c/amdgpu_atombios.c recompiled, amdgpu.ko relinked | 1.4 KB | view raw |
| env.txt | environment | guest uname, cc version, no-amdgpu/no-AMD-display confirmation | 405 B | view raw |
| build.sh | build-script | documents source-only verification (HW-gated) | 645 B | view raw |
| run.sh | run-script | documents no runtime PoC (HW-gated) | 256 B | view raw |
| README.md | readme | status + verification method + VBIOS preconditions summary | 1.1 KB | β raw |
DF-2027 PoC β VBIOS table parsers dereference attacker-controlled offsets
Status: inconclusive / reproduced=0 β HW-GATED (needs an AMD GPU; this guest has
none, amdgpu.ko not loaded, only generic VGA present).
Verification method: source trace of sys/dev/drm/amd/amdgpu/atom.c:1384-1403
(amdgpu_atom_parse_data_header returns the VBIOS-sourced idx as data_start with no
bounds check against bios_size) + sys/dev/drm/amd/include/atom-bits.h:39 (CU16 reads
ctx->bios) + the ~10 parsers in sys/dev/drm/amd/amdgpu/amdgpu_atomfirmware.c
(:42,62,80,141,217,264,287,310,346) that cast ctx->bios + data_offset to a struct and
read fields β OOB heap read with a crafted VBIOS. See VERDICT.md, fix.diff.
Preconditions (realistic, but not arrangeable on this audit guest): attacker controls
the VBIOS image via VFIO/PCI romfile passthrough, a compromised ACPI VFCT table, or a
reflashed ROM.
Build/run: no runtime PoC (no AMD GPU). fix_build.log proves atom.c,
amdgpu_atomfirmware.c, amdgpu_atombios.c recompiled with the fix under -Werror rc=0
and amdgpu.ko relinked.
DF-2027 β VBIOS table parsers dereference attacker-controlled offsets without bounds
Verdict
CONFIRMED (source-trace); NOT REPRODUCED AT RUNTIME β HW-GATED.
Status: inconclusive, reproduced=0. The defect is real (the offset is read from the
VBIOS image and never bounds-checked), but it cannot be exercised on this guest: there is
no AMD GPU (amdgpu.ko is not loaded; the guest's only display device is plain VGA, not
AMD), so none of the amdgpu_atomfirmware_* parsers run.
Mechanism (confirmed)
amdgpu_atom_parse_data_header() is the single choke point that hands every VBIOS table
parser its table pointer:
- sys/dev/drm/amd/amdgpu/atom.c:1384-1403
- :1388-1389 β int offset = index*2+4; int idx = CU16(ctx->data_table + offset);
idx is a 16-bit value read directly from the VBIOS image (CU16 β
get_u16(ctx->bios, ptr), sys/dev/drm/amd/include/atom-bits.h:39).
- :1401 β *data_start = idx; returns idx with NO validation that
idx + sizeof(table) lies within the BIOS buffer (ctx->bios, a kmalloc'd image of
adev->bios_size bytes β sys/dev/drm/amd/amdgpu/amdgpu_bios.c:111 etc.).
Every parser in sys/dev/drm/amd/amdgpu/amdgpu_atomfirmware.c then does
(ctx->bios + data_offset) cast to a struct pointer and reads fields β e.g.
:42-43 amdgpu_atomfirmware_get_gpu_clock_info, :62, :80
(vram_usagebyfirmware), :141, :217, :264, :287, :310, :346. With a crafted
VBIOS (attacker controls the image via VFIO/PCI romfile passthrough, a compromised ACPI
VFCT table, or a reflashed ROM β all noted in the parent finding), data_offset can point
past bios + bios_size, so each struct-field read is an OOB heap read of the
kmalloc'd VBIOS buffer into adjacent slab objects. Class: CWE-125.
The README's crafted-VBIOS example (1024-byte image, data_offset = 980 for firmwareinfo)
is consistent with this: firmware_info at bios+980 extends ~68 bytes, so
reserved2[6] at offset 44-67 reads bios+1024..bios+1047, 23 bytes past the allocation.
Why it is not reproduced on this guest (HW-gate)
pciconf -lvshows no AMD display device (only a generic VGA);kldstat -vshows noamdgpumodule.- The parsers run only during AMD GPU init (
amdgpu_device_initβamdgpu_atombios_*), which never executes without AMD hardware. The crafted-VBIOS preconditions (VFIO passthrough of an evil ROM / compromised VFCT) require a host/firmware an unprivileged guest user cannot arrange on this audit guest. - No userspace harness can reach these parsers without an AMD GPU.
Threat model: an attacker who controls the VBIOS image presented to the driver (compromised VM host, compromised firmware, or physical reflash). This is a latent/remote-image class bug, not a local-syscall privesc. No escalation chain was developed (it is a heap OOB read, not a write primitive, and is unreachable here).
PoC changes
None (source-only verification).
Fix (fix.diff)
Root-cause fix at the choke point, touching three files:
1. sys/dev/drm/amd/amdgpu/atom.h β add uint32_t bios_size; to struct atom_context.
2. sys/dev/drm/amd/amdgpu/atom.c β initialize ctx->bios_size = 0 in
amdgpu_atom_parse(), and in amdgpu_atom_parse_data_header() validate, before
returning data_start, that [idx, idx + tbl_size) (where tbl_size = CU16(idx) is the
table's own declared size) lies within ctx->bios_size; return false otherwise.
bios_size == 0 (legacy/unknown) skips the check for back-compat.
3. sys/dev/drm/amd/amdgpu/amdgpu_atombios.c β set
atom_context->bios_size = adev->bios_size right after amdgpu_atom_parse().
This single choke-point fix protects all ~10 amdgpu_atomfirmware_* parsers (and every
other amdgpu_atom_parse_data_header consumer) because none can receive an out-of-range
data_start. Supersedes the finding proposal's per-parser approach with a single,
maintainable bounds check at the offset source.
Fix validation (Phase 8)
Combined kernel build with all four fixes: rc=0, 0 warnings, 0 errors under -Werror;
atom.c, amdgpu_atomfirmware.c, amdgpu_atombios.c all recompiled and amdgpu.ko
(101 MB) relinked (see fix_build.log). HW-gated β fix_status = not_testable (diff
applies + compiles cleanly under -Werror + traced to close the path; no AMD GPU to run a
runtime demonstration).
Fix verification
not_testableVALIDATED build. amdgpu.ko rebuilds rc=0.
NK_DONE rc=0; atom.o/amdgpu_atomfirmware.o rebuilt.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- t
- o
- m
- .
- c
- :
- 1
- 3
- 8
- 8
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- t
- o
- m
- .
- c
- :
- 1
- 4
- 0
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- a
- t
- o
- m
- f
- i
- r
- m
- w
- a
- r
- e
- .
- c
- :
- 4
- 2
Detail
Exploit chain
none (HW-gated, read-only OOB). Threat: malicious VBIOS via VFIO/PCI romfile.
Evidence (decisive lines)
Source trace atom.c:1388-1401, amdgpu_atomfirmware.c:42,80,141. Combined build rc=0 -Werror.
Verified recommended fix
Add bios_size field to atom_context; in parse_data_header validate [idx, idx+tbl_size) within bios_size.
Verdict
HW-GATED (no AMD GPU). Source-CONFIRMED. amdgpu_atom_parse_data_header returns idx=CU16 from VBIOS as *data_start with NO bounds check vs bios_size. ~10 parsers in amdgpu_atomfirmware.c dereference bios+data_offset -> OOB heap read with crafted VBIOS.
No comments yet.