DF-5224 / verdict.json
{ "finding_id": "DF-5224", "status": "untested", "reproduced": false, "impact": "none", "confidence": "speculative", "verdict": "Info hardening, verified at source level only (per contract for Low/Info findings; the QEMU guest has no AMD GPU and no caller can produce the trigger). bios_get_image() (bios_parser_helper.c:36-44) is this files sole memory-safety boundary and its check `offset + size < bp->bios_size` is written in wrap-prone 32-bit arithmetic: a wrapped sum below bios_size returns bp->bios+offset as a wild kernel pointer that ~60 GET_IMAGE sites immediately dereference as atom-table structs. Reachability was hunted and KILLED: every caller offset is the sum of at most two little-endian uint16_t VBIOS fields plus small constants (max ~0x1FFFE; atomfirmware.h:381-424 master-data-table offsets are all uint16_t; bios_parser.c:137/164/234/297/887/1527/1988/2015/2884-2923), and every size is sizeof(struct) (<= ~4KB) or *number*sizeof(uint16_t) with number a uint8_t image byte (<=510, product computed in 64-bit via sizeof) at bios_parser.c:2022 \u2014 so offset+size (~0x21000 max) cannot wrap uint32. Distinct from DF-4168 (bios_parser2.c:1930: bios_size is content-derived bios[2]*512 instead of the allocated buffer length \u2014 wrong reference point, not wrong arithmetic) and composes with its fix. Recommended rewrite is overflow-safe: offset < bios_size && size <= bios_size - offset.", "exploit_chain": "", "evidence": "README.md (wrap math + caller-by-caller bound proofs); VERDICT.md (full narrative incl. register-helper and shift/mask audits); verify.sh + run.log (mechanical caller-bounds audit, incl. BIOS_SCRATCH_6 nonzero on all six constructible DCs); fix.diff (git apply --check clean)", "kernel_refs": [ "sys/dev/drm/amd/display/dc/bios/bios_parser_helper.c:40", "sys/dev/drm/amd/display/dc/bios/bios_parser_helper.h:39", "sys/dev/drm/amd/display/dc/bios/bios_parser.c:2878", "sys/dev/drm/amd/display/dc/bios/bios_parser.c:2022", "sys/dev/drm/amd/display/dc/bios/bios_parser2.c:1930", "sys/dev/drm/amd/include/atomfirmware.h:381" ], "poc_changes": "not applicable \u2014 no PoC (Info hardening; trigger set provably empty on this tree; guest has no AMD GPU)", "attempts": 0, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026\troot@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (guest up, not used \u2014 no trigger path)", "runtime_sec": 0, "guest_dirty": 0, "build_cmd": "", "run_cmd": "findings/poc/DF-5224/verify.sh > run.log; git apply --check findings/poc/DF-5224/fix.diff", "code_hash": "", "notes": "Pass 2. File compiled into live amdgpu.ko (amdgpu/Makefile:208, CONFIG_DRM_AMD_DC=1 in kconfig.h:23); image is firmware-supplied (amdgpu_dm.c:410 -> dc.c:639). Rest of the 90-LOC file audited clean: FN shift/mask pairs coherent (ATOM_S6_ACC_MODE 0x10<<4, ATOM_S6_CRITICAL_STATE 0x100<<8, atombios.h:6959/6963/7031/7035); BIOS_SCRATCH_6 nonzero on every constructible DC (dce80/100/110/112 =0x5CF, dce120/dcn10 NBIO); the BIOS_SCRATCH_3 guard at :86 is deliberate (pre-SOC15 bios_regs only populate SCRATCH_6) and the 0xFFFF mask matches the 16-bit field; < comparator is the conservative direction; bp->bios NULL-guarded. Explicitly NOT re-reporting DF-4168 (bios_size provenance) or caller-side DF-3942/DF-1299.", "recommended_fix": "bios_parser_helper.c:40 \u2014 rewrite as `if (bp->bios && offset < bp->bios_size && size <= bp->bios_size - offset)` (see fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": false, "fix_patched_reproduced": false, "fix_verdict": "No behavioral delta to validate: the finding has no runtime trigger on this tree (no caller can wrap the sum) and the guest has no AMD GPU. The one-line fix is behavior-identical for every honest input (all currently-passing calls still pass: offset<size and size<=size-offset holds whenever offset+size<size in true arithmetic), converts wrap-candidate inputs from wild pointer to NULL; git apply --check clean.", "fix_evidence": "fix.diff" } |