u32 integer overflow in amdgpu_uvd_cs_msg_decode defeats DPB/ctx buffer size validation β UVD runs physical addressing, so undersized decode sessions GPU-DMA past validated buffers (cross-process VRAM/GTT corruption)
Summary
image_size, min_dpb_size and min_ctx_size are computed entirely in u32. For stream types that do not divide by fs_in_mb (VC1/MPEG2/MPEG4/H265) a decode message with width=0x80000000, height=2 wraps every product to 0/constant, and the gates pass with dpb_size=0 - the firmware is handed a 2^31-wide decode session whose buffers were never validated. UVD executes in PHYSICAL addressing (job->vm=NULL) with addresses patched from bo_gpu_offset, so firmware accesses land in adjacent VRAM/GTT slots - other processes' GPU objects and host pages via GTT-backed BOs. Deliberately avoids the known DF-1595 div0 (the fs_in_mb cases). Upstream fixed the identical site (05e1387d151f 2026, absent here). Guest Part-A arithmetic replica: min_dpb_size=0 with both gates PASS; raw-ioctl chain ready. Upstream-mirror dimension-reject fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4404 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| uvd_dim_overflow.c | β | 6.5 KB | view raw | |
| build.sh | β | 181 B | view raw | |
| run.sh | β | 123 B | view raw | |
| build.log | β | 72 B | view raw | |
| run.log | β | 1.6 KB | view raw | |
| run.2.log | β | 1.6 KB | view raw | |
| env.txt | β | 708 B | view raw | |
| fix.diff | β | 796 B | view raw | |
| verdict.json | β | 3.4 KB | view raw | |
| manifest.json | β | 1.2 KB | view raw |
DF-4404 β u32 integer overflow in amdgpu_uvd_cs_msg_decode() defeats DPB/ctx buffer size validation (GPU physical-mode OOB)
File: sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:514-696 (decode), used by
amdgpu_uvd_cs_pass2() at sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:803-878.
Severity: High Β· Confidence: certain (kernel-side math proven; upstream
fixed the same defect in Linux commit 05e1387d151f "drm/amdgpu: Reject UVD
message with dimensions above 4096 β Fixes potential overflow in DPB size
calculations", 2026-07-30; the < 16 half that fixes known DF-1595 is commit
3e41d26c70b0).
Build
cc -O2 -I/usr/src/sys/dev/drm/include/uapi -o uvd_dim_overflow uvd_dim_overflow.c
(or ./build.sh which does this on the audit guest via ssh)
Run
./uvd_dim_overflow
(or ./run.sh on the audit guest)
Expected output
- Part A (always): replicates the kernel arithmetic and shows that for a
UVD MPEG2 decode message with
msg[6] = width = 0x80000000,msg[7] = height = 2,msg[9] = dpb_size = 0: image_size = (width*height + width*height/2) mod 2^32wraps to0(amdgpu_uvd.c:531-533),min_dpb_size = image_size*3 = 0(amdgpu_uvd.c:596-600),- the gate
dpb_size < min_dpb_size(amdgpu_uvd.c:686) passes with a zero-sized DPB, amdgpu_uvd_cs_pass2()then only requires(end-start) >= buf_sizes[1] = 0for the DPB buffer command (amdgpu_uvd.c:833) β any mapping passes.- Same demonstration for H265 (
case 16,amdgpu_uvd.c:666-674) whereALIGN(w,16)*ALIGN(h,16)wraps and bothmin_dpb_sizeandmin_ctx_sizecollapse. - Part B (needs AMD GPU): opens
/dev/dri/renderD128//dev/dri/card0and submits the crafted decode message. On the audit QEMU guest there is no AMD GPU (only QEMU bochs VGA), so Part B prints the open failures and exits 2.
Success criterion
On real AMD hardware (CIK..Vega UVD, amdgpu attached, user in group video β
DFly DRM nodes are 0660 root:video, sys/dev/drm/include/drm/drmP.h:108),
the CS is accepted (no -EINVAL) despite the undersized DPB β proving the
validator bypass; the firmware then decodes a stream whose declared dimensions
are 2^31 wide, accessing DPB well beyond the validated object. On this guest,
Part A + line-certain static proof is the deliverable.
Not-a-PoC note
The audit guest has no AMD GPU (pciconf -lv: only vgapci0 bochs
0x11111234; amdgpu.ko loads but attaches to nothing, /dev/dri absent) β
the ioctl path cannot be executed here. Status: inconclusive (see
VERDICT.md), kernel-side math certain.
DF-4404 β VERDICT
Finding: u32 integer overflow in amdgpu_uvd_cs_msg_decode()
(sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:514-696) under-computes
min_dpb_size / min_ctx_size for the non-dividing stream types (VC1=1,
MPEG2=3, MPEG4=4, H265=16), defeating the decode-message buffer-size gate at
amdgpu_uvd.c:686 and the mapping-size gate at amdgpu_uvd.c:833, so the UVD
engine (running in physical addressing, parser->job->vm = NULL,
amdgpu_uvd.c:983) is handed decode sessions whose DPB/ctx buffers were never
validated to cover the declared stream β GPU-side OOB access into adjacent
VRAM/GTT slots (other processes' GPU objects; via GTT, host memory pages).
Verdict: inconclusive (runtime execution impossible on the audit guest:
no AMD GPU). The kernel-side defect itself is certain:
- Math proven.
run.log/run.2.log(guest, cc 8.3): Part A replicates the exact kernel arithmetic (unsigned= u32) β for MPEG2,width=0x80000000, height=2, dpb_size=0βimage_size=0,min_dpb_size=0, both gates PASS. For H265,min_dpb_size=0andmin_ctx_size=0xee00(52 KiB constant only) for a 2^31-wide stream. The same wrap does NOT hit the known DF-1595 div-by-zero because stream types 1/3/4/16 contain no division byfs_in_mb. - Upstream confirms the class. Linux fixed the identical site:
commit
05e1387d151f"drm/amdgpu: Reject UVD message with dimensions above 4096 β Fixes potential overflow in DPB size calculations" (2026-07-30, by AMD), plus3e41d26c70b0(the< 16half = DF-1595's fix). The DragonFly tree carries neither. - Guest cannot execute Part B.
pciconf -lvshows the only display device is QEMU bochs VGA (vgapci0, chip0x11111234);amdgpu.koloads ([drm] amdgpu kernel modesetting enabledin dmesg) but attaches to nothing;/dev/dridoes not exist. The GEM/VA/CS ioctl chain therefore cannot run. This is a hardware limitation of the audit guest, not a refutation of the code path β the path is line-certain fromamdgpu_cs_ioctlβamdgpu_uvd_ring_parse_csβamdgpu_uvd_cs_pass2βamdgpu_uvd_cs_msgβamdgpu_uvd_cs_msg_decode.
Exploit chain (as it would run on real hardware)
- Local user in group
video(DFly DRM nodes are0660 root:video,sys/dev/drm/include/drm/drmP.h:108-110) opens/dev/dri/card0. - GEM-create two small VRAM BOs (msg, dpb), map them into the VM (GEM_VA), create a context.
- Submit a CS on the UVD ring whose IB: (a) writes
DATA0/DATA1= msg BO GPU address and issues the GPCOM msg command with a decode message declaringstream_type=3 (MPEG2), width=0x80000000, height=2, dpb_size=0; (b) issues the DPB buffer command pointing at the tiny dpb BO.amdgpu_uvd_ring_parse_csvalidates both becausemin_dpb_sizewrapped to 0. - UVD firmware parses the same message with real dimensions and its DPB accesses run past the validated object into adjacent VRAM/GTT slots β cross-process GPU-object corruption, or host-page corruption when the neighbor slot is a GTT-backed object; GPU hang (DoS) is the floor.
Whether step 4 corrupts memory or merely hangs depends on the UVD firmware's
internal use of the declared dimensions β hence impact: none (not
demonstrable here) with the bypass itself certain.
Attempts
1 runtime attempt on the guest (build OK, Part A executed, Part B blocked by
hardware absence) + line-level trace of every gate. See run.log, run.2.log,
env.txt.
Fix
fix.diff (applies cleanly with git apply) mirrors upstream
05e1387d151f + 3e41d26c70b0: reject width/height < 16 || > 4096 before
any arithmetic. With the clamp, image_size β€ 4096*4096*3/2 and every
width_in_mb*height_in_mb*k product fits u32; the div-by-zero (DF-1595) is
covered by the same check. Not applied to sys/ (read-only audit target); fix
validation on the guest is impossible for the same hardware reason, so
fix_status: not_testable.
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.diff mirrors the upstream clamp and git-apply --check passes against sys/, but a patched-kernel A/B run is impossible on this guest (no AMD GPU to attach amdgpu, so neither baseline nor patched path can execute).
fix.diff; git apply --check output in session log; upstream commits 05e1387d151f / 3e41d26c70b0
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:514
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:531
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:596
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:666
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:686
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:833
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:983
- sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:1005
Detail
Evidence (decisive lines)
run.log/run.2.log Part A (u32 wrap: min_dpb_size=0, both gates PASS); env.txt (bochs-only VGA, amdgpu.ko loaded, no /dev/dri); VERDICT.md full trace; fix.diff mirrors upstream clamp and applies cleanly (git apply --check)
PoC changes
Trigger written fresh (no seed): Part A replicates kernel u32 arithmetic; Part B implements the device-open stage of the GEM/VA/CS chain and stops honestly when no AMD GPU is present. Needed -I/usr/src/sys/dev/drm/include (asm/int-ll64.h) and #define __user empty for userland UAPI compile.
Verified recommended fix
Reject width/height < 16 or > 4096 in amdgpu_uvd_cs_msg_decode (mirror upstream 05e1387d151f + 3e41d26c70b0); see fix.diff.
Verdict
Kernel-side defect certain: u32 overflow in amdgpu_uvd_cs_msg_decode (sys/dev/drm/amd/amdgpu/amdgpu_uvd.c:531-533,596-611,666-674) collapses min_dpb_size/min_ctx_size to 0 for non-dividing stream types (VC1/MPEG2/MPEG4/H265) with width=0x80000000/height=2, bypassing the dpb_size gate (line 686) and the pass2 mapping gate (line 833); proven by exact-arithmetic replica on the guest (run.log) and by upstream's own fix (Linux 05e1387d151f 'Reject UVD message with dimensions above 4096', 2026-07-30, absent from this tree). Runtime PoC impossible on the audit guest: no AMD GPU (QEMU bochs VGA only), amdgpu.ko loads but /dev/dri never appears, so the CS ioctl chain cannot execute; impact not demonstrated (inconclusive), not refuted.
No comments yet.