dce110_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 twin)
Summary
Three const-table lookups use enums from the VBIOS object tables with no validation: transmitter -1/7..9 into link_enc_regs[7], channel-1 -1/6/7 into aux_regs[6] (the -1 shift is unique to this instance), hpd_source 6..7 into hpd_regs[6]. The OOB reads yield garbage register-offset structs stored as the encoder's MMIO bases - VBIOS-steerable wild MMIO. Guest model: 9/28 reachable values index OOB including index -1 twice. Bounds-check-return-NULL fix (caller handles NULL).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4352 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df4352.c | β | 6.5 KB | view raw | |
| build.sh | β | 86 B | view raw | |
| run.sh | β | 89 B | view raw | |
| build.log | β | 496 B | view raw | |
| run.log | β | 1.5 KB | view raw | |
| env.txt | β | 966 B | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| README.md | β | 1.3 KB | β raw |
DF-4352 β dce110_link_encoder_create indexes 7/6/6-entry const register tables with unvalidated VBIOS-derived transmitter/channel/hpd_source (DF-4312/4319 twin)
Build / Run (guest model)
cc -O2 -Wall -o df4352 df4352.c ./df4352
Expected: CONST-TABLE OOB READ CONFIRMED (model) β 9 of 28 reachable
enum values index out of bounds:
- link_enc_regs[transmitter] (7 entries): TRANSMITTER_UNKNOWN(-1),
TRAVIS_CRT(7), TRAVIS_LCD(8), UNIPHY_G(9)
- link_enc_aux_regs[channel-1] (6 entries): CHANNEL_ID_UNKNOWN(0)β[-1],
DDC_VGA(7)β[6], I2C_PAD(8)β[7]
- link_enc_hpd_regs[hpd_source] (6 entries): COUNT(6), UNKNOWN(7)
Root cause
dce110_resource.c:588-590 performs the three lookups with values sourced at link bring-up from the VBIOS object tables (dc_link.c:1177-1183 β get_ddc_line, get_hpd_line, translate_encoder_to_transmitter, each with UNKNOWN defaults) and no bounds checks. An OOB const read yields a garbage register-offset struct that becomes the encoder's MMIO base for every later register access β VBIOS-steerable wild MMIO. Trust boundary identical to DF-4216/DF-4312/DF-4319 (crafted/foreign VBIOS).
Fix
fix.diff (git apply --check clean): bounds-check all three and return
NULL β the caller (dc_link.c:1187-1190) already fails the link cleanly
on NULL.
DF-4352 VERDICT
Status: untested (kernel path) β code defect certain; guest model enumerates every reachable OOB index.
Root cause (line-accurate)
dce110_link_encoder_create (sys/dev/drm/amd/display/dc/dce110/ dce110_resource.c:576-592):
588: &link_enc_regs[enc_init_data->transmitter], /* 7 entries :259-267 */ 589: &link_enc_aux_regs[enc_init_data->channel - 1], /* 6 entries :230-237 */ 590: &link_enc_hpd_regs[enc_init_data->hpd_source] /* 6 entries :244-251 */
The three indices are VBIOS-derived, filled in dc_link.c at link construct:
- transmitter β translate_encoder_to_transmitter (dc_link.c:1006-1024+) returns TRANSMITTER_UNKNOWN (-1) for unrecognized encoder ids, and external/NUTMEG/TRAvis encoders and UNIPHY_G map to 6..9 (grph_object_defs.h:81-94). Table domain: 0..6.
- channel β get_ddc_line (dc_link.c:964-1004) returns
CHANNEL_ID_UNKNOWN (0) by default, CHANNEL_ID_DDC_VGA (7) and
CHANNEL_ID_I2C_PAD (8) explicitly. With the
- 1shift at :589 the reachable indices are -1, 6, 7 against a 6-entry table (grph_object_defs.h:58-69). - hpd_source β get_hpd_line (dc_link.c:925-962) returns HPD_SOURCEID_UNKNOWN (7) when no HPD gpio exists; COUNT=6. Both are OOB against the 6-entry table (grph_object_defs.h:45-55).
None of the three is validated anywhere on the path. The OOB read
produces a struct dce110_link_enc_registers-shaped blob of adjacent
.rodata which dce110_link_encoder_construct stores; every subsequent
register access on that encoder dereferences offsets from it as MMIO
addresses β VBIOS-chosen wild MMIO (bring-up hang / machine check at
minimum, wrong-hardware register pokes at worst). No userspace-readable
leak.
Twin of DF-4312 (dcn10, 4-entry tables) and DF-4319 (dce80) β same family, different file, different table sizes and a -1 shift unique to the channel lookup.
Verification
- Kernel path untestable on the audit guest: pciconf (root) lists only i440FX/PIIX + virtio devices β no AMD GPU, amdgpu unbindable (env.txt).
- Guest model
df4352.c(cc 8.3; build.log/run.log) replicates the three tables (7/6/6) and the three verbatim index expressions over the real enum domains: 9 of 28 reachable values index OOB, including -1 twice.
Fix validation
Kernel-level validation impossible on this guest. fix.diff (git apply --check clean) bounds-checks all three values and returns NULL; the caller dc_link.c:1187-1190 already handles NULL by failing this link's creation cleanly (link_enc_create_fail).
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_testableKernel fix validation impossible on this guest (no DCE11 hardware). fix.diff is git-apply-clean against sys/; the caller dc_link.c:1187-1190 already handles a NULL link encoder by failing this link cleanly, so the added check cannot regress bring-up beyond refusing the malformed VBIOS entry.
fix.diff; caller NULL-handling trace dc_link.c:1184-1190 in VERDICT.md
Confirmed kernel references
- sys/dev/drm/amd/display/dc/dce110/dce110_resource.c:588
- sys/dev/drm/amd/display/dc/dce110/dce110_resource.c:589
- sys/dev/drm/amd/display/dc/dce110/dce110_resource.c:590
- sys/dev/drm/amd/display/dc/core/dc_link.c:1177
- sys/dev/drm/amd/display/dc/core/dc_link.c:929
- sys/dev/drm/amd/display/include/grph_object_defs.h:81
Detail
Exploit chain
crafted or foreign VBIOS on a Carrizo/Stoney APU declares an external/TRAvis encoder, a VGA/I2C-pad DDC line, or a connector without HPD mapping -> amdgpu bind -> dc_link_construct fills enc_init_data with UNKNOWN/out-of-range enum values -> dce110_link_encoder_create performs OOB const-table reads -> stored bogus register bases -> subsequent encoder programming issues MMIO at VBIOS-chosen offsets (hang/machine-check). Requires firmware/hardware control; no unprivileged or remote path.
Evidence (decisive lines)
run.log ('CONST-TABLE OOB READ CONFIRMED (model)', 9/28 table incl. -1/-1/7/8 indices); VERDICT.md (line-accurate domain proof); env.txt (no AMD GPU on guest); fix.diff (git apply --check clean)
PoC changes
No executable kernel PoC possible on this guest. Authored df4352.c: replica of the three const tables with the verbatim index expressions evaluated over the full enum domains from grph_object_defs.h.
Verified recommended fix
Bounds-check transmitter in [UNIPHY_A, NUTMEG_CRT], channel in [DDC1, DDC6], hpd_source in [HPD_SOURCEID1, HPD_SOURCEID6] and return NULL on violation (see fix.diff).
Verdict
Source-certain, hardware-gated (DF-4312/DF-4319 twin at the DCE11 instance). dce110_link_encoder_create (dce110_resource.c:588-590) indexes link_enc_regs[7] with transmitter (reachable -1 via TRANSMITTER_UNKNOWN from dc_link.c:1006-1024+ and 7..9 via TRAVIS_CRT/TRAVIS_LCD/UNIPHY_G), link_enc_aux_regs[6] with channel-1 (reachable -1 via CHANNEL_ID_UNKNOWN default from dc_link.c:964-1004, and 6..7 via DDC_VGA/I2C_PAD), and link_enc_hpd_regs[6] with hpd_source (reachable 6..7 via HPD_SOURCEID_COUNT/HPD_SOURCEID_UNKNOWN default from dc_link.c:925-962) - all from the GPU VBIOS object tables, unvalidated. The OOB const reads become the link encoder's MMIO register bases -> VBIOS-steerable wild MMIO on every later encoder register access (bring-up hang / machine check); no userspace-readable leak. Kernel path untestable on the guest (pciconf: no AMD/ATI device). Guest model (cc 8.3) reproduces the verbatim index expressions over the real enum domains: 9/28 reachable values OOB.
No comments yet.