β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4001

Unclamped VBIOS SCLK/MCLK DPM counts overflow fiji SMC GraphicsLevel[8]/MemoryLevel[4] and the 384-byte STACK MC-arb timing table

Summary

DF-4000 (iceland) family on fiji: the SMC-table populate walks index GraphicsLevel[8], MemoryLevel[4] and the STACK SMU73_Discrete_MCArbDramTimingTable (entries[8][4], 384B) with the raw VBIOS dependency counts (u8<=255). Guest demonstrator compiles the kernel's own packed headers: sclk=12 -> 176B past GraphicsLevel; mclk=6 -> 48B past MemoryLevel (count 5 suffices); arb walk 12x6 -> deepest write 600B into the 384B stack object (216B into the caller frame; 255/255 -> 15,300B past; kernel built -fno-stack-protector). Crafted-vBIOS attach/resume heap+stack smash. Clamp fix compile-validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4001 Β· 8 files
FileTypeDescriptionSize
README.md β€” 3.3 KB ↓ raw
VERDICT.md β€” 3.8 KB ↓ raw
build.sh β€” 65 B view raw
run.sh β€” 89 B view raw
run.log β€” 3.8 KB view raw
env.txt β€” 493 B view raw
fix.diff β€” 7.7 KB view raw
trigger_analysis.c β€” 17.3 KB view raw

DF-4001 β€” Unclamped VBIOS SCLK/MCLK DPM counts overflow fiji SMC state tables incl. a 384-byte STACK MC-arb table

File: sys/dev/drm/amd/powerplay/smumgr/fiji_smumgr.c Class: kernel heap + stack OOB write (memcorrupt) β€” DF-4000 (iceland) / DF-3908/3939/3940 family, fiji variant Severity: High Β· Confidence: certain (source) Β· runtime hardware-gated

Build / Run / Expected (on the DragonFly audit guest)

./build.sh     # cc -O2 -Wall -I /usr/src/sys/dev/drm/amd/powerplay/inc -o fiji_walks trigger_analysis.c
./run.sh       # ./fiji_walks

Expected (see run.log): the demonstrator compiles the kernel's own packed headers (smu73.h, smu73_discrete.h) so sizeof/offsets are the kernel's, then reproduces the walks with attacker-chosen VBIOS counts (sclk=12, mclk=6):

DF-4001a ... sclk_count=12: 4 OOB level writes, 176 bytes past GraphicsLevel[8]
DF-4001b ... mclk_count=6 (Fiji SMU73 bound is 4!): 2 OOB writes, 48 bytes past MemoryLevel[4]
DF-4001c ... sclk=12 mclk=6: 26 PAST the 32-entry table (max linear entry 49 of 32)
            deepest write offset 600B into a 384B stack object -> 216 bytes into the caller frame

Why it is real (source chain, attacker = crafted vBIOS / eGPU flash)

  1. VBIOS PowerPlay table (v1) ucNumEntries (u8 ≀ 255) is stored verbatim as the dependency-table count: process_pptables_v1_0.c:389 (mclk), :436 (sclk).
  2. Fiji is pinned to this parser: hwmgr.c:86 (PP_TABLE_V1 default) + hwmgr.c:127 (CHIP_FIJI β†’ fiji_smu_funcs; only CI/TOPAZ drop to PP_TABLE_V0).
  3. smu7_hwmgr.c:783-810 dedups the dep tables into dpm_table.sclk_table.count / mclk_table.count β€” unclamped against the fixed dpm_levels[8] (DF-1166, pass 1).
  4. fiji_populate_all_graphic_levels (fiji_smumgr.c:1026-1036) walks count entries into levels[i] = smc_state_table.GraphicsLevel[SMU73_MAX_LEVELS_GRAPHICS=8] (smu73_discrete.h:249); [count-1] store at :1042; pcieDpmLevel loops :1055-1057, :1080-1081. struct fiji_smumgr is a single kzalloc (fiji_smumgr.c:339) β†’ heap OOB write past the object for count > ~45 (GraphicsLevel sits at DpmTable+236 of 1632B).
  5. fiji_populate_all_memory_levels (:1238-1247, [0] fixups :1250/:1257, [count-1] :1265) into MemoryLevel[SMU73_MAX_LEVELS_MEMORY=4] (smu73_discrete.h:251) β€” count 5 already overflows.
  6. fiji_program_memory_timing_parameters (:1533) declares SMU73_Discrete_MCArbDramTimingTable arb_regs; on the stack (384 B, entries[8][4], smu73_discrete.h:154-157) and the nested loops :1537-1546 write entries[i][j] for i<sclk_count, j<mclk_count β€” with 255/255 the walk runs to 15,300 B past a 384-byte kernel-stack object (return-address direction; write content is VBIOS-derived register values).
  7. The clock-stretcher DDT walk additionally READS GraphicsLevel[j] for j<GraphicsDpmLevelCount (:1792-1795) β€” OOB read of the same count.

Sinks are reached from fiji_init_smc_table (:1964, :1988) during pp_hwmgr init (device attach / resume) β€” privileged-time execution of attacker-controlled VBIOS bytes (malicious vBIOS flash or hostile eGPU).

Fix

fix.diff clamps every walk to the SMU73 array bounds (and clamps the level counts handed to the SMC); compile-validated by full nativekernel rebuild (see fix_build.log).

VERDICT.md
↓ download raw

VERDICT β€” DF-4001

Status: inconclusive at runtime (hardware-gated); source-certain.

Reproduced

The index arithmetic and corruption extents are reproduced on the audit guest by trigger_analysis.c (run.log), using the kernel's own packed headers:

walk sink (kernel object) bound demo counts OOB observed
fiji_populate_all_graphic_levels:1026 GraphicsLevel[8] (heap, fiji_smumgr kzalloc @ fiji_smumgr.c:339) 8 sclk=12 4 level writes / 176 B past; [count-1] store at :1042 also OOB
fiji_populate_all_memory_levels:1238 MemoryLevel[4] (heap) 4 mclk=6 2 writes / 48 B past (mclk count 5 suffices)
fiji_program_memory_timing_parameters:1537-1546 SMU73_Discrete_MCArbDramTimingTable STACK object (:1533) entries[8][4] 12Γ—6 26 entry writes past, max linear entry 49 of 32, 216 B into caller frame; 255/255 β†’ 15,300 B past

Struct geometry measured with the kernel's own headers: sizeof(DpmTable)=1632, GraphicsLevel@+236 (44 B/level), MemoryLevel@+612 (24 B/level), arb entry 12 B, arb table 384 B.

The in-kernel fault could not be observed: the audit guest has no AMD GPU (env.txt: amd-gpu=0, virtio-only), and the trigger requires a Fiji (Fiji XT / R9 Fury) card with a crafted vBIOS β€” physical attacker / malicious vBIOS / hostile eGPU, same gate as DF-4000 (iceland), DF-3908/3939/3940.

Why the counts are attacker-controlled and unclamped (three-hop provenance)

  1. process_pptables_v1_0.c:425-436 get_sclk_voltage_dependency_table and :378-391 get_mclk_voltage_dependency_table set table->count = ucNumEntries (VBIOS byte, ≀ 255); the allocation matches the count, so no clamp exists anywhere.
  2. hwmgr.c:86 sets pp_table_version = PP_TABLE_V1 by default; hwmgr.c:127-130 pins CHIP_FIJI to fiji_smu_funcs (only CI/TOPAZ downgrade to PP_TABLE_V0).
  3. smu7_hwmgr.c:783-810 (smu7_setup_dpm_tables_v1) copies the dep-table entries (dedup by clk) into dpm_table.sclk_table/mclk_table.count β€” unclamped vs MAX_REGULAR_DPM_NUMBER(8); the en-route dpm_levels[8] smash is DF-1166 (pass 1, smu7_hwmgr.c). The fiji-side sinks below are this file's: GraphicsLevel[8], MemoryLevel[4], stack entries[8][4], plus the :1792-1795 DDT GraphicsLevel[j] OOB read.

No intervening clamp exists: grep the whole populate path β€” the only min() on pcie is smu7_hwmgr.c:561 (which is why LinkLevel is safe; see negative notes in the audit result).

Exploitability (why High)

  • Heap: count 12 already writes 176 B past GraphicsLevel[8] inside the struct fiji_smumgr allocation, corrupting MemoryACPILevel/MemoryLevel/ LinkLevel/Uvd/Vce/Acp/Samu/Ulv/Smio...; count β‰₯ ~45 runs past the whole kzalloc into adjacent heap (count 255 β†’ 10.8 KB). Content is VBIOS-influenced (clocks/voltages per level).
  • Stack: the arb walk is a linear stack smash up to 15.3 KB past a 384-byte frame object in the return-address direction; DragonFly kernels are built without stack protectors by default. Trigger context = driver attach/resume (privileged), so this is malicious-vBIOS-class, not unprivileged-local.

Fix validation

fix.diff clamps all walks/counts to the SMU73 bounds. Applied to pristine guest /usr/src (md5 800babd0.. β†’ d50bdc81..), full make nativekernel KERNCONF=X86_64_GENERIC rebuild completed (fix_build.log / fix_kernel_full.log), then reverted. Runtime re-test impossible on this guest (no AMD GPU).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline no→ patch + rebuild →patched clean

fixed (compile-validated): both diffs applied to clean guest /usr/src (fiji_smumgr.c md5 800babd0..->d50bdc81.., i915_gem_execbuffer.c 334c7bd9..->dc882976..), make nativekernel KERNCONF=X86_64_GENERIC completed with error_count=0 under -Werror (fix_build.log); behavior additionally model-verified for DF-4007 (PATCHED run balanced). Runtime re-test impossible on this guest (no AMD/Intel GPU). Patch reverted after validation.

findings/poc/DF-4001/fix.diff; findings/poc/DF-4001/fix_build.log; findings/poc/DF-4007/run.log (PATCHED model run for DF-4007)
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT (nativekernel X86_64_GENERIC, patched, -Werror; see fix_build.log)

Confirmed kernel references

Detail

Exploit chain

crafted vBIOS (malicious flash / hostile eGPU) -> pp_hwmgr init at attach/resume -> fiji_init_smc_table -> SMC-table populate walks with ucNumEntries>bounds -> OOB writes into the fiji_smumgr heap allocation and the 384B kernel-stack arb table (return-address direction; -fno-stack-protector confirmed in the guest kernel build) -> kernel heap corruption / stack control-flow hijack in privileged context

Evidence (decisive lines)

run.log (DF-4001a/b/c sections: +176B/+48B heap, 26/72 stack entry writes OOB, 216B into caller frame, 255/255 -> 15,300B past the 384B stack object); VERDICT.md three-hop provenance; fix_build.log; env.txt (amd-gpu=0)

PoC changes

Pass-2 reviewer authored demonstrator compiling the kernel's own smu73.h/smu73_discrete.h (packed) so sizeof/offsets are the kernel's; fix.diff generated mechanically against the pristine file and compile-validated via full nativekernel rebuild on the guest (shared build with DF-4001..DF-4007).

Verified recommended fix

Clamp all fiji SMC-table walks to SMU73_MAX_LEVELS_GRAPHICS/MEMORY (and the arb walk to SMU__NUM_SCLK_DPM_STATE x SMU__NUM_MCLK_DPM_LEVELS) as in fix.diff; structurally, clamp the counts at the parser (DF-1574/DF-1166 family)

Verdict

Source-certain kernel heap+stack OOB writes: the fiji (SMU73) SMC-table populate walks index GraphicsLevel[8], MemoryLevel[4] and the 384-byte STACK MCArbDramTimingTable (entries[8][4]) with the raw VBIOS SCLK/MCLK dependency counts (u8 ucNumEntries<=255, unclamped through process_pptables_v1_0.c -> smu7_setup_dpm_tables_v1 -> dpm_table, fiji pinned to this path at hwmgr.c:86/127). Guest demonstrator with the kernel's own packed headers reproduces the exact index math (sclk=12/mclk=6: 4/12 GraphicsLevel writes +176B past GraphicsLevel[8], 2/6 MemoryLevel writes +48B past MemoryLevel[4] (Fiji bound is 4 - count 5 suffices), 26/72 arb entry writes with the deepest at linear entry 49 of 32, 216B into the caller frame; 255/255 reaches 15,300B past the stack object). The in-kernel fault could not be observed - no AMD GPU in QEMU and the trigger requires a Fiji card with a crafted VBIOS (malicious vBIOS flash / hostile eGPU at attach/resume). Hardware-gated, matching the DF-4000 (iceland) precedent. Fix compile-validated by full nativekernel rebuild under -Werror.