radeon fb creation never validates framebuffer size against the backing GEM object β display engine scans out megabytes past a tiny BO, disclosing adjacent VRAM on the physical display
Summary
The DRM core validates format/width/height/pitch but never resolves handles or compares image size to the buffer object; radeon_user_framebuffer_create and radeon_framebuffer_init fill the fb with no size check either - nothing between addfb2 and scanout/pin ever compares pitch*height against the BO. A 16KB dumb buffer backs a 1920x1080 XRGB8888 fb (8,294,400B) and the display engine reads ~8MB past the object end - adjacent VRAM contents (other processes' buffers) appear on screen. Unprivileged with DRM master. Info-leak ceiling (visual). amdgpu_display.c:523 carries the identical gap (follow-up). Per-plane min-size fix (validated -Werror).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4181 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | verification verdict + full path:line trace | 3.5 KB | β raw |
| rfb_oob.c | trigger | CREATE_DUMB 64x64 + ADDFB2 1920x1080 on the tiny BO | 3.6 KB | view raw |
| build.sh | build | build command | 87 B | view raw |
| run.sh | run | run command | 144 B | view raw |
| run.log | runlog | guest run: open /dev/dri/card0 ENOENT (no GPU) | 162 B | view raw |
| env.txt | env | guest environment | 621 B | view raw |
| fix.diff | suggested-fix | per-plane BO min-size check in radeon_framebuffer_init (git-apply-able) | 1.9 KB | view raw |
| verdict.json | verdict | machine verdict | 2.9 KB | view raw |
DF-4181 β Verification Verdict
Status: inconclusive (hardware-gated: no radeon GPU in the QEMU guest) Impact: none on this guest (display-engine OOB read past a GEM object on affected hardware β info disclosure of adjacent VRAM to the physical display) Confidence: certain (absence of any size validation provable from source)
Mechanism (source trace)
DRM_IOCTL_MODE_ADDFB2βdrm_mode_addfb2βdrm_internal_framebuffer_create(drm_framebuffer.c:277).framebuffer_check()(drm_framebuffer.c:165-275) validates: format known (via__drm_format_info), width/height within mode_config min/max,width*cpp <= UINT_MAX,height*pitches[i] + offsets[i] <= UINT_MAX(:208),pitches[i] >= width*cpp(:211). It never looks at the buffer object at all (it doesn't have it β handles are resolved by the driver's fb_create).radeon_user_framebuffer_create()(radeon_display.c:1311-1348):drm_gem_object_lookup()on handles[0] (:1320), rejects imported dma-bufs (:1328), allocates the fb (:1334) and callsradeon_framebuffer_init()(:1340). No comparison of required image size vs BO size.radeon_framebuffer_init()(radeon_display.c:1294-1309):drm_helper_mode_fill_fb_struct()fills pitches/format; then straight todrm_framebuffer_init()β again no size check.- Scanout path: set_config/page_flip pin the BO
(
radeon_bo_pin(..., RADEON_GEM_DOMAIN_VRAM, ...)β pinning does not check fb dimensions either) and program the display base address + pitch; the display engine then fetchespitch * crtc_vdisplaybytes starting at the BO β reading far past its end when the fb is larger than the BO. The flip path repeats this (radeon_display.c:524-539, radeon_crtc_page_flip_target pins new_rbo and computespitch_pixels = fb->pitches[0] / fb->format->cpp[0]). - Same gap exists in amdgpu_display_user_framebuffer_create (amd/amdgpu/amdgpu_display.c:523) β this DRM vintage predates the drm_gem_fb size checks; upstream later moved radeon/amdgpu to drm_gem_fb_create which checks per-plane min-size vs BO size.
Impact: an unprivileged user with DRM master/modeset rights (any desktop session via logind) creates a small VRAM BO, registers an oversized fb on it, and modesets: the panel then displays the contents of VRAM immediately following the BO β other applications' buffers. The leak is to the physical display (photo/screen-capture by a second GPU path is not available), so the ceiling is visual disclosure; no kernel memory is involved and no write primitive exists. Rating: Medium (info leak, default desktop configs, needs radeon KMS).
Why it cannot be reproduced on this guest
QEMU guest has no ATI/AMD GPU: no /dev/dri, radeon.ko not loaded.
rfb_oob builds and runs; open("/dev/dri/card0") fails ENOENT
(run.log).
Fix validation
fix.diff adds the per-plane
offsets[i] + pitches[i] * DIV_ROUND_UP(height, vsub) <= radeon_bo_size()
check in radeon_framebuffer_init(). Applies cleanly; radeon.ko module
built with the patch (fix_build.log β module build, since radeon is not
part of X86_64_GENERIC). Expected behavioral change on hardware: the PoC
ADDFB2 returns -EINVAL.
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
inconclusiveCompile-validated only: fix.diff applied cleanly and radeon.ko built with -Werror as part of the batch nativekernel build (fix_build.log: radeon_display.c compiled, '--- radeon.ko ---' linked, zero errors). Behavioral test impossible on this guest (no GPU).
fix_build.log excerpt (radeon_display.c -Werror KLD_MODULE compile + radeon.ko link).
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (core-vs-driver responsibility trace); rfb_oob.c (CREATE_DUMB 64x64 + ADDFB2 1920x1080; success criterion: ADDFB2==0 bug vs -EINVAL fixed); run.log (no GPU, build OK); fix.diff + fix_build.log (radeon.ko built -Werror with fix)
PoC changes
n/a (first-pass PoC authored in this run; raw ioctls, no libdrm dependency)
Verified recommended fix
Per-plane check in radeon_framebuffer_init: offsets[i] + pitches[i]*DIV_ROUND_UP(height, vsub) must be <= radeon_bo_size(obj), else -EINVAL.
Verdict
Source-certain missing size validation: radeon_user_framebuffer_create (radeon_display.c:1311) / radeon_framebuffer_init (radeon_display.c:1294) never compare the requested scanout image size against the backing GEM object; the DRM core's framebuffer_check (drm_framebuffer.c:165-275) only enforces format validity, pitch>=widthcpp (:211) and heightpitch+offsets<=UINT_MAX (:208) and never sees the object. A 16 KiB BO therefore backs a 1920x1080 XRGB8888 fb (8,294,400 B); the display engine then scans out ~8 MB past the object end in VRAM, disclosing adjacent VRAM contents (other applications' buffers) on the physical display; the flip path repeats this with the undersized pinned BO (radeon_display.c:524-539). amdgpu_display.c:523 has the identical gap in this tree (pre-drm_gem_fb vintage). Not exercisable on this guest: no GPU, no /dev/dri (run.log).
No comments yet.