Unbounded array index in encode_pcie_lane_width() allows OOB .rodata read via crafted GPU VBIOS
Summary
encode_pcie_lane_width() indexes 17-entry static table pp_r600_encode_lanes[num_lanes] with no bounds check. Multiple callers pass attacker-influenced uint8_t lane-width value parsed directly from GPU VBIOS PowerPlay table without validation. vega10_hwmgr.c:1244 passes bios_pcie_table->entries[i].lane_width directly populated from ucPCIELaneWidth (UCHAR uint8_t) at vega10_processpptables.c:795 no validation values 0-255 accepted. process_pptables_v1_0.c:537/577 same. get_pcie_lane_support passes non-standard values through unchanged (default loop falls through without assignment). Crafted VBIOS causes OOB read up to 239 bytes past table into adjacent kernel .rodata. Leaked byte written into SMC firmware command table LinkLevel[i].PcieLaneCount uploaded to GPU SMC coprocessor: small info leak + likely GPU-hang/system-panic DoS from out-of-spec lane width. Attacker: malicious PCIe device (hot-plug Thunderbolt/USB4/server hot-swap/SR-IOV vGPU pass-through) with crafted expansion ROM presenting AMD vendor/device IDs. No privilege escalation path identified read-only primitive no write. Decode path safe: sole caller enforces link_width<=7 array [8] index 7 in-bounds.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2219 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + unbounded index trace; kldload PoC rejected | 3.5 KB | β raw |
| fix.diff | suggested-fix | bounds-check num_lanes against nitems() of each table | 561 B | view raw |
| build.sh | build-script | documents HW gate | 231 B | view raw |
| run.sh | run-script | prints gate proof | 361 B | view raw |
| env.txt | environment | guest env | 1.1 KB | view raw |
| gate_proof.txt | gate-proof | no AMD GPU, no /dev/dri* | 219 B | view raw |
DF-2219 β Unbounded array index in encode_pcie_lane_width() (drm/amd powerplay)
Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)
Hardware gate (why the PoC cannot run on this guest)
encode_pcie_lane_width() is part of the AMDGPU powerplay (PowerPlay/SMU)
subsystem and is reached only when an AMD GPU DRM driver attaches and parses the
card's VBIOS PowerPlay table. The audit QEMU/KVM guest has no AMD GPU and no
DRM driver:
$ pciconf -l | grep -iE "0x030000|0x038000|vga|amd|1002" vgapci0@pci0:0:2:0: class=0x030000 chip=0x11111234 rev=0x02 # Bochs/QEMU std VGA, not AMD $ ls /dev/dri* /dev/drm* # No such file or directory $ kldstat | grep -iE "drm|amdgpu" # (none)
With no AMD GPU and no attached AMDGPU driver, the PowerPlay-table parsing that
feeds lane_width into encode_pcie_lane_width() never runs on this guest. The
unprivileged maxx user has no path to supply a crafted VBIOS.
(The README's PoC suggestion β cc -DKLD test_encode_oob.c; kldload β is
explicitly rejected by the bright-line rule: kldload requires root and a
root-loaded module can prove nothing about unprivβroot. It is not a valid PoC.)
Source trace β the bug is REAL (sys/dev/drm/amd/powerplay/hwmgr/pppcielanes.c)
static const unsigned char pp_r600_encode_lanes[] = { /* 17 entries: indices 0..16 */
0,1,2,0,3,0,0,0,4,0,0,0,5,0,0,0,6 };
static const unsigned char pp_r600_decoded_lanes[8] = { 16,1,2,4,8,12,16, };
uint8_t encode_pcie_lane_width(uint32_t num_lanes) /* pppcielanesc:56 */
{
return pp_r600_encode_lanes[num_lanes]; /* NO bounds check */
}
uint8_t decode_pcie_lane_width(uint32_t num_lanes) /* pppcielanesc:61 */
{
return pp_r600_decoded_lanes[num_lanes]; /* NO bounds check */
}
Callers pass attacker-influenced VBIOS values straight through, e.g.:
- vega10_hwmgr.c:1244:
encode_pcie_lane_width(bios_pcie_table->entries[i].lane_width) where
lane_width is populated from the VBIOS ucPCIELaneWidth (UCHAR/uint8_t,
values 0-255) at vega10_processpptables.c:795 with no validation.
- ci_smumgr.c:1007, tonga_smumgr.c:518, iceland_smumgr.c:776,
polaris10_smumgr.c:778, fiji_smumgr.c:841, vegam_smumgr.c:582 likewise
feed param1/lane_width from device-controlled tables.
For any num_lanes > 16, pp_r600_encode_lanes[num_lanes] reads up to 238
bytes past the 17-element table into adjacent kernel .rodata. The leaked byte
is then written into LinkLevel[i].PcieLaneCount and uploaded to the GPU SMC
coprocessor β a small info-leak plus an out-of-spec lane width that can hang the
GPU / panic the system. (The decode side is currently safe because its sole
caller smu7_hwmgr.c:167 enforces link_width <= 7.)
Attacker: a malicious PCIe device (hot-plug Thunderbolt/USB4/server hot-swap/ SR-IOV vGPU pass-through) presenting AMD vendor/device IDs with a crafted expansion ROM. Read-only primitive; no write.
Exploit chain status
Not pursuable β read-only OOB .rodata read primitive behind absent AMD GPU
hardware (valid Phase-6 hard blocker: read-only primitive + dead path at
runtime). No escalation chain derivable.
PoC changes
None. The README's kldload PoC is invalid (bright-line rule); no DRM/AMD GPU
on guest. Verified by source trace only.
Recommended fix
Bounds-check num_lanes against the table size (return 0 / a safe default for
out-of-range). See fix.diff (matches finding proposal intent; the encode side
is the reachable one).
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.
git apply --check findings/poc/DF-2219/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.
Evidence (decisive lines)
usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2219/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).
Verified recommended fix
bounds-check num_lanes against nitems(), return 0 OOR. Full git-apply-able diff in findings/poc/DF-2219/fix.diff (git apply --check OK).
Verdict
NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). drm/amd encode_pcie_lane_width unbounded array index (VBIOS lane_width 0-255 into 17-entry table); no AMD GPU. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.
No comments yet.