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

dce80_link_encoder_create indexes 7/6/6-entry const register tables with unvalidated VBIOS-derived transmitter/channel/hpd_source (DF-4312 family)

Summary

The three const-table indexings have no range validation. The DC core legitimately produces out-of-range values for all three (UNKNOWN/-1 defaults plus TRAVIS/UNIPHY_G transmitters, DDC_VGA/I2C_PAD channels, HPD_SOURCEID_UNKNOWN). The stored OOB pointers are dereferenced on live AUX/HPD register paths - feeding dm_read/write_reg with register addresses read from adjacent static data. Crafted/malformed VBIOS on a DCE8 GPU: semi-controlled MMIO during link-encoder setup. Guest-built reachability matrix; hardware-gated. Validate-before-index fix (caller handles NULL).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4319 Β· 9 files
FileTypeDescriptionSize
df4319.c β€” 5.4 KB view raw
build.sh β€” 86 B view raw
run.sh β€” 89 B view raw
build.log β€” 16 B view raw
run.log β€” 1.7 KB view raw
env.txt β€” 891 B view raw
fix.diff β€” 1.6 KB view raw
VERDICT.md β€” 3.8 KB ↓ raw
verdict.json β€” 4.2 KB view raw
VERDICT.md
↓ download raw

VERDICT β€” DF-4319

Status: untested (kernel path) / index-reachability-confirmed (guest) Impact: none on this guest (hardware-gated); code defect certain, exploitation requires crafted VBIOS

Reproduced?

Not against the kernel: the indices come from the GPU's VBIOS object tables (parsed by the DC BIOS parser) and the audit guest has no AMD GPU at all (pciconf -l β†’ virtio + generic VGA only; see env.txt).

What was verified:

  1. Index reachability proof (df4319.c) built and run on the guest (DragonFly cc 8.3): prints the full matrix of index values the DC core can legitimately produce for each of the three const tables, marking the OOB ones (see run.log): transmitter -1/7/8/9 vs 7 entries; channel-1 -1/6/7 vs 6 entries; hpd_source 6 vs 6 entries.

  2. Line-by-line trace (static, cited): - dce80_resource.c:661-678 β€” dce80_link_encoder_create indexes link_enc_regs[enc_init_data->transmitter] (7 entries, :249-257), link_enc_aux_regs[enc_init_data->channel - 1] (6 entries, :221-228), link_enc_hpd_regs[enc_init_data->hpd_source] (6 entries, :235-242) with no validation. - Provenance: core/dc_link.c:1177-1183 fills the three fields from get_ddc_line() (:964-1004), get_hpd_line() (:925-962) and translate_encoder_to_transmitter() (:1006-1069). All three helpers have default arms returning UNKNOWN/-1 variants, and map legitimate enum values (DDC_VGA, I2C_PAD, TRAVIS_*, UNIPHY_G) that exceed the dce80 table bounds. Enums live in include/grph_object_defs.h:45-94 (TRANSMITTER_UNKNOWN = -1; TRAVIS_CRT=7, TRAVIS_LCD=8, UNIPHY_G=9; CHANNEL_ID_DDC_VGA=7, I2C_PAD=8; HPD_SOURCEID_UNKNOWN=6). - Dereference sites of the stored OOB pointers: dce_link_encoder.c:494 and :501 (AUX_REG(...) β†’ dm_read_reg/dm_write_reg) and :1370/:1384 (HPD_REG(DC_HPD_CONTROL) β†’ dm_read_reg/dm_write_reg).

Impact ceiling

Register addresses are read from static data adjacent to the const tables (&arr[-1] / &arr[6..8] land in neighboring .data/.rodata), then used for MMIO reads and writes. With a crafted VBIOS (physical access / VBIOS flash / PCIe device spoofing) this is semi-controlled MMIO corruption; on stock hardware the realistic outcome for an unknown-encoder/VGA-DDC/no-HPD connector object is programming garbage register offsets β†’ display engine misbehavior or hang. No path from an unprivileged local user was found that influences these values (they never traverse userspace).

Calibration: same family and severity as DF-4312 (dcn10, Low, AV:P).

Exploit chain

Physical/VBIOS prerequisite β‡’ none from an unprivileged user. Chain for a VBIOS-controlling attacker: choose encoder object id/enum_id and DDC/HPD GPIO lines in the ROM object table β†’ pick which adjacent static data is read as the register base β†’ MMIO write with partially chosen 32-bit value at dm_write_reg(ctx, addr, value) during link encoder setup / HPD enable-disable. Deterministic per-VBIOS, but hardware-bound.

Fix validation

fix.diff adds explicit range checks before the three lookups (transmitter ∈ [0,6], channel ∈ DDC1..DDC6, hpd ∈ HPD_SOURCEID1..6) and frees enc110/returns NULL on violation; caller handles NULL gracefully (dc_link.c:1187-1189 β†’ link_enc_create_fail). Not kernel-tested β€” no hardware on the guest.

Conclusion

Missing validation is certain; exploitation is hardware/physical-gated β‡’ Low severity, confidence "likely".

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 adds range checks (transmitter∈[UNIPHY_A,NUTMEG_CRT], channel∈[DDC1,DDC6], hpd∈[SOURCEID1,SOURCEID6]) with kfree(enc110)+NULL return; caller dc_link.c:1187-1189 fails link construction gracefully. Not kernel-testable: guest has no AMD GPU and the trigger values require a crafted VBIOS.

fix.diff; reachability matrix in run.log showing exactly the enum values the new guards reject
↓ fix.diffper-fix-DF-4319

Confirmed kernel references

Detail

Exploit chain

Physical/VBIOS-gated: flash crafted VBIOS with encoder object (e.g. EXTERNAL_TRAVIS enum_id=2 β†’ transmitter=8) or connector without HPD GPIO (hpd_source=UNKNOWN=6) on a Sea Islands dGPU β†’ OOB regs pointer into adjacent .data at link creation β†’ dm_write_reg at semi-chosen offsets during encoder setup/HPD handling β†’ display-engine misprogramming or hang (corruption ceiling with fully chosen adjacent data). No unprivileged route: values never traverse userspace.

Evidence (decisive lines)

run.log (guest-built index matrix: -1/7/8/9 vs 7; -1/6/7 vs 6; 6 vs 6, all marked OUT OF BOUNDS); enum defs include/grph_object_defs.h:45-94; provenance dc_link.c:925-1069; deref sites dce_link_encoder.c:494,501,1370,1384; env.txt (no AMD GPU on guest)

PoC changes

No seed PoC (pass-2 checklist applying DF-4312's family to dce80). Authored df4319.c reachability proof with enum values transcribed from grph_object_defs.h and table sizes from dce80_resource.c; fixed an enumerator-name collision (HPD_SOURCEID6) found during the first guest build.

Verified recommended fix

Bounds-validate transmitter/channel/hpd_source against the three table ranges in dce80_link_encoder_create before forming the pointers; free enc110 and return NULL on violation (caller already handles NULL).

Verdict

dce80_link_encoder_create (dce80_resource.c:661-678) indexes three const register tables with unvalidated VBIOS-derived enum values: &link_enc_regs[transmitter] (7 entries) accepts TRANSMITTER_UNKNOWN=-1 (default arm of translate_encoder_to_transmitter, dc_link.c:1006-1069) and TRAVIS_CRT=7/TRAVIS_LCD=8/UNIPHY_G=9; &link_enc_aux_regs[channel-1] (6 entries) accepts channel=CHANNEL_ID_UNKNOWN(0)β†’-1, DDC_VGA(7)β†’6, I2C_PAD(8)β†’7 (get_ddc_line, dc_link.c:964-1004); &link_enc_hpd_regs[hpd_source] (6 entries) accepts HPD_SOURCEID_UNKNOWN=6 (get_hpd_line default, dc_link.c:925-962). The stored OOB pointers are dereferenced on live paths (AUX_REG at dce_link_encoder.c:494,501; HPD_REG at :1370,1384 β†’ dm_read_reg/dm_write_reg), programming register addresses read from adjacent static data. Same defect family as DF-4312 (dcn10). Kernel-path reproduction untested: guest has no AMD GPU and the indices originate in the GPU ROM, so no local user can influence them; exploitation requires crafted VBIOS (physical access). Index-reachability proof built and run on the guest (run.log) β€” the code-level defect is certain, exploitation hardware-gated, hence Low/likely.