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)
PoC verification
Evidence pack
findings/poc/DF-4375 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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?
- Source trace (certain). The three const-table indexings at
dce112_resource.c:577-579have 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): returnsTRANSMITTER_UNKNOWN(-1)for any unrecognized encoder id/enum pair (multipledefault:arms), andTRANSMITTER_UNIPHY_G(9)/TRAVIS_CRT(7)/TRAVIS_LCD(8)forENCODER_ID_INTERNAL_UNIPHY3/EXTERNAL_TRAVISentries 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): returnsCHANNEL_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): returnsHPD_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. - Index-arithmetic proof on the guest (ran, deterministic).
df4375_poc.cmirrors 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. - ASan attempt:
cc -fsanitize=addressfails on the guest β GCC 8.3 DragonFly has nolibasan(build.logfirst 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_testablenot_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)
Confirmed kernel references
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:577
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:578
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:579
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:267
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:239
- sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:253
- sys/dev/drm/amd/display/dc/core/dc_link.c:1165
- sys/dev/drm/amd/display/dc/core/dc_link.c:1006
- sys/dev/drm/amd/display/dc/core/dc_link.c:964
- sys/dev/drm/amd/display/dc/core/dc_link.c:925
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).
No comments yet.