DF-4792 / verdict.json
{ "finding_id": "DF-4792", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "Signed int16_t table_id in vega20_copy_table_from_smc/_to_smc passes the only bounds check (table_id < TABLE_COUNT) for every API value 0x8000..0xFFFF, indexing priv->smu_tables.entry[] out of bounds before the array (struct vega20_smumgr has entry[] at offset 0, so negative indexes hit the preceding heap object of the kzallocd backend). Proven in-kernel with a verbatim-extraction KLD harness: a groomed entry[-1] (version=1,size=8,table=victim) yields a controlled 8-byte kernel write (to_smc, rc=0 success) and an OOB kernel read (from_smc, rc=0); 0x8000->entry[-32768] shows a 32KB pre-object read window. Impact none as shipped because every in-tree vega20 caller (vega20_hwmgr.c:760..2961) passes compile-time constant table ids and no sysfs/ioctl forwards a user-controlled 16-bit selector - the DF-2021/DF-2034/DF-2072 family twin that pass 1 missed for vega20; latent memcorrupt primitive, Medium.", "exploit_chain": "Conditional only (no trigger on stock system): if a future caller forwards a user-controlled 16-bit table_id -> groom heap chunk preceding hwmgr->smu_backend (kzalloc vega20_smumgr.c:386, device-lifetime stable) -> entry[-1].table=target, .size=len -> vega20_copy_table_to_smc = arbitrary kernel write returning 0 (success). No unprivileged trigger exists today, so no uid=0 chain.", "evidence": "run.log decisive lines: to_smc(0xFFFF) rc=0 victim[0..7]=4141414141414141 << CONTROLLED KERNEL WRITE and from_smc(0xFFFF) rc=0 ... << OOB KERNEL READ (baseline, param type: int16_t); PP: Invalid SMU Table ID! + untouched for all probes on the uint16_t fixed variant; fix_build.log: patched nativekernel 35829 lines 0 errors RC=0 with vega20_smumgr.o compiled -Werror clean", "kernel_refs": [ "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:163", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:170", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:192", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:203", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:210", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:217", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:563", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.c:386", "sys/dev/drm/amd/powerplay/smumgr/vega20_smumgr.h:37", "sys/dev/drm/amd/powerplay/inc/smu11_driver_if.h:837", "sys/dev/drm/amd/powerplay/inc/hwmgr.h:218", "sys/dev/drm/amd/powerplay/inc/smumgr.h:114" ], "poc_changes": "PoC authored fresh (no seed): verbatim extraction of vega20_smumgr.c:163-236,563-574 + vega20_smumgr.h:29-43 + pp_debug.h PP_ASSERT into a DragonFly KLD; only the MMIO SMC-message sender stubbed to return 0. Two harness-only panics during development (DFly DECLARE_MODULE takes moduledata_t struct, not a handler; uninitialized local hwmgr.smu_backend in the sanity probe) - both fixed, neither a property of the target code. Trigger ids passed via volatile uint16_t (as they arrive via the public API) after GCC -Warray-bounds statically flagged the constant-propagated OOB index.", "attempts": 6, "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": 1800, "guest_dirty": 0, "build_cmd": "sh build.sh (make SYSDIR=/usr/src/sys; second variant after sed int16_t->uint16_t)", "run_cmd": "sh run.sh (kldload ./df4792.baseline.ko; kldunload -i <id>; kldload ./df4792.fixed.ko; kldunload -i <id>)", "code_hash": "84eab35c05d9b852a95d07601019a7b3752268feb2da9d6cec4a7343ead68df6", "notes": "Family twin of DF-2021 (vega12), DF-2034 (vega10), DF-2072 (smu10) - all same pattern+fix. vega20 additionally audited clean on: smu_init/smu_fini error-path pairing (err0..err4 each free exactly the prior successes; amdgpu_bo_free_kernel is NULL-safe amdgpu_object.c:362; kfreed backend can never see a later smu_fini because amdgpu_device.c:1899 gates sw_fini on sw status set only after successful sw_init), SMC-message timeout handling (PPSMC_Result_OK=0x1 so a timeout read of 0 yields -EIO, not false success), DF-4774 non-applicability (vega20_smu_init only NULL-gates cgs info.kptr at :383, never copies from it - no fw-header-driven upload exists in this file), and workload_type only riding inside SMU message params (:259,:285), never a kernel index. Guest reset with-src after verification.", "recommended_fix": "Widen both internal copy functions table_id from int16_t to uint16_t (matching the uint16_t public API) so the existing < TABLE_COUNT guard compares unsigned - identical to the applied-and-verified DF-2021/DF-2034/DF-2072 fixes.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (int16_t -> uint16_t on vega20_copy_table_from_smc/_to_smc table_id) applied to the guest /usr/src: (a) harness-level - the uint16_t-built variant rejects 0xFFFF and 0x8000 with Invalid SMU Table ID! -EINVAL and leaves the victim buffer untouched, while in-bounds behavior (unallocated slot -> version-check -EINVAL) is unchanged; (b) kernel-level - full make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with 0 error: lines in 35829 (default -Werror), vega20_smumgr.o compiled clean into the amdgpu module. Baseline (unpatched) behavior reproduced 4x before the fix. Bad behavior GONE on the patched build.", "fix_evidence": "fix_run.log (patched-variant rejection output); fix_build.log (compile line for vega20_smumgr.c with -Werror, --- vega20_smumgr.o --- marker, Kernel build for X86_64_GENERIC completed, RC=0, 0 errors); run.log baseline sections for contrast" } |