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

dce112_link_encoder_create indexes 7/6/6-entry const register tables with unvalidated VBIOS-derived transmitter/channel/hpd_source β€” OOB const reads become the encoder's MMIO register bases (DF-4312/4319/4352 twin)

Summary

The three const-table indexings have no range validation; the DC core legitimately produces UNKNOWN(-1)/TRAVIS/UNIPHY_G transmitters, UNKNOWN/VGA/I2C_PAD channels, and UNKNOWN hpd sources from the VBIOS object tables - every one of those reads past a table end. The OOB-read structs are stored as the link encoder's register bases and dereferenced on every later AUX/HPD register access (wild MMIO). Guest PoC mirrors the exact arrays/indices: every DC-legitimate value pair lands OOB. The UNKNOWN variants need only a well-formed VBIOS entry without a mapping, not crafted values. Bounds-check-return-NULL fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4375 Β· 10 files
FileTypeDescriptionSize
df4375_poc.c β€” 5.4 KB view raw
build.sh β€” 147 B view raw
run.sh β€” 95 B view raw
build.log β€” 787 B view raw
run.log β€” 1.8 KB view raw
env.txt β€” 227 B view raw
fix.diff β€” 1.2 KB view raw
VERDICT.md β€” 4.4 KB ↓ raw
manifest.json β€” 1.7 KB view raw
verdict.json β€” 4.2 KB view raw
VERDICT.md
↓ download raw

DF-4375 VERDICT

Status: inconclusive (defect certain from source; kernel-path runtime manifestation requires AMD DCE112 hardware that the verification guest does not have β€” same classification as twin DF-4312).

Reproduced? How?

  1. Source trace (certain). The three const-table indexings at dce112_resource.c:577-579 have no range validation. The index values are produced by the DC core itself from the VBIOS object tables: - enc_init_data.transmitter β€” translate_encoder_to_transmitter() (core/dc_link.c:1006-1071): returns TRANSMITTER_UNKNOWN(-1) for any unrecognized encoder id/enum pair (multiple default: arms), and TRANSMITTER_UNIPHY_G(9) / TRAVIS_CRT(7) / TRAVIS_LCD(8) for ENCODER_ID_INTERNAL_UNIPHY3 / EXTERNAL_TRAVIS entries a VBIOS can carry. β†’ link_enc_regs[-1|7|8|9] into a 7-entry array. - enc_init_data.channel β€” get_ddc_line() (core/dc_link.c:964-1004): returns CHANNEL_ID_UNKNOWN(0) (β†’ aux_regs[-1]), CHANNEL_ID_DDC_VGA(7) (β†’ 6), CHANNEL_ID_I2C_PAD(8) (β†’ 7) into a 6-entry array. - enc_init_data.hpd_source β€” get_hpd_line() (core/dc_link.c:925-962): returns HPD_SOURCEID_UNKNOWN(7) when the connector has no HPD GPIO or an unmapped IRQ source β†’ hpd_regs[7] into a 6-entry array. This one is reachable with an entirely well-formed VBIOS entry that merely lacks an HPD mapping β€” no crafted values needed.
  2. Index-arithmetic proof on the guest (ran, deterministic). df4375_poc.c mirrors the arrays with their exact element counts and drives the exact index expressions with the exact enum values above (build.log, run.log, env.txt). Every DC-legitimate out-of-range value lands past the table end. The plain (non-ASan) build actually performs the OOB reads without trap β€” as does the kernel, silently.
  3. ASan attempt: cc -fsanitize=address fails on the guest β€” GCC 8.3 DragonFly has no libasan (build.log first attempt recorded in VERDICT-notes; final build used plain mode).

Why not reproduced (kernel)

dce112_link_encoder_create runs only during amdgpu driver bind on a Polisas (DCE112) ASIC. Guest pciconf -l shows only QEMU/virtio devices; there are 0 AMD (0x1002) PCI devices, so the amdgpu KLD (present in the tree at sys/dev/drm/amd/amdgpu/Makefile) never attaches and construct() is never reached. No unprivileged-software trigger exists: the only inputs are the GPU's own VBIOS tables, i.e. the attacker model is a crafted/flashed VBIOS or malicious DP-dock silicon enumerating TRAVIS/NUTMEG-class encoders β€” identical to the accepted twins DF-4312 (dcn10), DF-4319 (dce80), DF-4352 (dce110).

Impact

OOB reads of adjacent .rodata/.data; the garbage struct ..._registers becomes the link encoder's permanent register base, so subsequent REG_UPDATE/AUX paths compute MMIO offsets from unrelated memory β†’ wild MMIO reads/writes on the GPU BAR. Realistic ceiling: display pipeline misconfiguration / firmware misbehavior / system hang on the affected machine; not a remote or unprivileged local primitive. Severity Low (hardware + malicious-firmware precondition), bucket kernleak β€” consistent with the twins.

Exploit chain

None developed β€” no path from unprivileged input; requires attacker-controlled GPU firmware/VBIOS. Chain stops at "crafted VBIOS β†’ wild MMIO offsets".

Fix validation

fix.diff adds an explicit range check (unsigned-compare idiom rejects both the -1 sentinels and the above-range values) and returns NULL, which the caller already handles gracefully (core/dc_link.c:1184-1189 β†’ link_enc_create_fail β†’ link construction fails cleanly). git apply --check passes against sys/ (not applied; sys/ is read-only for this audit). Kernel build/run validation impossible on this guest (no AMD GPU), so fix_status: not_testable β€” the fix is 4 statements of trivially-compilable C (ARRAY_SIZE/kfree only), same classification as twins DF-4319/DF-4352.

Guest hygiene

Guest left up, untouched (only /root scratch files). guest_dirty: 0.

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

not_applicable->not_testable: no DCE112 hardware on the verification guest, so no baseline or patched kernel run is possible. fix.diff is git-apply-clean against sys/ (git apply --check OK), adds a 4-statement unsigned range check (rejects -1 sentinels and above-range values) with kfree(enc110)+NULL return; the caller core/dc_link.c:1184-1189 already handles NULL by failing link construction gracefully, so the check cannot regress bring-up beyond refusing the malformed VBIOS entry. Same treatment as twins DF-4319/DF-4352.

git apply --check findings/poc/DF-4375/fix.diff => OK (sys/ left unmodified); diff context generated mechanically from the pristine tree (orig.c vs patched.c in run notes)
↓ fix.diffper-fix-DF-4375

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (guest run of df4375_poc.c: per-row 'index (array size N) <-- OOB' verdicts, e.g. link_enc_regs[9] into size 7, link_enc_aux_regs[-1] into size 6, link_enc_hpd_regs[7] into size 6); env.txt (uname 6.5-DEVELOPMENT, cc 8.3, 0 AMD PCI devices); build.log; VERDICT.md full trace; fix.diff git-apply-clean

PoC changes

n/a (pack authored fresh in this run; no seed harness existed). Harness mirrors enum values from grph_object_defs.h and array sizes from dce112_resource.c verbatim; read-only OOB loads instead of stores so the proof matches the kernel's const-table reads

Verified recommended fix

Validate transmitter/channel/hpd_source against ARRAY_SIZE(link_enc_regs)/ARRAY_SIZE(link_enc_aux_regs)/ARRAY_SIZE(link_enc_hpd_regs) in dce112_link_encoder_create and return NULL (caller dc_link.c:1184-1189 fails the link cleanly) β€” see fix.diff

Verdict

Defect certain from source: dce112_resource.c:577-579 indexes link_enc_regs[7], link_enc_aux_regs[6] and link_enc_hpd_regs[6] with unvalidated transmitter / channel-1 / hpd_source values that the DC core itself derives from VBIOS object tables (TRANSMITTER_UNKNOWN=-1, TRAVIS_CRT=7, TRAVIS_LCD=8, UNIPHY_G=9; CHANNEL_ID_UNKNOWN=0 -> -1, DDC_VGA=7 -> 6, I2C_PAD=8 -> 7; HPD_SOURCEID_UNKNOWN=7), producing OOB const-table reads that become the link encoder's register bases (wild MMIO offsets) β€” the DF-4312/DF-4319/DF-4352 twin on DCE112. Runtime kernel manifestation could not be exercised: the verification guest has zero AMD PCI devices (pciconf) so amdgpu never binds; the index arithmetic itself was executed and verified deterministically on the guest with the exact enum values and table sizes (run.log: every '<-- OOB' row). ASan mode unavailable on the guest (GCC 8.3 lacks libasan; build.log). Classification mirrors twin DF-4312 (inconclusive, no DC hardware on the verification guest).