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

Five spread-spectrum table parsers dereference unchecked GET_IMAGE() results β€” NULL-page kernel panic at amdgpu/dc attach from a crafted/corrupt VBIOS

Summary

GET_IMAGE() returns NULL when the table pointer plus the struct size leaves the VBIOS image. Five SS parsers fetch the full struct and immediately dereference ->sHeader.usStructureSize with no NULL check. Reachability: the master-data-table SS pointer is a raw VBIOS u16; point it at bios_size-10 so the revision probe succeeds while the full struct fetch fails -> NULL -> panic. Every sibling parser in the file checks. VERIFIED: logic replica executes the exact bounds semantics and SIGSEGVs on the replicated deref; 5-guard fix compile-validated by a full amdgpu.ko -Werror rebuild. Attacker who controls the VBIOS image on an AMD GPU: deterministic kernel NULL-page panic during attach (DoS).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3942 Β· 11 files
FileTypeDescriptionSize
bios_ss_null_deref_replica.c β€” 5.4 KB view raw
build.sh β€” 131 B view raw
run.sh β€” 112 B view raw
build.log β€” 304 B view raw
run.log β€” 619 B view raw
fix.diff β€” 1.4 KB view raw
fix_build.log β€” 423 B view raw
VERDICT.md β€” 4.2 KB ↓ raw
README.md β€” 1.4 KB ↓ raw
verdict.json β€” 4.6 KB view raw
manifest.json β€” 1.5 KB view raw

DF-3942 β€” bios_parser.c: five SS-table parsers dereference unchecked GET_IMAGE() results (NULL-deref panic)

File: sys/dev/drm/amd/display/dc/bios/bios_parser.c (pass 2) Severity: Low Β· Confidence: certain (source level) Β· Hardware-gated (needs an AMD GPU + crafted/corrupt VBIOS)

Build

cc -O2 -Wall -o /tmp/bios_ss_null_deref_replica bios_ss_null_deref_replica.c

Run

/tmp/bios_ss_null_deref_replica

Expected

This is a userland logic replica of the exact kernel decision path (same bios_get_image() bounds semantics, same probe order). It prints the kernel-side trace (4-byte revision probe succeeds at a table offset 10 bytes from the image end; full-struct GET_IMAGE returns NULL) and then executes the replicated deref β†’ SIGSEGV (rc=139), demonstrating the NULL is reached. In the kernel the same load faults the NULL page β†’ panic during amdgpu/dc attach. See run.log.

The in-kernel path cannot run on the audit guest: there is no AMD/ATI PCI device and no amdgpu module (verified, env.txt).

Contents

  • bios_ss_null_deref_replica.c β€” logic replica of bios_parser.c:1028-1048
  • build.sh / run.sh, build.log / run.log β€” untrimmed
  • fix.diff β€” five NULL guards, git-apply-able, compile-validated by a full amdgpu.ko rebuild on the guest kernel tree (fix_build.log, -Werror, rc=0)
  • VERDICT.md, manifest.json, verdict.json
VERDICT.md
↓ download raw

DF-3942 VERDICT β€” unchecked GET_IMAGE() NULL derefs in bios_parser.c SS parsers

Bottom line

inconclusive at runtime (no AMD GPU on the audit guest); the reachable NULL dereference is proven at source level and by an executed logic replica of the exact kernel decision path. With a crafted or corrupt VBIOS whose spread-spectrum table pointer points near the end of the ROM image, the kernel dereferences a NULL GET_IMAGE() result and panics during driver attach.

The five sites

GET_IMAGE(type, off) is (type *)bios_get_image(bp, off, sizeof(type)) (bios_parser_helper.h) and returns NULL when off + sizeof(type) leaves the image (bios_parser_helper.c:36-44). All five functions below fetch the 4-byte revision probe successfully (which gates entry), then fetch the full struct without checking for NULL and immediately dereference ->sHeader.usStructureSize:

# function GET_IMAGE deref
1 get_ss_info_from_internal_ss_info_tbl_V2_1 bios_parser.c:1041 :1046
2 get_ss_info_from_ss_info_table bios_parser.c:1114 :1140
3 get_ss_entry_number_from_ss_info_tbl bios_parser.c:1645 :1671
4 get_ss_entry_number_from_internal_ss_info_tbl_v2_1 bios_parser.c:1723 :1726
5 get_ss_entry_number_from_internal_ss_info_tbl_V3_1 bios_parser.c:1759 :1761

Reachability (site 1 traced; the others are the same shape):

  1. bios_parser_construct() accepts any usMasterDataTableOffset whose ATOM_MASTER_DATA_TABLE fits (bios_parser.c:2898-2903);
  2. master_data_tbl->ListOfDataTables.AsicInternalSS_Info is a raw VBIOS u16 β€” a crafted VBIOS points it at bios_size - 10;
  3. bios_parser_get_spread_spectrum_info() (called during DC init / SS setup) probes GET_IMAGE(ATOM_COMMON_TABLE_HEADER, off) (bios_parser.c:952-954) β€” 4 bytes fit, the header claims rev 2.1 β†’ gate passes;
  4. get_ss_info_from_tbl() dispatches to site 1;
  5. GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, off) needs 4 + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2) bytes β€” only 10 remain β†’ NULL;
  6. line 1046 loads header->sHeader.usStructureSize from NULL β†’ panic.

Every sibling parser in the file does check: get_gpio_pin_info (:1802), get_gpio_i2c_info (:1852), get_embedded_panel_info_v1_2 (:1233), get_integrated_info_v8/v9 (:2248/:2398). The five missing checks are plain omissions (upstream added them in later revisions).

Impact: deterministic kernel NULL-page dereference β†’ panic at amdgpu/dc attach (or first SS query). Attacker model: crafted VBIOS (flashed ROM, passthrough ROM override, or bit-rotted image). DoS only β€” no info leak, no write. Severity Low per the project rubric (crafted-VBIOS-gated DoS), consistent with the class of DF-3741/DF-3745.

What ran on the guest

  • Hardware absence documented: no 1002: PCI device, no drm/amdgpu KLD.
  • bios_ss_null_deref_replica.c (build.log, run.log): replicates bios_get_image() and the probe order exactly; prints the trace, then executes the replicated deref and dies with SIGSEGV (rc=139) β€” the same load the kernel would take. This demonstrates the NULL is reachable; it is honestly labeled a replica, not the in-kernel path.
  • Fix compile-validated: fix.diff (5 NULL guards) applied to the guest's /usr/src, amdgpu.ko fully rebuilt with -Werror, rc=0 (fix_build.log). Runtime baseline/patched comparison impossible without an AMD card β†’ fix_status not_testable for that reason only.

Kernel references

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.diff (5 NULL guards) applied to guest /usr/src/sys; full amdgpu.ko module rebuilt under -Werror with rc=0 (fix_build.log). Runtime baseline/patched comparison impossible: no AMD GPU on the guest.

fix_build.log (bios_parser.c recompiled, amdgpu.ko relinked, AMDGPU_FIX_MAKE_RC=0); fix.diff is git-apply-able (git apply --check passed on the audit tree)
↓ fix.diffper-fix-DF-3942

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (replica: revision probe ok at off=bios_size-10, struct GET_IMAGE NULL, SIGSEGV on replicated deref); VERDICT.md (full per-site trace); fix_build.log (amdgpu.ko rebuilt with the 5 NULL guards, -Werror, rc=0)

PoC changes

Authored as a logic replica (no seed existed): exact copies of bios_get_image() bounds semantics and the revision-gate order, packed ATOM struct layouts from atombios.h, live NULL load (first iteration was DCE-eliminated; fixed with noinline + observable result)

Verified recommended fix

Add NULL checks after each of the five GET_IMAGE() calls (fix.diff, matching the file's own existing checks)

Verdict

Hardware-gated: the audit guest has no AMD/ATI PCI device and no amdgpu KLD, so the in-kernel path cannot execute β€” runtime reproduction inconclusive on this machine. The NULL dereference is proven at source level and by an executed userland logic replica of the exact kernel decision path (bios_get_image bounds semantics + probe order): with a crafted VBIOS whose AsicInternalSS_Info u16 points 10 bytes before the image end, the 4-byte revision probe at bios_parser.c:952 succeeds and gates rev 2.1 entry, the full-struct GET_IMAGE at :1041 returns NULL, and :1046 loads header->sHeader.usStructureSize from NULL -> kernel panic at amdgpu/dc attach. Five sites share the omission (:1041/:1046, :1114/:1140, :1645/:1671, :1723/:1726, :1759/:1761); every sibling parser in the file checks. The replica run.log shows the reached NULL (SIGSEGV rc=139).