DCN10 copy of the uninitialized-*id defect: offset_to_id returns success for all eight DDC-family offsets (165-189) without writing *id β dal_gpio_service_create_irq switches on the uninitialized gpio_id; garbage==GPIO_ID_HPD smuggles DDC-line en=6 (DDC_VGA) / en=7 (I2C_PAD) into the 6-entry hpd_regs[] (OOB)
Summary
The DCN10 (Raven) unit carries the same contract violation as DF-4943/DF-4945 (proven verbatim with real DCN10 constants on the guest: id==0x7b7b7b7b UNTOUCHED across all 8 offsets, en=6/7 flagged). The dce80 GPIOPAD cross-domain producer is ABSENT here (harness-verified rejected) - the dcn10 factory has no aux_regs and the counts are consistent. Write-*id-in-DDC-branches family fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4946 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| harness_dcn10.c | β | 17.7 KB | view raw | |
| harness_fixed.c | β | 6.5 KB | view raw | |
| b1.sh | β | 312 B | view raw | |
| r1.sh | β | 175 B | view raw | |
| build.log | β | 1.0 KB | view raw | |
| run.log | β | 4.2 KB | view raw | |
| env.txt | β | 166 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| manifest.json | β | 997 B | view raw | |
| verdict.json | β | 4.7 KB | view raw |
DF-4946 β DCN10 offset_to_id returns success on all DDC-family offsets without writing *id
File: sys/dev/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c:165-189
Family: DF-4943 (dce80) / DF-4945 (dce120) β this is the DCN10 (Raven) twin.
Severity: Low (memcorrupt bucket) Β· CWE-457
Build
gcc -O2 -Wall -Wextra -o harness_dcn10 harness_dcn10.c gcc -O2 -Wall -Wextra -o harness_fixed harness_fixed.c gcc -O2 -Wall -Wextra -o harness_mask harness_mask.c # DF-4947 side-car
Run
./harness_dcn10
Expected
All eight DDC-family register offsets (REG(DC_GPIO_DDC{1..6}_A) = 0x5d91..0x5da5,
REG(DC_GPIO_DDCVGA_A) = 0x5da9, REG(DC_GPIO_I2CPAD_A) = 0x5dc1) return true
after writing only *en β *id is left untouched (sprayed 0x7b7b7b7b survives).
en=6 (DDC_VGA) and en=7 (I2C_PAD) exceed the 6-entry hpd_regs[] of
hw_factory_dcn10.c:71-78, so a garbage id == GPIO_ID_HPD(3) decodes into
define_hpd_registers β &hpd_regs[6..7] OOB const-array read whose garbage
register descriptors then drive MMIO. harness_fixed applies the fix
(*id = GPIO_ID_DDC_DATA in every DDC branch) and reports 0 residual violations.
The kernel stage needs a Raven-class ASIC + crafted VBIOS gpio_pin_lut entry (absent in QEMU β 0 AMD devices, env.txt); the deterministic contract violation is proven at unit level with the verbatim function and real DCN10 constants.
DF-4946 VERDICT β reproduced (deterministic unit-level; kernel stage needs Raven HW + crafted VBIOS)
What was run
harness_dcn10.c embeds verbatim copies of offset_to_id and id_to_offset
from sys/dev/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c (lines 61-201,
203-388) with the single substitution ASSERT_CRITICAL(x) β printf marker.
Constants are the real DCN10 values: DCE_BASE__INST0_SEG2 = 0x34C0
(vega10_ip_offset.h:277) and the mmDC_GPIO_* offsets/BASE_IDX from
dcn_1_0_offset.h:10349-10584. Enums are the real gpio_types.h values
(GPIO_DDC_LINE_I2C_PAD aliases VIP_PAD = 7; COUNT = 8).
Result (run.log)
- T1: all 8 DDC-family offsets return
truewith*iduntouched (id==0x7b7b7b7b UNTOUCHEDon every line). DDC1..6 β en 0..5, DDCVGA β en=6, I2CPAD β en=7.hpd_regs[]in hw_factory_dcn10.c:71-78 has exactly 6 entries (indices 0..5) β en 6/7 are out of bounds. - T2 (negative): raw mmGPIOPAD-style offset 0x183 (the DF-4942 dce80
deterministic producer) is rejected β DCN10 has no GPIOPAD case, so the
cross-domain producer is absent;
REG(DC_GPIO_DEBUG)correctly returns false. - T4 (negative): the HPD branch is well-formed (writes
*id, en 0..5).
Consumer chain (source-cited)
get_hpd_gpio(core/dc_link.c:96-119) callsdal_gpio_service_create_irqwithpin_info.offset/masktaken from the VBIOS atom gpio pin table (dcb->funcs->get_gpio_pin_info, bios_parser2.c:537-539).dal_gpio_service_create_irq(gpio_service.c:143-157) declaresenum gpio_id id;uninitialized and passes&idtooffset_to_id; ontrueit forwards the garbageid+ DDC-lineentodal_gpio_create_irq.dal_gpio_create_irq(gpio_service.c:366-391) acceptsGPIO_ID_HPD;dal_gpio_createβdal_gpio_open_exβdal_gpio_service_open(gpio_service.c:224-290):busyness[HPD]is 1 uint (number_of_pins[HPD]=6, hw_factory_dcn10.c:186) sois_pin_busy(en=6/7)stays inside the allocation, and if the bit is clear βcreate_hpd+define_hpd_registers(pin, 6/7).define_hpd_registers(hw_factory_dcn10.c:149-157) evaluates&hpd_regs[en]β OOB read of the 6-entry const array; the garbagestruct hpd_registers(register offsets read past the array into adjacent rodata) then drivesdal_hw_gpio_open/hw_hpdMMIO programming (wrong-register writes / reads at attacker-uncontrolled MMIO offsets) β misbehavior to panic (dos). The final stage is probabilistic (depends on the uninitialized stack slot decoding to 3).
The primary DDC consumer, dal_gpio_create_ddc (gpio_service.c:408-457),
ignores id and hardcodes GPIO_ID_DDC_DATA/CLOCK; with DCN10's
ddc_data_regs[8]/ddc_clk_regs[8] (hw_factory_dcn10.c:94-114) and
number_of_pins[DDC_*]=8 (hw_factory_dcn10.c:183-184), en 0..7 are in-bounds β
so, unlike dce80 (DF-4942), the DDC happy path itself is safe on DCN10. The
uninit-*id defect matters only through the IRQ path above.
Why not kernel-stage on this guest
env.txt: 0 AMD PCI devices in the QEMU guest β no amdgpu/DC attach, no
VBIOS under test. Same situation as the verified family twins DF-4943/4945;
their precedent (unit-level deterministic proof + cited kernel chain) is
followed exactly.
Fix validation
fix.diff adds *id = GPIO_ID_DDC_DATA; to all eight DDC-family branches.
harness_fixed.c applies the same edit to the embedded copy: every DDC-family
success now reports id=0 (WRITTEN), and with id==GPIO_ID_DDC_DATA
dal_gpio_create_irq takes its default branch and returns NULL β
define_hpd_registers can never be reached with en=6/7 (0 residual
violations, FIXEXIT=0). A kernel rebuild cannot alter the output of a
userland unit harness; structural validation matches the family precedent
(DF-4943/4945 fix_status=validated).
fix.diff applies cleanly?
Authored against the read-only sys/ tree at
hw_translate_dcn10.c:165-189; hunks are one-line insertions on distinct
case labels β no context drift.
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_testablefix.diff adds *id = GPIO_ID_DDC_DATA to all eight DDC-family branches of the dcn10 offset_to_id; harness_fixed applies the identical edit to the embedded copy and demonstrates the uninitialized id is structurally eliminated (all 8 report id=0 WRITTEN) β with id==GPIO_ID_DDC_DATA, dal_gpio_create_irq (gpio_service.c:373-381) returns NULL, so define_hpd_registers can never see en=6/7: 0 residual violations (FIXEXIT=0). A kernel rebuild cannot change userland unit-harness output; structural validation matches the family precedent (DF-4943/4945).
fix.diff (hw_translate_dcn10.c hunks); run.log 'FIXED T1' section; harness_fixed.c e781a6d68365136864cb903683e6274e967e24514209508af01df7665cd3cc0d.
Confirmed kernel references
- sys/dev/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c:165
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:148
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:366
- sys/dev/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.c:71
- sys/dev/drm/amd/display/dc/gpio/dcn10/hw_factory_dcn10.c:149
- sys/dev/drm/amd/display/dc/core/dc_link.c:115
- sys/dev/drm/amd/display/dc/bios/bios_parser2.c:537
Detail
Exploit chain
crafted VBIOS gpio_pin_lut v2.1 (data_a_reg_index = REG(DC_GPIO_DDCVGA_A)=0x5da9 or REG(DC_GPIO_I2CPAD_A)=0x5dc1, bios_parser2.c:537) -> get_hpd_gpio (dc_link.c:115) -> dal_gpio_service_create_irq -> offset_to_id true, id UNWRITTEN -> garbage id==GPIO_ID_HPD(3) -> dal_gpio_service_open(HPD, en=6/7) -> define_hpd_registers -> hpd_regs[6..7] OOB rodata read -> hw_gpio/hw_hpd MMIO at garbage offsets -> wrong-register programming/panic. Probabilistic final stage; no unpriv-user local route (requires GPU VBIOS control).
Evidence (decisive lines)
run.log T1: 'offset_to_id(REG(DDCVGA),0x100) = true en=6(id==0x7b7b7b7b UNTOUCHED)' and '(REG(I2CPAD),0x100) = true en=7(id==0x7b7b7b7b UNTOUCHED)' with the hpd_regs[6]/[7] OOB annotations; T2: 0x183 rejected (no GPIOPAD mapping β dce80-style producer absent); T4: HPD branch well-formed; fixrun section: harness_fixed reports id=0 WRITTEN on all 8 and 0 residual violations.
PoC changes
n/a β fresh harness (verbatim dcn10 offset_to_id + id_to_offset copies, real DCN10 constants: SEG2 base 0x34C0, mmDC_GPIO_DDC1_A=0x28d1 .. I2CPAD_A=0x2901, all BASE_IDX 2).
Verified recommended fix
Write *id (GPIO_ID_DDC_DATA) in every successful offset_to_id DDC-family branch (dcn10 file), matching the validated dce80/dce120 family fix.
Verdict
The DCN10 copy of offset_to_id (hw_translate_dcn10.c:165-189) has the family defect of DF-4943/DF-4945: all eight DDC-family branches (DDC1-6, DDCVGA, I2CPAD) return true after writing only en β id is never assigned. dal_gpio_service_create_irq (gpio_service.c:148-156) declares 'enum gpio_id id;' uninitialized and switches on the garbage value in dal_gpio_create_irq; garbage==GPIO_ID_HPD(3) with the DDC-line en of 6 (DDC_VGA) or 7 (I2C_PAD) reaches define_hpd_registers, which indexes the 6-entry hpd_regs[] (hw_factory_dcn10.c:71-78,149-157) out of bounds β garbage register descriptors then drive MMIO (hw_hpd/hw_gpio open path). Executed on the DF guest with a verbatim copy of the dcn10 function and real DCN10 constants (REG(DDCVGA)=0x5da9, REG(I2CPAD)=0x5dc1 β run.log T1: both return true with *id untouched; en=6/7 exceed hpd_regs). Kernel-stage execution requires a Raven-class ASIC + crafted VBIOS gpio_pin_lut (absent in QEMU: 0 AMD devices). Unlike dce80 there is no GPIOPAD case (T2 negative control), so no deterministic DF-4942 producer exists on DCN10; the primary dal_gpio_create_ddc consumer ignores id and its en 0..7 are in-bounds against the 8-entry ddc arrays β the uninit-id path via the HPD IRQ route is the only OOB route and is probabilistic.
No comments yet.