# 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)

1. `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's `fb_create`, dispatched at :310).
2. `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), calls
   `amdgpu_display_framebuffer_init()` (:550). **No comparison of required
   image size vs BO size.** It is registered as `.fb_create` for **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.
3. `amdgpu_display_framebuffer_init()` (amdgpu_display.c:506-520):
   `drm_helper_mode_fill_fb_struct()` (:513) fills pitches/format from
   `mode_cmd`; then straight to `drm_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.
4. 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 programs `GRPH_X_END =
     fb->width` / `GRPH_Y_END = fb->height` (:2073-2074), `GRPH_PITCH =
     pitches[0]/cpp` (:2076-2077) and `VIEWPORT_SIZE = crtc mode`
     (:2088-2091): the display engine fetches `pitch * height` bytes 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.
