# DF-4182 — Verification Verdict

**Status:** inconclusive (hardware-gated: no radeon GPU in the QEMU guest; the race needs a real CRTC with vblank interrupts)
**Impact:** none on this guest (permanent per-CRTC page-flip wedge + vblank refcount leak on affected hardware — local DoS)
**Confidence:** likely (state machine provable from source; the race window depends on fence wait timing)

## Mechanism (source trace)

1. `DRM_IOCTL_MODE_PAGE_FLIP` → core takes a vblank reference
   (drm_plane.c:1053 `drm_crtc_vblank_get`) →
   `radeon_crtc_page_flip_target()` (radeon_display.c:474):
   pins the new BO (:524), sets `flip_status = RADEON_FLIP_PENDING`,
   `flip_work = work`, `crtc->primary->fb = fb` (:584-588) and returns 0;
   the actual flip runs later on the crtc workqueue (:592 `queue_work`).
2. User immediately disables the CRTC (SETCRTC with no mode / DPMS off).
3. `radeon_flip_work_func()` (radeon_display.c:402): waits for fences,
   then the wait loop `while (radeon_crtc->enabled && ...)` (:449-458) —
   the `enabled` check only decides whether to *wait for vblank*;
   execution continues regardless.
4. Under the event lock: `radeon_irq_kms_pflip_irq_get()` (:464),
   `radeon_page_flip()` programs the surface base on the **disabled**
   CRTC (:467), and `flip_status = RADEON_FLIP_SUBMITTED` (:469).
5. Completion is detected by `radeon_crtc_handle_vblank()` /
   `radeon_crtc_handle_flip()` (:278/:359) from vblank/pflip interrupts —
   a disabled CRTC generates none, and **nothing else in the driver ever
   resets `flip_status`** (grep across sys/dev/drm/radeon/: only
   radeon_display.c:381 in `radeon_crtc_handle_flip`).
6. Consequences: `radeon_crtc_page_flip_target` rejects every future flip
   with -EBUSY (:578-583); the vblank event never fires
   (`drm_crtc_send_vblank_event` only in the completion path, :386);
   the `drm_crtc_vblank_get` reference from step 1 is never put (the put
   is in the completion path, :390) → vblank irq bookkeeping leaks;
   `work` (with the pinned old_rbo reference) is only freed by the
   unpin_work scheduled from the completion path (:392) → leak.

Reachability: any DRM master (unprivileged desktop session via logind) on
radeon KMS.  The window is the fence wait in step 3 (ms–s under load) —
trivially widened by keeping the GPU busy.  Local DoS of the flip path on
one CRTC until module reload.

## Why it cannot be reproduced on this guest

No ATI/AMD GPU, no /dev/dri; `rflip_wedge` builds and runs,
`open("/dev/dri/card0")` fails ENOENT (run.log).  The race itself requires
a real CRTC with vblank interrupts.

## Fix validation

`fix.diff`: stash `new_rbo` in `struct radeon_flip_work`; in
`radeon_flip_work_func`, when `!radeon_crtc->enabled` at programming
time, reset `flip_status`/`flip_work`, drop the vblank reference, deliver
the event, unpin the new BO and queue the existing unpin work.  Applies
cleanly (radeon.h + radeon_display.c); radeon.ko module built with the
patch (fix_build.log).  Expected behavior on hardware: after disable /
re-enable, PAGE_FLIP succeeds again (step 5 of README).

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