DF-4862 / verdict.json
{ "finding_id": "DF-4862", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Harness-verified on the DragonFly 6.5-DEVELOPMENT guest using verbatim function bodies with 1:1 struct layouts (no AMD GPU on the audit guest; methodology identical to DF-4774/DF-4665/DF-3683): amdgpu_cgs_get_firmware_infos CP_MEC branch (amdgpu_cgs.c:234-235) sets info->image_size = jt_offset<<2 from an unvalidated firmware-header dword. A crafted but fully self-consistent MEC header (jt_offset=0x4000 dwords, validate PASSES) yields TOC entry data_size_byte=0x10000 against a staged window of 0x2000 -> the SMU DMA engine (PPSMC_MSG_LoadUcodes via smu7_smumgr.c:405/424/431) reads 57344 bytes beyond the staged fw_buf window (adjacent GTT pages disclosed device-side, unmapped range -> GPU page fault / SMU hang -> driver-load DoS), with ZERO kernel-side OOB reads in the staging path (distinct from DF-1947: image_size<<2 up to 0xFFFFFFFC, wraps mod 2^32). Legit header fully in-bounds (no false positive). Patched twin (bound jt_offset<<2 <= ucode_size_bytes at the construction) rejects the crafted header before any TOC entry while the legit header still loads; 3/3 deterministic runs.", "exploit_chain": "crafted MEC gfx firmware file (root-placed /usr/lib/firmware/amdgpu, or compromised fw source) -> driver attach on SMU-load asics (Topaz/Tonga/Fiji/Polaris/VegaM: gfx_v8_0.c:1204 registers ucode[CP_MEC1].fw) -> amdgpu_ucode_validate passes (datasize==size_bytes only) -> amdgpu_ucode_init_bo stages ALIGN(ucode_size,PAGE)+ALIGN(jt<<2,PAGE) -> powerplay start_smu -> smu7_request_smu_load_fw populates CP_MEC TOC entry with data_size_byte = jt_offset<<2 (unbounded) -> TOC uploaded, PPSMC_MSG_LoadUcodes -> SMU DMA engine reads data_size_byte bytes from the MEC1 staged page: up to ~4 GB window; adjacent GTT pages disclosed into SMC SRAM (device-side, no userland readback absent a second bug), unmapped range -> GPU page fault / SMU hang / repeated failed loads -> system-level DoS. Ceiling: DoS + limited device-side disclosure (kernleak family convention, same as DF-4774).", "evidence": "findings/poc/DF-4862/run.log: [2] ... TOC entry[0]: data_size_byte=0x10000 ... staged MEC window=[0x100003000,0x100005000) then SMU DMA: 8192 bytes in-bounds, 57344 bytes BEYOND the staged window (hit UNMAPPED GPU page at +0x13000 -> GPU page fault / SMU hang) and RESULT: SMU-SIDE OOB DMA READ CONFIRMED, with kernel-side OOB reads during init_bo/patch_jt: 0 bytes (fully in-bounds); [1] legit header 3072 bytes fully in-bounds; patched-twin [2] shows populate CP_MEC entry: -22 (rejected at the cgs construction - FIX VALIDATED) and [1] unchanged. Identical markers in run.2.log and run.3.log.", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_cgs.c:234-235", "sys/dev/drm/amd/amdgpu/amdgpu_cgs.c:219-231", "sys/dev/drm/amd/amdgpu/amdgpu_ucode.c:251-259", "sys/dev/drm/amd/amdgpu/amdgpu_ucode.c:449-485", "sys/dev/drm/amd/powerplay/smumgr/smu7_smumgr.c:305-327", "sys/dev/drm/amd/powerplay/smumgr/smu7_smumgr.c:405", "sys/dev/drm/amd/powerplay/smumgr/smu7_smumgr.c:424-431", "sys/dev/drm/amd/amdgpu/gfx_v8_0.c:1204", "sys/dev/drm/amd/amdgpu/amdgpu_ucode.h:61-66" ], "poc_changes": "Harness authored fresh for this finding (no prior seed): verbatim fw_type_convert + amdgpu_get_firmware_version + non-SMU branch of amdgpu_cgs_get_firmware_info + amdgpu_ucode_validate + init_single_fw/patch_jt/init_bo staging math + smu7_populate_single_firmware_entry; _Static_assert-proven struct offsets; mock GTT (staged BO + adjacent mapped page + PROT_NONE guard) walked by a mock SMU DMA engine. Patched twin generated from the same source with only the two fix.diff checks added.", "attempts": 3, "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": 420, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -O0 -g -Wall -o cgs_mec_jt_oob cgs_mec_jt_oob.c; cc -O0 -g -Wall -o cgs_mec_jt_oob_patched cgs_mec_jt_oob_patched.c)", "run_cmd": "sh run.sh", "code_hash": "16a0787bb19af20dc874ccd3096b84f344608a083ed4127c6416fdfd8a4938a2", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched-twin harness (fix.diff bound jt_offset<<2 <= ucode_size_bytes at the amdgpu_cgs.c:234-235 construction + the DF-4863 index guard; every other byte identical) rejects the crafted header with -EINVAL before any TOC entry is populated (no SMU DMA attempted), while the legit-header scenario [1] still loads 3072 bytes fully in-bounds. 3/3 deterministic runs. Full kernel rebuild not performed: no AMD GPU exists on the audit guest, so the verbatim-function harness is the faithful validation surface (same methodology as DF-4774, whose SMU-branch fix used the identical pattern).", "fix_evidence": "findings/poc/DF-4862/run.log patched-twin sections: scenario [1] SMU DMA: 3072 bytes in-bounds, 0 bytes BEYOND (unchanged) and scenario [2] populate CP_MEC entry: -22 (rejected at the cgs construction - FIX VALIDATED); fix.diff passes git apply --check against sys/.", "notes": "Second unchecked-header-window in amdgpu_cgs_get_firmware_info (pass-2 sweep of every branch after DF-4774 bounded only the SMU branch). JT1/JT2 branch verified self-consistent with staging (no finding). Overlap note: an extreme jt_offset also faults kernel-side in patch_jt (DF-1947 site, amdgpu_ucode.c:410-422) - scenario [3] demonstrates that overlap (child SIGSEGV); the clean DF-4862 window (scenario [2]) requires no kernel-side violation at all. Twins already filed: DF-4774 (SMU branch, same file/function), DF-1946/DF-1947 (validate + staging on amdgpu_ucode.c).", "recommended_fix": "In amdgpu_cgs_get_firmware_info (amdgpu_cgs.c:234-235), bound the CP_MEC window: reject unless (uint64_t)jt_offset<<2 <= ucode_size_bytes (data_size) before assigning info->image_size; also range-check fw_type_converts return before indexing adev->firmware.ucode[] (fix.diff)." } |