DF-4942 / verdict.json
{ "finding_id": "DF-4942", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "offset_to_ids mmGPIOPAD_A case (hw_translate_dce80.c:164-167) reports success with en in [0..30] while the only reverse-map consumer, dal_gpio_create_ddc (gpio_service.c:408-457), discards the id output and treats en as a gpio_ddc_line, letting define_ddc_registers index ddc_data_regs[8]/ddc_clk_regs[8] (hw_factory_dce80.c:122) \u2014 for en 8..30 this is a deterministic OOB const-array read whose register descriptors then drive MMIO programming. The verbatim function copy executed on the DragonFly guest reproduces the decode (offset_to_id(mmGPIOPAD_A,1<<20) == true, en=20); the kernel stages are a straight-line code trace (four hops, en unmodified). End-to-end kernel execution requires an AMD ASIC with a crafted VBIOS (usClkA_RegisterIndex == 0x183), absent from the QEMU guest. DoS-class (panic/wrong-register MMIO at driver attach); no unprivileged-user reach on stock hardware.", "exploit_chain": "crafted VBIOS GPIO_I2C record (usClkA_RegisterIndex=0x183 mmGPIOPAD_A, ucClkA_Shift in 8..30) -> dc_link_ddc.c:272 dal_gpio_create_ddc -> offset_to_id success with en=8..30 -> gpio_service.c:428-437 DDC gpio objects with foreign-domain en -> first DDC open -> define_ddc_registers -> ddc_data_regs[en] OOB rodata read -> REG_UPDATE/REG_GET MMIO at garbage offsets -> panic/MCE or display-block state corruption. (Same input family as DF-4827 but different consumer array: ddc_data_regs/ddc_clk_regs sized 8 vs the GPIOPAD domain 0..30.)", "evidence": "run.log T2 section: offset_to_id(mmGPIOPAD_A,1<<20) = true id=GPIO_ID_GPIO_PAD(4) en=20 -> BUG(DF-4942) ... ddc_data_regs[20] OOB (hw_factory_dce80.c:122); T3 negative controls prove the internal GPIOPAD bound is sound; fixrun.log: consumer guard rejects the decode, 0 regressions; harness_dce80.c is a verbatim copy of hw_translate_dce80.c:44-400 with real header constants.", "kernel_refs": [ "sys/dev/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c:164", "sys/dev/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c:172", "sys/dev/drm/amd/display/dc/gpio/gpio_service.c:418", "sys/dev/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.c:84", "sys/dev/drm/amd/display/dc/gpio/dce80/hw_factory_dce80.c:122", "sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:272", "sys/dev/drm/amd/display/dc/bios/bios_parser.c:1881" ], "poc_changes": "n/a \u2014 harness written fresh (verbatim function copy; BREAK_TO_DEBUGGER mapped to printf; real DCE8 register constants from dce_8_0_d.h/dce_8_0_sh_mask.h/smu_7_0_1_d.h).", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 12, "guest_dirty": 0, "build_cmd": "gcc -O2 -Wall -Wextra -o harness_dce80 harness_dce80.c", "run_cmd": "./harness_dce80", "code_hash": "8df7d21c82ff5c569875602af619df7050af7a55a26992ab957c8f557669120c", "fix_status": "validated", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (translate sets *id=GPIO_ID_DDC_DATA on all DDC cases + dal_gpio_create_ddc rejects id != GPIO_ID_DDC_DATA) applied to the harness model: the en=20 GPIOPAD decode is rejected at the consumer (accepted=0), 0 regressions across all test classes (fixrun.log); git apply --check clean against sys/.", "fix_evidence": "fixrun.log lines: -> FIXED: translate still decodes en=20, but fixed consumer guard (id != GPIO_ID_DDC_DATA) rejects: accepted=0 -> no ddc_data_regs[20] OOB and 0 regressions after fix (expect 0); fix.diff in pack.", "notes": "Kernel-path execution impossible on this guest (no AMD ASIC: pciconf 0x1002 count=0, amdgpu not loaded \u2014 env.txt); unit harness executes the verbatim audited functions instead. Guest left clean (files under ~/dfpoc only, no kernel changes).", "recommended_fix": "Set *id=GPIO_ID_DDC_DATA in all DDC-family offset_to_id cases and make dal_gpio_create_ddc reject decodes whose id is not GPIO_ID_DDC_DATA." } |