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

CENTRAL FIX: V1 PowerPlay parser has no bounds-validation layer β€” every firmware USHORT offset/UCHAR count/UCHAR index is consumed unvalidated (V1 side of DF-4247; count-provenance root of the powerplay count family)

Summary

The V1 parser serves all SMU7 ASICs (Tonga/Fiji/Polaris10/11/12/VegaM) consuming every firmware offset/count/index with zero validation against soft_pp_table_size and zero clamping to destination capacities - the parse-time root behind DF-1166(V1), 4001/4002/4004, 4036/4038a/4041b, and home of knowns DF-1574..1577. CENTRAL FIX (415+/83-, git-apply OK, kernel-toolchain syntax-validated, 11/11 clamp/range unit tests on the guest): the DF-4247 range-ok/clamp layer extended to every V1 deref site - all 16 USHORT offsets range-checked (Tonga-vs-Fiji struct dispatch), counts clamped to image AND destination constants (SCLK 8/MCLK 4/MM 8/lookup 16/VCE 6), VCE indices clamped (killing the entries[-1] underflow), NULL guards + off-by-one fix. NEUTRALIZATION TABLE: combined DF-4247+DF-4388 neutralizes DF-1574/1575/1576(V1)/1577/4389 + DF-1166(V1)/4001/4002/4004/4036/4038a/4041b; per-file fixes still stand for the V0-parser ASICs (smu8/iceland/topaz), the smumgr-local voltage tables, the entries[count] off-by-ones, and the NULL-deref consumers.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4388 Β· 30 files
FileTypeDescriptionSize
README.md β€” 5.3 KB ↓ raw
VERDICT.md β€” 12.0 KB ↓ raw
fix.diff β€” 29.8 KB view raw
build.sh β€” 167 B view raw
run.sh β€” 155 B view raw
build.log β€” 2.5 KB view raw
run.log β€” 18.5 KB view raw
run.2.log β€” 18.5 KB view raw
run.3.log β€” 18.5 KB view raw
env.txt β€” 1.1 KB view raw
hw_probe.log β€” 34 B view raw
manifest.json β€” 2.1 KB view raw
verdict.json β€” 6.9 KB view raw
rig/gen_rig.sh β€” 4.6 KB view raw
rig/check_verbatim.sh β€” 3.4 KB view raw
rig/rig_shims.h β€” 7.0 KB view raw
rig/pp_rig.c β€” 41.7 KB view raw
rig/Makefile β€” 428 B ↓ download
rig/build.sh β€” 494 B view raw
rig/run.sh β€” 1.0 KB view raw
rig/pp_slice.inc β€” 46.5 KB ↓ download
rig/pp_slice_fixed.inc β€” 57.4 KB ↓ download
rig/pp_slice_verbatim.h β€” 55.0 KB view raw
rig/code_hashes.txt β€” 405 B view raw
process_pptables_v1_0_fixed.c β€” 58.7 KB view raw
v1_clamp_test.c β€” 3.4 KB view raw
syntax_check_guest.txt β€” 1.2 KB view raw
unit_test_guest.log β€” 864 B view raw
fix_build.log β€” 1.2 KB view raw
fix_run.log β€” 543 B view raw

DF-4388 β€” V1 PowerPlay parser has no central bounds-validation layer (central fix for the powerplay count family, V1 side of DF-4247)

Status: REPRODUCED (verbatim-rig evidence class, DF-4165/4247/4396). Fix A/B-validated in the same rig: fixed. See VERDICT.md for the full story.

sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c parses the Tonga-format VBIOS PowerPlay table for Tonga, Fiji, Polaris10/11/12, VegaM (hwmgr.c:86 default PP_TABLE_V1; smu7_hwmgr.c:5144 binds pptable_v1_0_funcs). Every firmware USHORT sub-table offset, UCHAR entry count and UCHAR index is consumed with zero validation against hwmgr->soft_pp_table_size and (for counts) zero clamping to destination array capacities.

How it was verified (no AMD GPU needed β€” proven absent, hw_probe.log)

The entire production parser (process_pptables_v1_0.c:34-1337 β€” every helper, every parse function, the exported entry getters, the pptable_v1_0_funcs vtable) was compiled into a guest-side harness:

  • rig/gen_rig.sh machine-extracts the parser slice plus 24 header slices (pptable_v1_0.h firmware structs, hwmgr/hwmgr_ppt/hardwaremanager/ power_state/kgd/atombios type blocks, the PP_ASSERT_WITH_CODE and GET_FLEXIBLE_ARRAY_MEMBER_ADDR macros).
  • rig/check_verbatim.sh re-verified all 24 blocks byte-identical on the guest against /usr/src/sys β€” the tree its running kernel was compiled from (build.log: ALL VERBATIM CHECKS PASSED; guest↔host MD5s of all ten sliced sources in env.txt).
  • The fixed build is the same slice + exactly this pack's fix.diff (applied to a copy of the pristine full file with patch --fuzz=0; proven bidirectionally: forward apply succeeds, reverse apply restores the pristine file byte-exactly).
  • Shims (rig/rig_shims.h) touch no defect path: identity le16_to_cpu/le32_to_cpu, guard-backed end-anchored kzalloc (the first byte past any allocation is a PROT_NONE page β€” this is what turns the decisive destination heap OOB WRITE into a fault at a computable offset), a minimized struct pp_hwmgr with production names/types for every field the parser and the fix consult.
  • Crafted tables mimic real Tonga-format PowerPlay tables; the image ends at a guard page (OOB source reads fault at a computable offset), or at a patterned victim region (leak mode), or starts after a PROT_NONE page (negative-index underflow mode).

Observed (run.log; identical in run.2.log / run.3.log, md5-equal)

Baseline β€” 13 SIGSEGVs + 1 byte-exact leak + 2 behavioral proofs, every one at the exact predicted verbatim site:

mode observation verbatim site
vddc-lookup-255-write DEST heap OOB WRITE: first OOB byte at kzalloc(164)+0; loop bound ucNumEntries=255 vs 16-record allocation; source records all in-image get_vddc_lookup_table :176-190
statearray-offset-oob source OOB read at image+65281 (0xFF00) :1042-1053
thermal-offset-oob image+65281 :891-897
fan-offset-oob image+65280 :922-929
sclkdep-offset-oob image+65280 :781-783,420
ppm-offset-oob image+65281 :301-305,214-215
vce-offset-oob image+65281 :1219-1222
sclk-count-255 image+131 (first byte past a 131B image) :438-451
leak-canary 175/175 field values byte-exact with victim bytes past the image (mm count=40 vs 5 records) :710-724
vce-sclk-index-255 image+2914 (entries[0xFF] = sclk_table+2+255*11) :1249-1252,1261
vce-mclk-underflow imageβˆ’2 (entries[ucNumEntries-1] = entries[-1] with count 0) :1263-1267
entry-offbyone image+178 (<= guard accepts entry_index==ucNumEntries) :1310-1321
pcie-null-vdddepsclk NULL DEREF at NULL+0 (vdd_dep_on_sclk->count, sclk offset 0) :522
gpio-null-vdddepsclk NULL DEREF at NULL+0 :745
gpio-gate-zero-offset GPIO "parsed" from the PowerPlay header itself when usGPIOTableOffset==0 (gate tests the never-NULL pointer) :790-792,843-845
vce-count-unclamped hwmgr->num_vce_state_tables stored = 200 (array holds 6) :1328

Regression control (clean-valid): a fully valid table parses identically in both builds (all counts, limits, fan params, 2 states, 2 VCE states, rc=0) β€” the fix changes nothing on valid firmware.

Fixed build, identical inputs: 0 SIGSEGVs. Lookup count clamped 255β†’16, dep counts clamped to the records that fit (255β†’3, 40β†’5), oversized offsets rejected (rc=-1/-22), VCE indices clamped to 0, entries[-1] gone, entry < guard rejects one-past (rc=-1), NULL guards on PCIE/GPIO (rc=-22), GPIO gate moved to the offset (skipped), VCE count clamped 200β†’6.

Build / run / verify (on the audit guest)

cd findings/poc/DF-4388
./build.sh /usr/src     # verbatim check vs /usr/src + rig_base + rig_fixed
./run.sh                # baseline (verbatim) vs fixed A/B over 18 modes

Layout mode prints the exact verbatim struct geometry (PP table 77B, Tonga state 20B, SCLK rec 11B, MM rec 23B, lookup rec 8B β†’ get_vddc_lookup_table allocates 4+10*16=164 bytes for up to 255 firmware entries).

Historical artifacts from the pre-verification run (compile-validation and clamp unit test): syntax_check_guest.txt, unit_test_guest.log, fix_build.log, fix_run.log, v1_clamp_test.c, process_pptables_v1_0_fixed.c (byte-equal to pristine+fix.diff, proven in build.log).

VERDICT.md
↓ download raw

DF-4388 VERDICT (verify run, 2026-10-14)

Status: REPRODUCED (verbatim-rig evidence class). Every defect family the finding enumerates was demonstrated in byte-identical production code β€” including the heap out-of-bounds WRITE at get_vddc_lookup_table() β€” and the pack's fix.diff was A/B-validated in the same rig: fixed. In-kernel execution is AMD-ASIC-gated on this guest (no AMD GPU β€” proven, env.txt / hw_probe.log).

1. Method

The guest cannot attach amdgpu (no 1002: PCI device, no /dev/dri). Following the DF-4165/4247/4396 evidence class for crafted-firmware parsers, the actual production parser was compiled into a guest-side harness:

  • rig/gen_rig.sh extracts process_pptables_v1_0.c:34-1337 β€” the entire parser body (every static helper, pp_tables_v1_0_initialize/ uninitialize, the exported get_number_of_powerplay_table_entries_v1_0 / get_powerplay_table_entry_v1_0, the pptable_v1_0_funcs vtable) plus 24 header slices: pptable_v1_0.h 24-30 + 33-436 (every ATOM_Tonga_ firmware struct, packed layout preserved β€” sliced around its single #include "hwmgr.h" at :32), hwmgr_ppt.h 31-113 (phm_ppt_v1_ dest types), hwmgr.h 88-186/335-345/375-409/454-465/485-493/497-524/595-624/ 626-631/633-677/679-685, hardwaremanager.h caps enum + helpers + PP_Clocks + platform descriptor, power_state.h 26-187, kgd_pp_interface amd_vce_state + AMD_MAX_VCE_LEVELS, atombios.h ATOM_COMMON_TABLE_HEADER, atom-types.h, and the verbatim PP_ASSERT_WITH_CODE / GET_FLEXIBLE_ARRAY_MEMBER_ADDR macros from pp_debug.h.
  • rig/check_verbatim.sh re-verified all 24 blocks byte-identical β€” run on the guest against /usr/src/sys, the tree its running kernel was compiled from (build.log: ALL VERBATIM CHECKS PASSED; guest↔host MD5 equality of all ten sliced sources, env.txt).
  • The fixed build is the same slice + exactly this pack's fix.diff (applied to a copy of the pristine full file with patch --fuzz=0; proven bidirectionally: forward apply succeeds with no rejects, reverse apply restores the pristine file byte-exactly; the packed reference process_pptables_v1_0_fixed.c is byte-equal to pristine+fix.diff).
  • Harness shims (rig/rig_shims.h) touch no defect path: identity le16_to_cpu/le32_to_cpu (LE host, like the target), a minimized struct pp_hwmgr carrying production names/types for every field the parser and the fix consult (soft_pp_table, soft_pp_table_size as hwmgr.h:718-719; vce_states[AMD_MAX_VCE_LEVELS], num_vce_state_tables as hwmgr.h:722-723), and kzalloc/kfree mapped end-anchored at a PROT_NONE guard page so the first byte past any allocation faults β€” the destination-OOB-write detector. The allocator fidelity tradeoff (kmalloc slab vs mmap) touches no defect path: the defect is the count-vs-allocation mismatch (255 firmware entries vs a 16-record allocation), not the allocator.

Crafted tables mimic real Tonga-format PowerPlay tables (revision 7 = ATOM_Tonga_TABLE_REVISION_TONGA, verbatim packed structs, little-endian host so struct member writes produce exactly the firmware bytes). Images end exactly at a PROT_NONE guard page (source OOB reads fault at a computable offset), at a patterned victim region (leak mode, no fault so the captured bytes can be compared), or start right after a PROT_NONE page (negative-index underflow mode).

2. Observed (run.log; byte-identical in run.2.log / run.3.log, md5-equal)

Exact layouts from the verbatim structs (layout mode): POWERPLAYTABLE=77B, Tonga state=20B, SCLK rec=11B (Polaris 15B), MCLK rec=13B, MM rec=23B, lookup rec=8B β†’ get_vddc_lookup_table allocates 4+1016=164B* while its copy loop is bounded by firmware ucNumEntries (≀255).

Baseline β€” production code faults on every crafted input (13 SIGSEGVs), each classified by the fault handler, plus the leak leg and two behavioral proofs:

mode fault offset vs table verbatim site
vddc-lookup-255-write kzalloc(164)+0 β€” DESTINATION HEAP OUT-OF-BOUNDS WRITE (source records all in-image; write loop bound 255 vs 16-record allocation; up to ~2390B of firmware-chosen usVdd/usCAC* values past the object) get_vddc_lookup_table :176-190
statearray-offset-oob 65281 vs 256 state_arrays->ucNumEntries at base+0xFF00 β€” :1042-1053
thermal-offset-oob 65281 vs 256 thermal_controller->ucType β€” :891-897
fan-offset-oob 65280 vs 256 fan_table->ucRevId β€” :922-929
sclkdep-offset-oob 65280 vs 256 sclk_dep_table->ucRevId β€” :781-783 β†’ :420
ppm-offset-oob 65281 vs 256 atom_ppm_table->ucPpmDesign β€” :301-305 β†’ :214-215
vce-offset-oob 65281 vs 256 vce_state_table->ucNumEntries β€” :1219-1222
sclk-count-255 131 vs 131 entries[3] read past a 3-record table β€” :438-451
vce-sclk-index-255 2914 vs 256 entries[ucSCLKIndex=0xFF].ulSclk at sclk_table+2+255*11 β€” :1249-1252,1261
vce-mclk-underflow imageβˆ’2 entries[ucNumEntriesβˆ’1] = entries[-1] with count 0 β€” :1263-1267
entry-offbyone 178 vs 170 entry_index <= ucNumEntries accepts one-past; state_entry->usClassification at image end +10 β€” :1310-1321
pcie-null-vdddepsclk NULL+0 vdd_dep_on_sclk->count with sclk offset 0 β€” :522 (DF-1575)
gpio-null-vdddepsclk NULL+0 same in get_gpio_table β€” :745 (DF-1575)

Leak leg (leak-canary): MM dep ucNumEntries=40 with only 5 records in image, image followed by patterned victim bytes: the parse completes and 175/175 field values (dclk+vclk+eclk+aclk+samclock of entries[5..39]) are byte-exact with the victim bytes at the exact OOB source positions β€” kernel-heap data past soft_pp_table flows 1:1 into pp_table_information->mm_dep_table, which userland reads back through the powerplay sysfs/DPM interfaces. Stable across 3 runs.

Behavioral proofs: gpio-gate-zero-offset β€” with usGPIOTableOffset==0 the :843 gate tests powerplay_table+0 (never NULL), so get_gpio_table runs and reads ucVRHotTriggeredSclkDpmIndex from PowerPlay header byte 1 (the "SCLK DPM index for VRHot cannot exceed" assert fires on header bytes); vce-count-unclamped β€” hwmgr->num_vce_state_tables stored = 200 against a 6-entry vce_states[] (the fill loop is gated, but every consumer walking the stored count reads past the array β€” DF-4389's root, :1328).

Regression control (clean-valid): a fully valid table (2 states, fan, thermal, MM 2, PowerTune, SCLK 3, MCLK 2, PCIE 1, hard limits, VCE 2 states, PPM, vddc lookup 4, vddgfx lookup 2, GPIO) parses identically in both builds β€” rc=0, all counts and limits byte-equal, TDP=250, vce_states[0]={evclk=100000, sclk=30000}.

3. Fix validation (A/B, identical crafted inputs)

rig_fixed = same verbatim slice + this pack's fix.diff (checker-proven identity, bidirectional patch proof). All 13 fault modes: no fault β€” oversized offsets rejected (rc=-1/-22), counts clamped to the entries that fit AND to destination constants (255β†’16 lookup, 255β†’3 sclk, 40β†’5 mm), VCE indices clamped to 0, entries[-1] gone, one-past entry rejected (rc=-1), NULL guards on PCIE/GPIO (rc=-22); leak-canary: 0 out-of-image values; gpio-gate: GPIO parse skipped when the offset is 0; vce count stored clamped to 6. clean-valid: identical behavior to baseline.

Baseline corrupts, patched slice is clean on the same input β†’ fix validated (fix_validation = the FIXED half of run.log; three identical runs). A rebuilt-kernel validation was not performed: the fix sites execute in-kernel only on AMD ASICs absent from this guest; per the verbatim-rig protocol (DF-4165/4247/4396) the fixed slice was A/B-validated in the same rig. fix.diff is authored against the pristine read-only sys/ tree and was never applied there (guest /usr/src verified pristine by MD5 before and after).

4. Primitive and impact

Primitives, all in verbatim production code:

  1. Heap OOB WRITE (get_vddc_lookup_table :176-190): a firmware UCHAR (ucNumEntries ≀ 255) bounds a copy loop into a fixed 164-byte allocation β†’ up to ~2390 bytes past the object with firmware-chosen content (usVdd, usCACLow/Mid/High β€” 16-bit values), via both usVddcLookupTableOffset and usVddgfxLookupTableOffset. In-kernel this is kmalloc-heap corruption; the realistic floor is a kernel panic/oops (impact recorded as dos), with content-controlled heap overwrite potential.
  2. Attacker-directed source OOB reads out of the kernel-heap PowerPlay image: USHORT offsets reach +65535, UCHAR count loops read linearly past the image (fully value- and length-controlled), UCHAR indices Γ— record sizes reach +2807 (VCE sclk index), entries[-1] underflow reads before the image β€” with the leaked bytes converted 1:1 into parsed DPM tables exposed to userland (175/175 byte-exact, proven).
  3. NULL-deref kernel panics (vdd_dep_on_sclk in get_pcie_table / get_gpio_table) whenever the ROM omits the SCLK dependency table but ships a PCIE or GPIO table; plus the never-NULL gate defect that reads the PowerPlay header as a GPIO table when the offset is 0.
  4. Unclamped count store (num_vce_state_tables ≀ 255 vs vce_states[6]) β€” DF-4389's provenance root.

Threat model (the family's established one): crafted/corrupted GPU VBIOS (flashed ROM, vfio-pci romfile override, malicious discrete device) parsed at driver attach (pptable_v1_0_initialize), or root re-parsing a crafted blob through /sys/class/drm/card0/device/pp_table β†’ pp_dpm_set_pp_table β†’ amd_powerplay_reset β†’ pptable_init. No unprivileged userland trigger on stock hardware β€” same class as DF-4247 (V0 twin). Demonstrated out-of-kernel context (rig); the write primitive's in-kernel consequence (panic at minimum) follows from the verbatim instruction stream and the production allocation sizes.

5. Honest limitations

  • Demonstrated with production instructions and production struct layouts, but in a userland mapping, not the kernel's kmalloc heap. The kzalloc shim's guard pages detect the destination overflow; they do not model slab freelist interaction (whether the ~2390-byte overwrite is weaponizable in-kernel was not investigated β€” the VBIOS-gated threat model gives the attacker kernel-equivalent control already at ROM-flash time in the realistic scenarios).
  • The leak leg proves source bytes are captured into parsed tables; the slab neighborhood contents in kernel context are the allocator's, not a controlled victim.
  • entry-offbyone requires a caller passing entry_index == ucNumEntries (the stock caller loops 0..N-1), so it is a latent wrong-guard defect (defense-in-depth), demonstrated by direct call.
  • The crafted images are synthetic minimal tables; a real crafted ROM must also satisfy earlier ATOM ROM parsing stages, which precede and do not constrain these fields.
  • Offset modes demonstrate 6 of the 16 USHORT consumers concretely; the remaining ten (MM/PowerTune/MCLK/HardLimit/PCIE/VddcLookup/VddgfxLookup/ GPIO/Fan/VCE-in-entry-getter) are the same unvalidated powerplay_table + offset pattern at :772-792/:281-304/:1219-1240 and are all covered by the fix's range checks (validated where exercised: MM count clamp in leak-canary, vddc/vddgfx lookup range checks and count clamps in the write mode, VCE table checks in the index modes).

6. Bottom line

The central-fix claim is verified end to end: the V1 parser consumes every firmware offset/count/index unvalidated (heap OOB write, OOB reads with byte-exact leak, NULL derefs, negative-index underflow, off-by-one, unclamped count store β€” 16 defect modes demonstrated in byte-identical production code), and the pack's fix.diff neutralizes every demonstrated mode on identical input while leaving valid-table parsing byte-identical.

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 reproduced→ patch + rebuild →patched clean

A/B-validated in the verbatim rig (DF-4165/4247/4396 protocol): rig_fixed = same byte-identical slice + fix.diff (patch --fuzz=0, reverse-apply restores pristine byte-exactly; byte-equal to the packed process_pptables_v1_0_fixed.c). On the identical crafted inputs that fault the baseline 13 times, the fixed build faults ZERO times: vddc lookup count clamped 255->16 (write gone), oversized USHORT offsets rejected (rc=-1/-22), dep counts clamped to image and destination constants (255->3 sclk, 40->5 mm), VCE indices clamped, entries[-1] underflow eliminated, entry_index guard corrected to '<', vdd_dep_on_sclk NULL guards added (rc=-22), GPIO gate moved to the offset, num_vce_state_tables clamped 200->6; clean-valid table parses byte-identically to the baseline build. A rebuilt-kernel A/B was not performed: the fix sites execute in-kernel only on AMD ASICs absent from this guest.

run.log FIXED half (0 SIGSEGV / 11 graceful rejections + clamped counts); build.log (verbatim checks + bidirectional fix proof); fix.diff
↓ fix.diffper-fix-DF-4388

Confirmed kernel references

Detail

Exploit chain

crafted/corrupted GPU VBIOS PowerPlay table (Tonga format, PP_TABLE_V1: Tonga/Fiji/Polaris10/11/12/VegaM) at driver attach -> pptable_v1_0_initialize -> get_vddc_lookup_table heap OOB write (255 firmware entries into a 16-record kmalloc(164), ~2390B content-controlled overwrite) and/or OOB reads with byte-exact leak into userland-readable DPM tables and/or vdd_dep_on_sclk NULL-deref panic; alternatively root-written /sys/class/drm/card0/device/pp_table -> pp_dpm_set_pp_table -> amd_powerplay_reset -> same parse. No unprivileged userland route on stock hardware.

Evidence (decisive lines)

run.log (baseline 13 classified SIGSEGVs at exact predicted offsets + 175/175 byte-exact leak + 2 behavioral proofs; FIXED half: 0 faults, all clamps visible; byte-identical across run.2.log/run.3.log); build.log (ALL VERBATIM CHECKS PASSED vs guest /usr/src, 24 blocks + bidirectional fix proof); VERDICT.md (full path:line trace); rig/ (pp_rig.c 18 modes, gen_rig.sh, check_verbatim.sh); env.txt (guest md5s = host tree)

PoC changes

fresh rig this run (no prior PoC existed - prior verification was compile-validation + clamp unit test only): guard-backed end-anchored kzalloc shim to make the destination heap OOB write fault decisively; 18-mode driver (16 defect modes + layout + clean-valid regression control); verbatim slice of the whole parser body + 24 header slices with guest-side byte-identity proof

Verified recommended fix

central pp_v1_table_range_ok/pp_v1_clamp_count layer with destination-constant clamps at every offset/count/index consumer (fix.diff, A/B-validated in the rig)

Verdict

REPRODUCED via the DF-4165/4247/4396 verbatim-rig evidence class: the ENTIRE production V1 parser (process_pptables_v1_0.c:34-1337, checker-proven byte-identical on the guest against /usr/src/sys, the tree its running kernel was built from) compiled into a harness and driven with crafted Tonga-format PowerPlay tables. 16 defect modes demonstrated at exactly the predicted verbatim sites, stable across 3 identical runs: (1) heap OOB WRITE in get_vddc_lookup_table (:176-190) - firmware ucNumEntries=255 bounds the copy loop into a fixed kzalloc(4+10*16=164) destination, fault at the first byte past the allocation, up to ~2390B of firmware-chosen content (both usVddcLookupTableOffset and usVddgfxLookupTableOffset paths); (2) six unvalidated USHORT-offset dereferences (state/thermal/fan/sclk-dep/ppm/vce) faulting at image+0xFF00; (3) count-bounded source OOB read loops (sclk 255 vs 3 records); (4) byte-exact kernel-heap leak: MM dep count=40 vs 5 records -> 175/175 parsed field values byte-exact with victim bytes past the image, parse completing into userland-readable DPM tables; (5) VCE ucSCLKIndex=0xFF -> entries[0xFF] read at sclk_table+2+2805; (6) entries[-1] NEGATIVE-INDEX underflow read before the image when ucNumEntries=0 (:1263-1267); (7) entry_index<=ucNumEntries off-by-one (:1310); (8) NULL derefs of vdd_dep_on_sclk in get_pcie_table/get_gpio_table (:522/:745); (9) the never-NULL GPIO gate parsing the PowerPlay header when usGPIOTableOffset==0; (10) num_vce_state_tables stored unclamped (200 vs vce_states[6], :1328). Fix A/B-validated in the same rig: fix.diff (git-apply proven bidirectionally, byte-equal to the packed reference) applied to the same slice -> 0 SIGSEGVs on identical input, counts clamped to image AND destination constants (255->16 lookup, 255->3 sclk, 40->5 mm, 200->6 vce), offsets rejected (rc=-1/-22), indices clamped, entries[-1] gone, NULL guards added, GPIO gate moved to the offset, and clean-valid parses byte-identically in both builds. In-kernel execution is AMD-ASIC-gated on this guest (no 1002: device, hw_probe.log); impact recorded as dos = the realistic in-kernel floor of the demonstrated heap-corruption primitive (panic), with content-controlled overwrite potential; threat model is VBIOS-crafted (flashed ROM / vfio romfile / malicious device) or root-written pp_table, same class as the DF-4247 V0 twin - no unprivileged userland trigger on stock hardware.