vce_v2_0: VCE bo sized exactly AMDGPU_VCE_FIRMWARE_OFFSET (256) bytes short β DATA window ends 256 bytes past vcpu_bo (DF-4524 v3 shape, v2 instance)
Summary
sw_init allocates the bo at exactly FW+STACK+DATA (172 pages, zero slack) but mc_resume lays the cache windows out starting at bo+256 - the DATA window (all 16 session handles) ends exactly 256 bytes past the bo, so fw session-data writes land in the adjacent VRAM object. Identical shape to DF-4524 (v3); v4 clamps to bo size (clean). Arithmetic proof in pack; hw-gated. Add-the-offset fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4742 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 3.9 KB | β raw | |
| check_arith.py | β | 1.2 KB | view raw | |
| arith.log | β | 264 B | view raw | |
| env.txt | β | 316 B | view raw | |
| fix.diff | β | 571 B | view raw | |
| manifest.json | β | 952 B | view raw | |
| verdict.json | β | 2.5 KB | view raw |
DF-4742 β vce_v2_0: VCE bo sized 256 bytes short of the VCPU cache-window layout
Finding
vce_v2_0_sw_init() allocates the shared VCE vcpu_bo at exactly
VCE_V2_0_FW_SIZE + VCE_V2_0_STACK_SIZE + VCE_V2_0_DATA_SIZE bytes
(sys/dev/drm/amd/amdgpu/vce_v2_0.c:424-425, passed to
amdgpu_vce_sw_init() β amdgpu_bo_create_kernel(adev, size, ...),
sys/dev/drm/amd/amdgpu/amdgpu_vce.c:181).
But vce_v2_0_mc_resume() programs the VCE VCPU cache windows starting at
bo offset AMDGPU_VCE_FIRMWARE_OFFSET = 256
(sys/dev/drm/amd/amdgpu/vce_v2_0.c:185-198; AMDGPU_VCE_FIRMWARE_OFFSET
defined at sys/dev/drm/amd/amdgpu/amdgpu_vce.h:28):
- window 0 (firmware): bo+256 .. bo+256+262144
- window 1 (stack): bo+256+262144 .. +65536
- window 2 (DATA, per-session data for all
AMDGPU_MAX_VCE_HANDLES=16 handles): bo+256+327680 .. +376832 β ends at bo+704768
bo size = 262144 + 65536 + 376832 = 704512 bytes = exactly 172 pages
(no allocator slack β page-aligned total). The DATA window therefore ends
256 bytes past the end of the bo: the VCE firmware's per-session DATA
writes into the last 256 bytes of the window land in the next VRAM
object beyond vcpu_bo.
Identical shape to the already-filed v3 instance DF-4524 (vce_v3_0.c,
instance-1 DATA window overruns by 256). vce_v4_0 is clean because it
reads back amdgpu_bo_size(adev->vce.vcpu_bo) and clamps the window
sizes to the actual bo (vce_v4_0.c:440).
Reachability / threat
Hardware-gated: requires a CIK-era Sea Islands ASIC with VCE 2.0
(Bonaire / Kaveri / Kabini / Hawaii / Mullins) present and initialized
(amdgpu loaded, VCE fw present). The DATA window is written by the VCE
firmware itself whenever encode sessions exist; sessions are created by
any local user with DRM render-node access via the CS ioctl
(amdgpu_vce_ring_parse_cs handles, DF-4447 wiring).
Impact ceiling: GPU-side out-of-bounds write of up to 256 bytes into the
VRAM object allocated after vcpu_bo β cross-object VRAM corruption
(another process's buffer / display buffer). Host-RAM or code-exec impact
is not established (firmware-defined write pattern) β impact is
speculative; the 256-byte window overrun itself is pure arithmetic and
certain.
Status
NOT runnable on the audit guest (QEMU virtio/bochs GPU, no AMD hardware).
Mirror of DF-4524's treatment: status untested, arithmetic verified by
check_arith.py in this pack.
Fix
Add the firmware offset to the bo size (one-line; keeps window layout intact):
--- a/sys/dev/drm/amd/amdgpu/vce_v2_0.c
+++ b/sys/dev/drm/amd/amdgpu/vce_v2_0.c
@@ -424,7 +424,8 @@ static int vce_v2_0_sw_init(void *handle)
}
r = amdgpu_vce_sw_init(adev, VCE_V2_0_FW_SIZE +
- VCE_V2_0_STACK_SIZE + VCE_V2_0_DATA_SIZE);
+ AMDGPU_VCE_FIRMWARE_OFFSET +
+ VCE_V2_0_STACK_SIZE + VCE_V2_0_DATA_SIZE);
if (r)
return r;
(Alternative, v4_0-style: clamp each VCE_VCPU_CACHE_SIZEn window to
amdgpu_bo_size(adev->vce.vcpu_bo) in vce_v2_0_mc_resume().)
DF-4742 VERDICT β vce_v2_0 VCE bo 256 bytes short
Status: untested (end-to-end), reproduced: 0, confidence: speculative (impact) / arithmetic certain β identical treatment to the sibling finding DF-4524 (vce_v3_0), which also could not be run for lack of AMD hardware on the audit guest.
What was verified, and how
- Allocation size β
vce_v2_0_sw_init()callsamdgpu_vce_sw_init(adev, VCE_V2_0_FW_SIZE + VCE_V2_0_STACK_SIZE + VCE_V2_0_DATA_SIZE)(sys/dev/drm/amd/amdgpu/vce_v2_0.c:424-425).amdgpu_vce_sw_init()createsvcpu_boat exactly that size (amdgpu_vce.c:181-183). No other allocation backs the windows. - Window layout β
vce_v2_0_mc_resume()(vce_v2_0.c:185-198) programs VCPU_CACHE_OFFSET0/1/2 at bo+256, bo+256+256K, bo+256+320K with sizes 256K / 64K / 23552*16.AMDGPU_VCE_FIRMWARE_OFFSETis 256 (amdgpu_vce.h:28). - Arithmetic β bo = 262144+65536+376832 = 704512 bytes = exactly 172
pages (no allocator slack); DATA window ends at 704768 = bo_end + 256.
Verified by
check_arith.py(seearith.log). - Firmware upload position β
amdgpu_vce_resume()copies the fw blob to bo offset 0 (amdgpu_vce.c:299-309) while the VCPU fetches fw through the bo+256 window; this offset-256 head is exactly the 256 bytes the sw_init size forgets. The fw copy itself does NOT overflow (datasize < FW window), so the host-side memcpy is safe β the exposure is the GPU-side DATA window tail, written by the VCE firmware during encode sessions. - Family consistency β v3 instance filed as DF-4524 (vce_v3_0.c:430,
sw_init size
FW + 2*(STACK+DATA)vs windows ending at 256+FW+2STACK+2DATA). v4 clean viaamdgpu_bo_size()clamp (vce_v4_0.c:440). v2 sits in the same defective generation with a single instance of DATA.
Why not reproduced on the guest
The audit guest is QEMU (bochs/virtio GPU, pciconf -l shows
vgapci0@pci0:0:2:0 chip=0x11111234), no AMD ASIC, so amdgpu never
binds a device, vce_v2_0_early_init/sw_init never run. The overrun is
firmware-behavior-dependent (whether the VCE fw actually touches the last
256 B of the DATA window) β same reason DF-4524 was filed speculative.
Negative checks performed on vce_v2_0.c (pass 2)
- IRQ dispatch
entry->src_data[0]is switch-guarded to 0/1,ring[AMDGPU_MAX_VCE_RINGS](vce_v2_0.c:540-549, amdgpu_vce.h:48) β safe. emit_frame_size=6 ==amdgpu_vce_ring_emit_fencedwords (amdgpu_vce.c:1056-1061);emit_ib_size=4 == emit_ib dwords (amdgpu_vce.c:1038-1041) β ring accounting exact.wait_for_idlelacks udelay but is upstream-identical; MMIO reads self-pace (~Β΅s each); not attacker-amplifiable beyond GPU-reset paths.firmware_loaded/lmi_cleanmdelay loops (~10 s worst) run only in hw_init / powergating (root/PM contexts) β not attacker-triggered.sw_finiearly-return whenamdgpu_vce_suspendsees live handles (amdgpu_vce.c:266-274) leaks bo/fw on unload with active sessions β root-triggered error-path leak, upstream-inherited; not filed.- parser wiring
parse_cs = amdgpu_vce_ring_parse_cs(vce_v2_0.c:615) β DF-4447 (desync) lives in amdgpu_vce.c and is NOT re-filed; the only v2-specific parser branch (0x0500000c hw-config, amdgpu_vce.c:838-850) rejects the command on BONAIRE/KABINI/HAWAII (v2 asics) while allowing KAVERI/MULLINS β functional asymmetry, no memory-safety impact.
Recommended fix
+ AMDGPU_VCE_FIRMWARE_OFFSET in the sw_init size (see fix.diff), or
clamp window sizes to amdgpu_bo_size() v4_0-style in mc_resume.
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
not_testableFix not kernel-validated: no AMD GPU on this guest, no baseline to flip. diff is arithmetic-exact against the verified window layout.
fix.diff (+256 bytes to bo size => window2 end == bo end)
Confirmed kernel references
Detail
Evidence (decisive lines)
arith.log (bo size 704512/172 pages, window2 end 704768, overrun=256B); VERDICT.md negative-check list; env.txt (guest GPU absent)
PoC changes
n/a (no seed; arithmetic harness written fresh)
Verified recommended fix
Add AMDGPU_VCE_FIRMWARE_OFFSET (256) to the amdgpu_vce_sw_init() size in vce_v2_0_sw_init(), or clamp CACHE_SIZEn windows to amdgpu_bo_size() v4_0-style
Verdict
vce_v2_0_sw_init sizes vcpu_bo at exactly FW+STACK+DATA (vce_v2_0.c:424-425 -> amdgpu_vce.c:181 bo_create_kernel) while vce_v2_0_mc_resume maps the VCPU cache windows starting AMDGPU_VCE_FIRMWARE_OFFSET=256 bytes into the bo (vce_v2_0.c:185-198): bo=704512 (exactly 172 pages, zero slack), DATA window ends at bo+704768 = bo_end+256, so the per-session DATA window's last 256 bytes lie in the VRAM object following vcpu_bo. Same defect shape as the filed v3 instance DF-4524; v4 is clean (clamps to amdgpu_bo_size, vce_v4_0.c:440). Arithmetic proven by check_arith.py (arith.log). End-to-end not runnable: guest has no AMD ASIC (QEMU bochs vgapci0 chip=0x11111234), and the actual fw write into the window tail is fw-behavior-dependent β hence speculative, mirroring DF-4524's treatment.
No comments yet.