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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2219 Β· 6 files
FileTypeDescriptionSize
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
VERDICT.md verdict gate analysis + unbounded index trace; kldload PoC rejected
↓ download 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.

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_testable
baseline no→ patch + rebuild →patched clean

not_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).
↓ fix.diffn/a (no target HW/device on this guest)

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.