dcn10_link_encoder_create indexes 4-entry const register tables with unvalidated VBIOS-derived transmitter/channel/hpd_source values (β1 and 4..9 / β1 and 4..7 / 4..6) β OOB kernel reads that become wild-MMIO register bases
Summary
The three index lookups into 4-entry const tables have no bounds checks; the indices come from the VBIOS object tables and can legally hold UNKNOWN(-1) or out-of-range enum values. The OOB const-array reads yield bogus register-base pointers stored in the link encoder and dereferenced for all later REG_* MMIO - VBIOS-chosen wild MMIO offsets (bring-up hang/machine-check at minimum); no userspace-readable leak. Crafted/foreign VBIOS on a Raven-class APU (the DF-4216 twin's trust boundary). Bounds-check-and-return-NULL fix (the caller handles NULL gracefully).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4312 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.5 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| fix.diff | β | 849 B | view raw | |
| env.txt | β | 694 B | view raw | |
| verdict.json | β | 3.5 KB | view raw | |
| manifest.json | β | 1.4 KB | view raw |
DF-4312 β dcn10_link_encoder_create OOB const-register-table indexing
What: sys/dev/drm/amd/display/dc/dcn10/dcn10_resource.c:744-746 indexes three 4-entry const register tables with values parsed from the GPU's object tables (VBIOS / atombios firmware) with no bounds validation:
&link_enc_regs[enc_init_data->transmitter] /* 4 entries */ &link_enc_aux_regs[enc_init_data->channel - 1] /* 4 entries */ &link_enc_hpd_regs[enc_init_data->hpd_source] /* 4 entries */
Value domains actually producible in-tree (dc_link.c):
transmitter = translate_encoder_to_transmitter() [dc_link.c:1008+] -1 TRANSMITTER_UNKNOWN (default/unknown enum ids) -> index -1 4 TRANSMITTER_UNIPHY_E (ENCODER_ID_INTERNAL_UNIPHY2/ENUM_ID_1) 5 TRANSMITTER_UNIPHY_F (ENCODER_ID_INTERNAL_UNIPHY2/ENUM_ID_2) 6 TRANSMITTER_NUTMEG_CRT (external encoder, VBIOS-declared) 7 TRANSMITTER_TRAVIS_CRT 8 TRANSMITTER_TRAVIS_LCD 9 TRANSMITTER_UNIPHY_G (ENCODER_ID_INTERNAL_UNIPHY3) -> all of { -1, 4..9 } are out of bounds for the 4-entry array
channel = get_ddc_line() [dc_link.c:966+] 0 CHANNEL_ID_UNKNOWN (ddc NULL / unknown GPIO_DDC_LINE) -> index -1 5..8 CHANNEL_ID_DDC5/DDC6/DDC_VGA/I2C_PAD -> indices 4..7 OOB
hpd_source = get_hpd_line() [dc_link.c:1007+] 4,5 HPD_SOURCEID5/6 (valid enum values on other ASICs) 6 HPD_SOURCEID_UNKNOWN (grph_object_defs.h:53, returned when no HPD gpio maps to the connector) -> indices 4,5,6 OOB for the 4-entry array
Impact: out-of-bounds read of kernel .rodata/.data yields a bogus
struct dcn10_link_enc_registers * (and aux/hpd equivalents) which is
stored in the link encoder and dereferenced for every subsequent
register access (dcn10_link_encoder.c REG_UPDATE/REG_READ via these
bases) -> wild MMIO reads/writes at attacker-chosen (VBIOS-chosen)
offsets: MMIO writes to arbitrary offsets within/near the GPU's register
aperture, at minimum a boot-time GPU bring-up failure / hang / machine
check; the register-table pointer is not exposed to userspace, so no
direct info leak.
Threat model: the indices come from the GPU's own firmware (VBIOS object tables). Exploitation requires a crafted / foreign VBIOS on a Raven-class DCN1 APU (e.g. flashed GPU, resale-supply-chain, or a buggy VBIOS declaring an external TRAVIS/NUTMEG encoder or DDC5/HPD5 line) - the same trust boundary as DF-4216 (dc_submit_i2c unvalidated DDC index, filed Low). Severity Low for that reason.
Reachability on this audit guest: NOT reachable - the guest is QEMU
std-VGA with zero AMD/ATI PCI devices (pciconf -l | grep -ci "amd\|ati"
= 0, see env.txt); dcn10_create_resource_pool() is only invoked when
amdgpu.ko attaches to a DCN1 (Raven) ASIC. PoC therefore not executable
on the available hardware: verdict inconclusive (code-certain defect,
hardware-gated trigger).
Files: ANALYSIS.md the full index-domain table and call graph (this text) fix.diff bounds check in dcn10_link_encoder_create (git-apply clean) env.txt guest evidence of hardware absence verdict.json machine verdict
Suggested manual test on real hardware (for maintainers with a Raven APU): amdgpu.debug_mask / patched atombios declaring a TRAVIS external encoder on connector 0, or hpd_source=HPD_SOURCEID5: pre-fix kernel logs bogus encoder MMIO / hangs in link encoder init; post-fix dcn10_link_encoder_create returns NULL and dc_link_construct fails gracefully (dc_link.c:1188 link_enc_create_fail).
VERDICT β DF-4312 dcn10_link_encoder_create unvalidated VBIOS-derived indices
STATUS: inconclusive (code-certain defect; trigger hardware-gated, absent from the audit guest) REPRODUCED: 0 (no AMD GPU in guest; dcn10 pool construction requires amdgpu attach to a DCN1/Raven ASIC)
ANALYSIS CONFIRMATION (static, line-accurate):
-
dcn10_resource.c:744-746 - the three table index expressions, arrays sized 4 (dcn10_resource.c:309-314 link_enc_regs, 284-289 link_enc_aux_regs, 296-301 link_enc_hpd_regs). No bounds checks exist anywhere between dc_link.c filling enc_init_data and these lookups.
-
dc_link.c:1176-1184 fills the struct: enc_init_data.channel = get_ddc_line(link); enc_init_data.hpd_source = get_hpd_line(link); enc_init_data.transmitter = translate_encoder_to_transmitter(enc_init_data.encoder); with bp_funcs->get_src_obj() (atombios parser) supplying the encoder object - i.e. all three originate in the GPU VBIOS.
-
Enum domains (grph_object_defs.h:81-92, :58-68, :45-54; dc_link.c translate_encoder_to_transmitter/get_ddc_line/get_hpd_line bodies): transmitter can be -1 (UNKNOWN) or 4..9 (UNIPHY_E/F, NUTMEG_CRT, TRAVIS_CRT/LCD, UNIPHY_G); channel 0 (UNKNOWN) or 5..8 (DDC5/6/VGA/ I2C_PAD -> idx 4..7); hpd_source 4..6 (HPD5/6, HPD_SOURCEID_UNKNOWN=6). Every one of those values indexes outside the 4-entry tables.
-
The resulting out-of-bounds
const struct ..._registers *is stored in the constructed encoder (dcn10_link_encoder_construct) and used as the base for all later REG_* MMIO in dcn10_link_encoder.c / dcn10_stream_encoder.c - i.e. wild MMIO at VBIOS-chosen offsets, not a userspace-readable leak.
Why Low: attacker must control GPU firmware (same boundary as DF-4216, rated Low). No unprivileged or remote path exists.
Why not guest-testable: guest has no AMD PCI device at all (env.txt); amdgpu.ko never probes; dcn10_create_resource_pool is unreachable.
Fix: one bounds check in dcn10_link_encoder_create returning NULL on
out-of-range indices (fix.diff; git apply --check clean against
sys/; ARRAY_SIZE already used in this compilation unit's sibling
dcn10_stream_encoder.c). The NULL return is already handled gracefully
by dc_link.c:1186-1190 (link_enc_create_fail path).
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: no DCN1 hardware available to run a baseline or patched test; fix verified by inspection and git apply --check against sys/.
findings/poc/DF-4312/fix.diff
Confirmed kernel references
- sys/dev/drm/amd/display/dc/dcn10/dcn10_resource.c:744
- sys/dev/drm/amd/display/dc/dcn10/dcn10_resource.c:745
- sys/dev/drm/amd/display/dc/dcn10/dcn10_resource.c:746
- sys/dev/drm/amd/display/dc/core/dc_link.c:1177
- sys/dev/drm/amd/display/dc/core/dc_link.c:1008
- sys/dev/drm/amd/display/include/grph_object_defs.h:81
Detail
Exploit chain
supply/flash a crafted or foreign VBIOS on a Raven-class DCN1 APU whose display object table declares e.g. an EXTERNAL_TRAVIS encoder, a DDC5/VGA DDC line, or HPD_SOURCEID5/6 for a connector -> amdgpu attach -> dc_link_construct -> dcn10_link_encoder_create performs the OOB const reads -> stored bogus register bases -> subsequent link-encoder register programming issues MMIO at VBIOS-chosen offsets (hang/machine-check/wrong-hardware pokes). Requires physical/firmware control of the GPU; no unprivileged or remote path.
Evidence (decisive lines)
VERDICT.md (line-accurate index-domain proof), README.md (value-domain table), env.txt (guest hardware absence: amd-gpu-count=0), fix.diff (bounds check, git-apply clean)
PoC changes
No executable PoC authored: the trigger requires DCN1 silicon + crafted VBIOS which the audit guest cannot provide; the evidence pack documents the static proof instead.
Verified recommended fix
Bounds-check transmitter/channel/hpd_source against ARRAY_SIZE of the three register tables in dcn10_link_encoder_create and return NULL on violation (see fix.diff).
Verdict
Code defect is certain: dcn10_resource.c:744-746 indexes the 4-entry const tables link_enc_regs/link_enc_aux_regs/link_enc_hpd_regs with transmitter (producible values -1 and 4..9 via dc_link.c translate_encoder_to_transmitter incl. TRANSMITTER_UNKNOWN=-1 and legitimate UNIPHY_E/F/G + external TRAVIS/NUTMEG encoders), channel-1 (producible -1 via CHANNEL_ID_UNKNOWN and 4..7 via DDC5/6/VGA/I2C_PAD) and hpd_source (producible 4..6 incl. HPD_SOURCEID_UNKNOWN=6) - all from the GPU's VBIOS object tables with no validation; the OOB const-array reads become the encoder's MMIO register bases (wild MMIO, minimum a bring-up hang, no userspace-readable leak). Could NOT be executed on the audit guest: pciconf shows zero AMD/ATI devices, so dcn10_create_resource_pool is unreachable at runtime. Same trust boundary and severity as DF-4216 (Low, crafted/foreign VBIOS required). fix.diff adds a bounds check returning NULL, which dc_link.c:1186-1190 already handles gracefully; git apply --check clean.
No comments yet.