β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2027

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2027 Β· 7 files
FileTypeDescriptionSize
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
README.md readme status + verification method + VBIOS preconditions summary
↓ download 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.

VERDICT.md verdict choke-point bounds-check rationale across atom.c + all amdgpu_atomfirmware parsers
↓ download raw

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 -lv shows no AMD display device (only a generic VGA); kldstat -v shows no amdgpu module.
  • 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_testable
baseline no→ patch + rebuild →patched clean

VALIDATED build. amdgpu.ko rebuilds rc=0.

NK_DONE rc=0; atom.o/amdgpu_atomfirmware.o rebuilt.
↓ fix.diffcombined build rc=0 -Werror

Confirmed kernel references

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.