amdgpu fb creation never validates framebuffer size against the backing GEM object (twin of DF-4181; covers BOTH legacy DCE and DC scanout stacks) β display engine scans out megabytes past a tiny BO, disclosing adjacent VRAM on the physical display
Summary
amdgpu_display_user_framebuffer_create resolves the handle and hands mode_cmd to amdgpu_display_framebuffer_init, which only fills and registers the fb struct. Nothing between ioctl and hardware compares pitch*height+offsets against amdgpu_bo_size(): DRM core checks format/dims/pitch but never sees the BO. A 16KB BO accepts a 1920x1080 XRGB8888 fb needing 8,294,400 bytes. UNLIKE the radeon twin, this single create fn is registered for BOTH display stacks (legacy amdgpu_mode_funcs DCE8/10/11 AND DC amdgpu_dm_funcs DCE12/DCN) - every ASIC this driver supports is affected. Scanout pins and programs GRPH_X_END/PITCH/VIEWPORT with no re-check (dce_v8/10/11 + DC prepare_fb + page-flip). Unprivileged DRM master; visual-disclosure ceiling. PoC built to the hardware gate; per-plane min-size fix compile-validated via standalone amdgpu.ko -Werror build. DF-1717 untouched.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4183 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | verification verdict + full path:line trace | 5.3 KB | β raw |
| amdgpu_fb_oob.c | trigger | CREATE_DUMB 64x64 + ADDFB2 1920x1080 on the tiny BO (twin of DF-4181 rfb_oob.c) | 4.8 KB | view raw |
| build.sh | build | build command | 99 B | view raw |
| build.log | buildlog | guest compile of the trigger | 13 B | view raw |
| run.sh | run | run command | 150 B | view raw |
| run.log | runlog | guest run: open /dev/dri/card0 ENOENT (no GPU) | 161 B | view raw |
| env.txt | env | guest environment: no AMD/ATI PCI, no /dev/dri, no drm modules | 445 B | view raw |
| fix.diff | suggested-fix | per-plane BO min-size check in amdgpu_display_framebuffer_init (git-apply-able) | 2.0 KB | view raw |
| fix_build.log | fixbuild | amdgpu.ko standalone module build with the fix: -Werror -DKLD_MODULE, rc=0, zero warnings | 1.2 MB | β download |
| verdict.json | verdict | machine verdict | 4.6 KB | view raw |
DF-4183 β Verification Verdict (amdgpu twin of DF-4181)
Status: inconclusive (hardware-gated: no AMD/ATI 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, width/height within mode_config limits (:288-298),height*pitches[i] + offsets[i] <= UINT_MAX(:208),pitches[i] >= width*cpp(:211), block alignment for some formats. It never looks at the buffer object (it cannot β GEM handles are resolved by the driver'sfb_create, dispatched at :310).amdgpu_display_user_framebuffer_create()(amdgpu_display.c:523-558):drm_gem_object_lookup()(:531), rejects imported dma-bufs (:539 β the DF-1717 neighborhood),kzalloc(:544), callsamdgpu_display_framebuffer_init()(:550). No comparison of required image size vs BO size. It is registered as.fb_createfor both display stacks in this tree: - legacy:amdgpu_mode_funcs(amdgpu_display.c:560-561) β used by DCE8/DCE10/DCE11 ASICs; - DC (DAL):amdgpu_dm_funcs(amdgpu_dm.c:945) β used by DCE12/DCN. So the gap is ASIC-uniform: every amdgpu chip this driver supports.amdgpu_display_framebuffer_init()(amdgpu_display.c:506-520):drm_helper_mode_fill_fb_struct()(:513) fills pitches/format frommode_cmd; then straight todrm_framebuffer_init()(:514) β again no size check and no other content validation (the DRM core owns the format check). Same helper is reused by the kernel-internal fbdev path (amdgpu_fb.c:248) whose sizes are kernel-computed (not attacker-chosen), so the fix belongs in this helper and covers both callers harmlessly.- Scanout paths pin and program without re-checking fb dimensions vs
amdgpu_bo_size(): - legacy DCE11:dce_v11_0_crtc_do_set_baseβamdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM)(dce_v11_0.c:1904),fb_location = amdgpu_bo_gpu_offset(abo)(:1910), then programsGRPH_X_END = fb->width/GRPH_Y_END = fb->height(:2073-2074),GRPH_PITCH = pitches[0]/cpp(:2076-2077) andVIEWPORT_SIZE = crtc mode(:2088-2091): the display engine fetchespitch * heightbytes from the pinned base address regardless of the object's true size. Same shape: dce_v10_0.c:1862, dce_v8_0.c:1791. - DC:dm_plane_helper_prepare_fb(amdgpu_dm.c:3484) βamdgpu_bo_pin(rbo, domain)(:3518); plane attributes consume fb pitches/dims downstream β no size gate. - page flip:amdgpu_display_crtc_page_flip_target(amdgpu_display.c:146) βamdgpu_bo_pin(new_abo, β¦)(:191) β the flip work then swaps the scanout base to the undersized pinned BO (amdgpu_display.c:109).
Impact: an unprivileged user with DRM master/modeset rights (any desktop session via logind) creates a 16 KiB VRAM BO (64x64x32 dumb buffer), registers a 1920x1080 XRGB8888 fb on it (8,294,400 B required), and modesets/flips: the panel then displays the contents of VRAM immediately following the BO β other applications' buffers. The disclosure is to the physical display, so the ceiling is visual; no kernel memory involved, no write primitive. Rating: Medium (info leak, default desktop configs, needs amdgpu KMS) β identical to DF-4181 (radeon).
Upstream context: this DRM vintage predates drm_gem_fb_create() with its
per-plane BO-size checks; upstream later moved amdgpu fb creation onto
that helper, closing the gap there.
Why it cannot be reproduced on this guest
The QEMU guest has no ATI/AMD GPU: no PCI vendor-0x1002 device
(pciconf), no /dev/dri at all, and no drm modules loaded (env.txt).
amdgpu_fb_oob builds and runs; open("/dev/dri/card0") fails ENOENT
(run.log, exit 2). The gate is hardware absence β without a PCI AMD GPU
the amdgpu drm device node cannot exist β so the finding is
source-certain but runtime-untestable here (same classification as
DF-4181 for radeon).
Fix validation
fix.diff adds the per-plane
offsets[i] + pitches[i] * DIV_ROUND_UP(height, vsub) <= amdgpu_bo_size()
check in amdgpu_display_framebuffer_init() (amdgpu_display.c:506), i.e.
exactly the DF-4181 radeon fix transplanted to the amdgpu helper β covering
the user path for both the legacy (amdgpu_mode_funcs) and DC
(amdgpu_dm.c:945) fb_create registrations. Applied cleanly to the guest
/usr/src (md5 21c72601β¦ β f68f1ac9β¦), amdgpu.ko built standalone with
-Werror -DKLD_MODULE (make -j6 in sys/dev/drm/amd/amdgpu: rc=0,
28.7 s, zero error/warning lines, --- amdgpu.ko --- linked with the
patched amdgpu_display.o β fix_build.log:90). Source reverted to pristine
after the build. 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 to the guest /usr/src (md5 21c72601... -> f68f1ac9...) and amdgpu.ko built standalone with -Werror -DKLD_MODULE (make -j6 in sys/dev/drm/amd/amdgpu: rc=0 in 28.7s, zero error/warning lines, amdgpu.ko 3.7 MB linked with the patched amdgpu_display.o; fix_build.log:90). Source reverted to pristine after the build. Behavioral test impossible on this guest (no AMD GPU).
fix_build.log line 90 (amdgpu_display.c compile with -Werror -DKLD_MODULE), '--- amdgpu.ko ---' link line, zero 'error:'/'warning:' matches; md5 sequence 21c726011a45df09bacd7632af9f2e81 -> f68f1ac92d89aaeb3985c00c4d684eec -> 21c726011a45df09bacd7632af9f2e81 in the run transcript.
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/amdgpu_display.c:523
- sys/dev/drm/amd/amdgpu/amdgpu_display.c:506
- sys/dev/drm/amd/amdgpu/amdgpu_display.c:561
- sys/dev/drm/amd/amdgpu/amdgpu_display.c:191
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:945
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:3518
- sys/dev/drm/amd/amdgpu/dce_v11_0.c:1904
- sys/dev/drm/amd/amdgpu/dce_v11_0.c:2073
- sys/dev/drm/amd/amdgpu/dce_v11_0.c:2076
- sys/dev/drm/amd/amdgpu/dce_v10_0.c:1862
- sys/dev/drm/amd/amdgpu/dce_v8_0.c:1791
- sys/dev/drm/drm_framebuffer.c:165
- sys/dev/drm/drm_framebuffer.c:208
- sys/dev/drm/drm_framebuffer.c:211
- sys/dev/drm/drm_framebuffer.c:310
Detail
Evidence (decisive lines)
VERDICT.md (core-vs-driver responsibility trace incl. DC path); amdgpu_fb_oob.c (CREATE_DUMB 64x64 + ADDFB2 1920x1080; success criterion: ADDFB2==0 bug vs -EINVAL fixed); build.log + run.log (build OK, open /dev/dri/card0 ENOENT, exit 2); env.txt (no vendor-0x1002 PCI, no drm klds); fix.diff + fix_build.log (amdgpu.ko built standalone -Werror -DKLD_MODULE with fix, rc=0)
PoC changes
Adapted from DF-4181's rfb_oob.c: same raw-ioctl trigger (no libdrm), amdgpu path citations and the amdgpu-specific fb_create registration sites added; UAPI identical for amdgpu.
Verified recommended fix
Per-plane check in amdgpu_display_framebuffer_init: offsets[i] + pitches[i]*DIV_ROUND_UP(height, vsub) must be <= amdgpu_bo_size(obj), else -EINVAL (mirrors DF-4181 radeon fix).
Verdict
Source-certain missing size validation, amdgpu twin of DF-4181: amdgpu_display_user_framebuffer_create (amdgpu_display.c:523) / amdgpu_display_framebuffer_init (amdgpu_display.c:506) never compare the requested scanout image size against the backing GEM object, and this one create function serves BOTH display stacks (.fb_create registered in amdgpu_mode_funcs at amdgpu_display.c:561 for legacy DCE8/10/11 and in amdgpu_dm.c:945 for DC). 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 (fb_create dispatched at :310). A 16 KiB BO therefore backs a 1920x1080 XRGB8888 fb (8,294,400 B); every scanout path then pins and programs without re-checking - legacy dce_v11_0_crtc_do_set_base pins (dce_v11_0.c:1904) and programs GRPH_X_END/Y_END=fb->width/height + GRPH_PITCH (:2073-2077) + VIEWPORT_SIZE (:2088-2091), same shape dce_v10_0.c:1862 / dce_v8_0.c:1791; DC dm_plane_helper_prepare_fb pins (amdgpu_dm.c:3518); the flip path pins the new BO (amdgpu_display.c:191) - so the display engine fetches pitch*height bytes from the undersized object and discloses adjacent VRAM contents on the physical display. Not exercisable on this guest: no AMD/ATI PCI device, no /dev/dri (run.log, env.txt); hardware-gated like DF-4181.
No comments yet.