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

Object-table walks dereference unvalidated VBIOS u16 offsets and stride unbounded/unchecked sizes β€” OOB kernel heap reads at attach (amdgpu twin of DF-3745)

Summary

get_connector_info_from_object_table walks display paths accumulating unvalidated per-entry usSize (up to ~16MB of pointer travel), encoder records at bios+data_offset+usRecordOffset with a ucRecordSize-stride walk terminating only on a non-conforming byte, router records + SrcDstTable derefs, connector records. parse_data_header does no extent validation and atom_context carries no bios_size, so every pointer runs past the kmalloced BIOS image. Crafted-VBIOS OOB heap reads at driver bind steering connector setup + page-fault DoS. table_end-bounding fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4152 Β· 4 files
FileTypeDescriptionSize
vbios_mutator.py trigger Illustrative trigger (hardware-gated) 1.5 KB view raw
VERDICT.md verdict Full source-trace analysis 4.3 KB ↓ raw
fix.diff suggested-fix Bound every object-table walk by the header-declared table end (bios+data_offset+size); validate each u16 offset deref and terminate record walks at the table end. 3.9 KB view raw
env.txt environment Guest uname + pciconf hardware-gate proof 980 B view raw
VERDICT.md verdict Full source-trace analysis
↓ download raw

DF-4152 β€” Object-table walks dereference unvalidated VBIOS offsets and stride unbounded sizes (amdgpu twin of DF-3745)

File: sys/dev/drm/amd/amdgpu/amdgpu_atombios.c (pass 2)

Reproduced?

No β€” hardware-gated. No AMD GPU on the audit guest (pciconf -l: only virtio VGA 0x1111:1234; zero amdgpu/radeon matches in dmesg β€” env.txt). Crafted-VBIOS delivery additionally requires romfile= VFIO assignment or a malicious FPGA device. Established at source level.

Source trace (all inside amdgpu_atombios_get_connector_info_from_object_table, lines 291-563)

Enabler: amdgpu_atom_parse_data_header() (atom.c:1384-1403) returns data_start = idx (a BIOS-supplied u16) with no extent validation, and struct atom_context carries no bios_size β€” identical to the radeon situation in DF-4104/DF-3745. The BIOS image itself is kmalloc(size) with size from the ROM (amdgpu_bios.c:106/135/206). Every pointer of the form bios + data_offset + <u16 from BIOS> is therefore an unvalidated heap pointer.

  1. Display-path walk (:333-338): path_size += le16_to_cpu(path->usSize) accumulates per-entry u16 sizes with no cumulative bound; addr walks path_obj->asDispPath + path_size. 255 entries * 65535 = up to ~16 MB of pointer travel past path_obj (itself at an unvalidated usDisplayPathTableOffset, :318-320). Each iteration dereferences path->usSize/usDeviceTag/usConnObjectId/usGraphicObjIds[] β†’ OOB heap reads of the parsed fields and of the usGraphicObjIds[j] inner loop (:375-386, count = (usSize-8)/2, unbounded).

  2. Encoder record deref + walk (:392-410): record = bios + data_offset + enc_obj->asObjects[k].usRecordOffset β€” u16 offset, unvalidated β†’ immediate OOB read of record->ucRecordSize; then while (ucRecordSize > 0 && ucRecordType > 0 && <= MAX) advances by the u8 ucRecordSize. The walk terminates only when it reads a non-conforming byte β€” with a crafted BIOS the walk runs arbitrarily far past the allocation (min stride 1; each step needs size>0 and 1<=type<=ATOM_MAX_OBJECT_RECORD_NUMBER). Values read (e.g. ATOM_ENCODER_CAP_RECORD.usEncoderCap) feed amdgpu_display_add_encoder.

  3. Router record walk + src/dst table (:416-482): same record-walk pattern (:420-422, :444-480), plus router_src_dst_table = bios + data_offset + usSrcDstTableOffset (:427-430) and the dst_objs[enum_id] walk bounded by the u8 *num_dst_objs (:431-442) β€” both deref/stride unvalidated data. (The ucMuxState[enum_id] read within this block is the already-filed DF-1409; the offsets/walks feeding it are not covered by any known.)

  4. Connector record walk (:491-541): identical deref of con_obj->asObjects[j].usRecordOffset (:495-502) and ucRecordSize walk (:507-538).

Related (same no-extent-check enabler, noted not separately filed): fixed union firmware_info reads at bios+data_offset regardless of actual table length (:588-713), and has_dce_engine_info's usDisplayPathTableOffset deref (:280-288).

Distinction from knowns

  • DF-1408 covers the (size - sizeof(HEADER)) / sizeof(ENTRY) underflow sites (:106, :177, :926/945/967, :1326-1331) and the u8-count flex-array walks at :375/:389 (enc_obj->ucNumberOfObjects, con_obj->ucNumberOfObjects).
  • DF-1409 covers ucMuxState[enum_id] OOB.
  • Not covered (this finding): the unvalidated u16 offset dereferences (usRecordOffset, usSrcDstTableOffset, usDisplayPathTableOffset), the ucRecordSize-stride record walks with no termination bound, and the usSize accumulation in the display-path walk.

Verdict

INCONCLUSIVE (hardware-gated / not reachable on the audit guest). Source-certain: crafted VBIOS β‡’ unbounded OOB kernel heap reads at driver attach / connector init (kheap disclosure ceiling is limited β€” parsed values steer encoder/connector/ddc setup β€” plus fault/DoS). Severity Medium, aligned with radeon twin DF-3745.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Fix not testable: single-fix kernel build cannot exercise adv(4)/amdgpu paths without the respective hardware on the guest.

fix.diff authored post-verification from line-accurate source trace (VERDICT.md); env.txt documents absent hardware.
↓ fix.diffper-fix-DF-4152

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (four distinct walk vectors traced), fix.diff (table_end bounds), env.txt (guest has no AMD GPU).

PoC changes

Seed replaced with an honest hardware-gated trigger sketch/mutator; no runnable PoC exists for this defect class on the audit guest.

Verified recommended fix

Bound every object-table walk by the header-declared table end (bios+data_offset+size); validate each u16 offset deref and terminate record walks at the table end.

Verdict

INCONCLUSIVE (hardware-gated). amdgpu_atombios_get_connector_info_from_object_table (amdgpu_atombios.c:291-563) walks display paths by accumulating unvalidated usSize (up to ~16MB of pointer travel), dereferences encoder/router/connector usRecordOffset and usSrcDstTableOffset (u16, unvalidated), and strides record walks by ucRecordSize with no termination bound -- all past the kmalloced BIOS image at attach/connector init. amdgpu twin of DF-3745; parse_data_header does no extent validation and atom_context has no bios_size. Distinct from DF-1407/1408/1409 (count underflow / i2c_bus write / ucMuxState).