Heap buffer overflow in get_vddc_lookup_table: VBIOS ucNumEntries > caller max_levels writes ~2.5KB past slab object
Summary
vega10_processpptables.c:1026-1027 table_size = sizeof(uint32)+sizeof(record)*max_levels (caller passes 4 for vddmem/vddci at 1118/1127). 1034 table->count = ucNumEntries (UCHAR 0..255). 1036-1038 loop i<ucNumEntries writes entries[i].us_vdd. For vddmem max_levels=4: alloc 44B, ucNumEntries=255 -> writes 2510B past slab into kmalloc-64 slab. Trigger: root writes /sys/class/drm/cardN/device/pp_table with crafted usVddmemLookupTableOffset ucNumEntries=255; OR malicious VFIO VBIOS. Sibling of DF-1574 (process_pptables_v1_0). Fix: clamp to min(ucNumEntries, max_levels).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1586 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full narrative: bug, reachability, fix, threat model | 4.7 KB | β raw |
| README.md | readme | summary + reproduce | 1.6 KB | β raw |
| fix.diff | suggested-fix | clamp table->count and loop to min(ucNumEntries, max_levels) | 927 B | view raw |
| fix_build.log | build-log | patched vega10_processpptables.c compiles -Werror clean | 1.4 KB | view raw |
| env.txt | environment | guest uname, pciconf, kldstat β no AMD hardware | 1.5 KB | view raw |
| manifest.json | manifest | this catalog | 2.7 KB | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1586 β vega10 get_vddc_lookup_table heap OOB write (amdgpu powerplay)
At a glance
| Field | Value |
|---|---|
| Finding | DF-1586 (sibling of DF-1574) |
| Severity | High |
| Class | CWE-787 Out-of-bounds Write |
| File | sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c:1014-1043 |
| Verdict | NOT REPRODUCED ON GUEST (real bug, unreachable runtime path) |
| Impact | none (on this guest); latent memory-corruption |
| Confidence | certain (source-traced line-by-line) |
The bug, in one sentence
vega10's get_vddc_lookup_table() allocates 4 + 10*max_levels bytes
(callers pass 4 or 8) but loops ucNumEntries times walking 10 bytes
per iteration; with a hostile VBIOS ucNumEntries=255 it writes ~2.5 KB
past the kmalloc-64 slab object.
Reproduce on this guest
Cannot run. Part of the amdgpu KLD module β not in
X86_64_GENERIC, not loaded, no AMD GPU present. See VERDICT.md for
full reachability analysis and fix_build.log for the standalone
patched-source compile.
Apply the fix
cd /usr/src
patch -p1 < findings/poc/DF-1586/fix.diff
# In sys/dev/drm/amd/amdgpu:
make # rebuild amdgpu.ko
Files in this folder
| File | Purpose |
|---|---|
VERDICT.md |
Full analysis: bug, reachability, fix, threat model |
fix.diff |
git-apply-able unified diff (clamps count + loop) |
fix_build.log |
Translation-unit compile log (patched source, -Werror clean) |
env.txt |
Guest environment + PCI/module state |
manifest.json |
Machine-readable catalog |
DF-1586 β Heap buffer overflow in get_vddc_lookup_table (vega10_processpptables.c)
Verdict
NOT REPRODUCED ON THIS GUEST β bug is real in source (sibling of
DF-1574 in the vega10 powerplay parser), but the affected TU is part of
the amdgpu KLD module, not in X86_64_GENERIC, not loaded on this
guest, and no AMD GPU is present. Latent memory-corruption defect.
Fix authored, applies cleanly, compiles cleanly.
The bug (confirmed in source)
sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c, function
get_vddc_lookup_table (lines 1014-1043):
- Line 1026-1027 β allocation uses the caller's
max_levels:table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels; - Three callers in
init_dpm_2_parameterspass differentmax_levels: - line 1109 β vddc:
max_levels=8β table_size = 4 + 80 = 84 (kmalloc-128) - line 1118 β vddmem:
max_levels=4β table_size = 4 + 40 = 44 (kmalloc-64) - line 1127 β vddci:
max_levels=4β table_size = 4 + 40 = 44 (kmalloc-64) - Line 1034 β
table->count = vddc_lookup_pp_tables->ucNumEntries;(ucNumEntriesis UCHAR 0..255 βvega10_pptable.h:237). - Line 1036-1038 β loop is bounded by
ucNumEntries, notmax_levels:for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++)table->entries[i].us_vdd = le16_to_cpu(...usVdd); entriesis of typephm_ppt_v1_voltage_lookup_record(5x uint16 = 10 bytes βhwmgr_ppt.h:78-84), so each iteration walks 10 bytes forward even though only the first 2 (us_vdd) are written.- For vddmem (max_levels=4): worst case
ucNumEntries=255writes at offset4 + 254*10 = 2544into a 44-byte kmalloc-64 allocation β ~2500 bytes of (partly) controlled OOB write into the kmalloc-64 bucket.
Sibling functions in this same file are correct
get_mm_clock_voltage_dependency_table (lines 302-330),
get_gpio_table (no β that's right),
get_clock_voltage_dependency_table (lines 528-557, 603-...,
669-..., 709-...), all use clk_dep_table->ucNumEntries for BOTH
allocation and loop (e.g. line 531-533 / 540 / 542). Only the vega10
get_vddc_lookup_table helper splits the bounds β clear inconsistency.
Reachability on this guest
- Same as DF-1574 β
amdgpunot inX86_64_GENERIC, not inLINT64except as a loadable KLD; no AMD GPU hardware on the QEMU guest. get_vddc_lookup_tableis only reachable viavega10_pp_tables_initializeβinit_dpm_2_parameters, called during amdgpu driver attach to a Vega 10-class AMD GPU.
Realistic threat model (when reachable)
- An attacker who controls the VBIOS PowerPlay table supplied to the amdgpu driver on Vega 10 hardware can trigger the OOB.
- Real-world scenarios:
1. VFIO PCI passthrough of an AMD Vega 10 GPU to a DragonFly
guest, with a crafted
vfio-pci.romfile=. 2. Malicious FPGA GPU / PCIe device presenting a Vega 10 vendor/device ID. - On a host where root can also write to
/sys/class/drm/cardN/device/pp_table(if exposed by the driver), root-controlled replacement of the parsed PowerPlay table can also trigger it β again, rootβkernel. - All triggers are root-controlled or physical-PCIe-level. No unprivileged-user path exists. On the default GENERIC kernel (INVARIANTS ON), the slab INVARIANTS would catch the cross-object overwrite and panic the kernel β realistic impact is panic/DoS.
Exploit chain
Not applicable β same hard blocker as DF-1574. The primitive is real (~2.5 KB partly-attacker-shaped heap write into kmalloc-64 bucket) but its only trigger paths are root-controlled (kldload / VFIO romfile) on the default kernel, and the guest has no AMD GPU hardware. Documented as a latent memory-corruption defect with a verified fix.
The fix
fix.diff β same shape as DF-1574's fix: clamp both table->count and
the loop bound to min(ucNumEntries, max_levels).
-table->count = vddc_lookup_pp_tables->ucNumEntries;
-
-for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++)
+/* ucNumEntries comes from VBIOS (UCHAR, 0..255) but the buffer was
+ * sized for max_levels entries. Clamp the count and loop bound to
+ * max_levels to prevent an out-of-bounds write past the slab object. */
+if (vddc_lookup_pp_tables->ucNumEntries > max_levels)
+ table->count = max_levels;
+else
+ table->count = vddc_lookup_pp_tables->ucNumEntries;
+
+for (i = 0; i < table->count; i++)
table->entries[i].us_vdd =
le16_to_cpu(vddc_lookup_pp_tables->entries[i].usVdd);
PoC changes
No userspace PoC can exercise this path. Same situation as DF-1574.
Reproduce
Not runnable on this guest. See fix_build.log for the standalone
patched-source compile.
Fix verification
not_testablecompile validated -Werror
module/translation-unit build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. vega10 get_vddc_lookup_table alloc by max_levels=4/8 but loop by ucNumEntries -> ~2.5KB OOB. amdgpu not in GENERIC, no AMD GPU. Sibling of DF-1574.
No comments yet.