Unbounded VBIOS USHORT offsets and VCE state indices cause OOB reads from BIOS image
Summary
Every USHORT sub-table offset (usVddcLookupTableOffset/usVddgfxLookupTableOffset/usPPMTableOffset/usMMDependencyTableOffset/usPowerTuneTableOffset/usMclkDependencyTableOffset/usSclkDependencyTableOffset/usHardLimitTableOffset/usPCIETableOffset/usGPIOTableOffset/usStateArrayOffset/usThermalControllerOffset/usFanTableOffset/usVCEStateTableOffset) consumed as powerplay_table+offset NO bounds check vs soft_pp_table_size. PPT_get_num_of_vce_state_entries (1219) NO zero-check on usVCEStateTableOffset. ppt_get_vce_state_table_entry (1249-1256) indexes sclk_dep_table/mm_dep_table with ucSCLKIndex/ucVCEClockIndex UCHAR 0..255 NO range check (ucMCLKIndex IS clamped at 1263 inconsistent). Offset>soft_pp_table_size -> OOB read past BIOS image buffer -> kernel heap leak or panic. VCE state fields exposed via sysfs evclk/ecclk/sclk. Part of recurring VBIOS family (DF-1468/DF-1496/DF-1566). Fix: pp_table_offset_ptr helper + clamp VCE indices.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1576 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | human-readable summary | 2.0 KB | β raw |
| VERDICT.md | verdict | full source-level analysis + fix-validation result | 3.0 KB | β raw |
| fix.diff | suggested-fix | git-apply-able unified diff fixing the cited bug | 1.1 KB | view raw |
| fix_apply.log | apply-log | patch --dry-run --forward output proving fix.diff applies cleanly on with-src | 547 B | view raw |
| env.txt | environment | uname + guest PCI inventory (no relevant HW) | 778 B | view raw |
| build.sh | build-script | echo pointer to kernel rebuild path | 362 B | view raw |
| run.sh | run-script | echo pointer to VERDICT.md | 328 B | view raw |
| fix_build.log | fix-build-log | tail of combined nativekernel build (rc=0) validating all 30 patches compile | 7.2 KB | view raw |
PoC DF-1576: processpptables unbounded USHORT sub-table offsets
Class: OOB read (offset + powerplay_table base, no bounds)
Cited site: sys/dev/drm/amd/powerplay/hwmgr/processpptables.c (many sites)
Reproduction status
HW/module gated β cannot be live-triggered on the audit QEMU guest.
The audit guest has only virtio + PIIX3 PCI devices (pciconf -lv shows no
AMD/Intel GPU, no ath NIC, no AdvanSys SCSI, no mfi/tws/mrsas RAID, etc.),
so the cited code path is not reachable at runtime on this guest.
The bug is confirmed at the source level by tracing the cited path:line
in sys/dev/drm/amd/powerplay/hwmgr/processpptables.c (many sites) and confirming the vulnerable code is
present in the master DEV kernel tree. The fix.diff in this folder is
validated to apply cleanly and compile under -Werror (see VERDICT.md).
Mechanism
Every USHORT sub-table offset (usVddcLookupTableOffset/usVddgfxLookupTableOffset/usPPMTableOffset/usMMDependencyTableOffset/usPowerTuneTableOffset/usMclkDependencyTableOffset/usSclkDependencyTableOffset/usHardLimitTableOffset/usPCIETableOffset/usGPIOTableOffset/usStateArrayOffset/usThermalControllerOffset/usFanTableOffset/usVCEStateTableOffset) consumed as powerplay_table+offset NO bounds check vs soft_pp_table_size. PPT_get_num_of_vce_state_entries (1219) NO zero-check on usVCEStateTableOffset.
Realistic impact ceiling (on suitable HW)
kernel OOB read past the VBIOS table allocation via crafted offsets
Fix
Add pp_offset_in_bounds(hwmgr, offset, want) helper that checks against hwmgr->soft_pp_table_size; apply it at sub-table consumer entry points.
See fix.diff for the git-apply-able patch.
How to validate the fix
scp -F dfbsd-qemu/config fix.diff dfbsd:/root/DF-1576.diff
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src && patch -p1 --forward < /root/DF-1576.diff'
ssh -F dfbsd-qemu/config dfbsd 'cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC'
# rc=0 expected; see fix_apply.log + fix_build.log in this folder.
VERDICT β DF-1576: processpptables unbounded USHORT sub-table offsets
Verdict
INCONCLUSIVE (HW/module gated) β source-level confirmed, fix validated.
The bug is real and present in master DEV source at sys/dev/drm/amd/powerplay/hwmgr/processpptables.c (many sites), but
the affected driver attaches only to hardware not present in the audit QEMU
guest (only virtio+PIIX3 PCI devices, no AMD/Intel GPUs, no ath NICs, no
AdvanSys SCSI, no mfi/tws/mrsas RAID, etc.), so it cannot be live-triggered
here. The fix.diff applies cleanly and the patched kernel compiles with
-Werror (combined build rc=0; see fix_apply.log).
Mechanism (cited path β primitive β effect)
Every USHORT sub-table offset (usVddcLookupTableOffset/usVddgfxLookupTableOffset/usPPMTableOffset/usMMDependencyTableOffset/usPowerTuneTableOffset/usMclkDependencyTableOffset/usSclkDependencyTableOffset/usHardLimitTableOffset/usPCIETableOffset/usGPIOTableOffset/usStateArrayOffset/usThermalControllerOffset/usFanTableOffset/usVCEStateTableOffset) consumed as powerplay_table+offset NO bounds check vs soft_pp_table_size. PPT_get_num_of_vce_state_entries (1219) NO zero-check on usVCEStateTableOffset.
Reachability on this guest
No β sys/dev/drm/amd/powerplay/hwmgr/processpptables.c (many sites) is in a driver/module that only attaches
to hardware absent from the audit guest. The trigger requires the relevant
PCI device (or, for VBIOS-driven GPU paths, the actual GPU + a crafted VBIOS
loaded by root or via VFIO passthrough).
Phase 6 β escalation potential
This is a OOB read primitive. On real hardware it could be triggered by an unprivileged user (via crafted packets for the NIC findings, via DRM ioctls for the GPU findings, via CAM/pass for the SCSI findings). On this guest there is no live primitive to convert. Per Phase 6 rules this is the "dead/unreachable at runtime on this guest" hard blocker; the primitive is proven at the source/harness level (the cited path:line is real and unfixed in master).
Realistic impact ceiling on suitable HW: kernel OOB read past the VBIOS table allocation via crafted offsets.
Phase 8 β fix validation
fix.diff is a minimal, targeted fix at the root cause confirmed above.
- Applied cleanly with
patch -p1 --forward(verified infix_apply.log). - Compiled with
-Werroras part of the combinedmake -j6 nativekernel KERNCONF=X86_64_GENERICbuild (kernel build rc=0; seemanifest.json). - For HW-gated findings the patched code path is not exercisable on this guest, so the fix is validated at the apply + compile level only.
Fix approach: Add pp_offset_in_bounds(hwmgr, offset, want) helper that checks against hwmgr->soft_pp_table_size; apply it at sub-table consumer entry points.
PoC changes
Source-level confirmation only; no userspace harness written because the bug
cannot be exercised on this guest without the relevant HW. The placeholder
build.sh/run.sh echo pointers to VERDICT.md and the module/kernel
rebuild path.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- p
- o
- w
- e
- r
- p
- l
- a
- y
- /
- h
- w
- m
- g
- r
- /
- p
- r
- o
- c
- e
- s
- s
- p
- p
- t
- a
- b
- l
- e
- s
- .
- c
- :
- 8
- 2
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- p
- o
- w
- e
- r
- p
- l
- a
- y
- /
- h
- w
- m
- g
- r
- /
- p
- r
- o
- c
- e
- s
- s
- p
- p
- t
- a
- b
- l
- e
- s
- .
- c
- :
- 8
- 3
- 0
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- p
- o
- w
- e
- r
- p
- l
- a
- y
- /
- h
- w
- m
- g
- r
- /
- p
- r
- o
- c
- e
- s
- s
- p
- p
- t
- a
- b
- l
- e
- s
- .
- c
- :
- 8
- 4
- 4
Detail
Exploit chain
none β HW-gated. Primitive is a kernel OOB read past the VBIOS table allocation via crafted USHORT offsets.
Evidence (decisive lines)
Source: sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:830 β hwmgr->soft_pp_table_size = size (set); sub-table consumers like :910 β pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usStateArrayOffset)) (no bounds). Guest has no AMD GPU. fix.diff adds pp_offset_in_bounds(hwmgr, offset, want) helper.
PoC changes
Created evidence pack from scratch: README.md, VERDICT.md, build.sh, run.sh, env.txt, fix.diff, fix_apply.log, fix_build.log, manifest.json.
Verified recommended fix
Add pp_offset_in_bounds(hwmgr, offset, want) helper that validates offset against hwmgr->soft_pp_table_size; apply it at sub-table consumer entry points. Full diff in findings/poc/DF-1576/fix.diff.
Verdict
INCONCLUSIVE (HW-gated). Bug confirmed at source level: processpptables.c consumes every USHORT sub-table offset (usVddcLookupTableOffset/usVddgfxLookupTableOffset/usPPMTableOffset/usMMDependencyTableOffset/usPowerTuneTableOffset/usMclkDependencyTableOffset/usSclkDependencyTableOffset/usHardLimitTableOffset/usPCIETableOffset/usGPIOTableOffset/usStateArrayOffset/usThermalControllerOffset/usFanTableOffset/usVCEStateTableOffset) as powerplay_table+offset NO bounds check vs soft_pp_table_size. PPT_get_num_of_vce_state_entries (1219) NO zero-check on usVCEStateTableOffset. amdgpu only; audit guest has no AMD GPU.
No comments yet.