DF-3855 / verdict.json
{ "finding_id": "DF-3855", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain parser desynchronisation: the non-IGP branch of radeon_combios_external_tmds_setup() walks the ext-TMDS init stream and every command case advances `index` past its operands EXCEPT case 0, which reads val = RBIOS32(index) and writes WREG32(reg, val) without index += 4 (radeon_combios.c:2955-2959), unlike the IGP branch of the same function (radeon_combios.c:2900-2905). After the first plain register write, the low half of the just-written VALUE is re-read as the next command id, the 0xffff terminator is swallowed as operand bytes, and the walk continues past the record stream executing data as MMIO/PLL/I2C commands. Harness proves it on a well-formed stream: buggy walk executes 2 MMIO writes (second one an unintended case-2 RMW at reg 0x5554 built from the terminator and following data) and terminates only at +0x38 vs the fixed walks single write terminating at the terminator (+0x30). Inherited verbatim from upstream (verified Linux v5.10:2965-2969). Security delta is bounded: a malicious-VBIOS author already controls the stream (DF-1251 family), so this matters mainly for benign-but-corrupt images turning valid init sequences into unintended MMIO writes (GPU register corruption/hang). Kernel-side run not possible on the virtio-only guest (no radeon device).", "exploit_chain": "corrupt/truncated ext-TMDS init table (or crafted VBIOS) on a board with a sil164-class external TMDS transmitter -> every modeset runs the desynced walk -> value/terminator bytes executed as commands -> unintended WREG32/WREG32_PLL writes -> GPU register corruption/hang", "evidence": "run.log: buggy walk [case 0 ... DOES NOT advance index] WREG32(0x0004, 0x55555555) then unintended case 2 (RMW): reg 0x5554 ... WREG32(0x5554, 0xaaaa0001) then terminated at index 0x0038 vs fixed walk WREG32(0x0004, 0x55555555) terminated at 0x0030; fix_apply.log: hunk #1 succeeded at 2955", "kernel_refs": [ "sys/dev/drm/radeon/radeon_combios.c:2950-2959", "sys/dev/drm/radeon/radeon_combios.c:2900-2905", "sys/dev/drm/radeon/radeon_legacy_encoders.c:985" ], "poc_changes": "harness written from scratch (no seed); first build failed - the header comment contained nested C comment markers copied from kernel code and em-dashes (gcc: stray \\342, -Wcomment); rewritten with bracket-style comment text", "attempts": 2, "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": 22, "guest_dirty": 0, "build_cmd": "./build.sh (cc -O2 -Wall -o harness harness.c)", "run_cmd": "./run.sh", "code_hash": "d043b10e363cff3147fa879b724e5ea8755c78f40e50f73836e0de9fd68489a7", "notes": "The out-of-table walk the desync causes belongs to the DF-1251 family (no bounds vs bios allocation); THIS finding is the desync itself (in-bounds data executed as commands) - fixing either does not fix the other. Severity Low.", "recommended_fix": "Add index += 4 after the case-0 value read in the non-IGP branch, matching the IGP branch (fix.diff)", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "one-line fix.diff applies cleanly to pristine /usr/src (fix_apply.log); runtime fix validation impossible on this guest (no hardware)", "fix_evidence": "fix.diff; fix_apply.log" } |