# DF-4976 VERDICT — VERIFIED-BY-TRACE, NOT EXECUTED (hardware absent)

**status: untested | reproduced: 0 | impact: none (on this guest) | confidence: likely**

## Defect proof (path:line)

1. **The work and its only queueing site.**
   `flr_work` lives in `struct amdgpu_virt` (amdgpu_virt.h:246). It is
   queued at exactly one site: `xgpu_ai_mailbox_rcv_irq` mxgpu_ai.c:296
   (`schedule_work(&adev->virt.flr_work)`), gated on an
   `IDH_FLR_NOTIFICATION` from the PF (mxgpu_ai.c:294) — i.e., the host
   decides when this work appears.

2. **The teardown hook does not drain it.**
   `xgpu_ai_mailbox_put_irq` (mxgpu_ai.c:365-369):
   ```c
   void xgpu_ai_mailbox_put_irq(struct amdgpu_device *adev)
   {
   	amdgpu_irq_put(adev, &adev->virt.ack_irq, 0);
   	amdgpu_irq_put(adev, &adev->virt.rcv_irq, 0);
   }
   ```
   `amdgpu_irq_put` (amdgpu_irq.c:541-562) only refcount-decrements and, at
   zero, calls `.set(DISABLE)` — it neither knows about nor waits for any
   work. A tree-wide grep confirms no `cancel_work_sync`/`flush_work` of
   `flr_work` anywhere (only mxgpu_ai.c:235/237/296/360 reference it).
   `put_irq` is called from `soc15_common_hw_fini` (soc15.c:843-846) —
   i.e., on **every** IP teardown: driver detach, suspend, and GPU
   recovery.

3. **What the surviving work does.**
   `xgpu_ai_mailbox_flr_work` (mxgpu_ai.c:235-271):
   - :237-238 `container_of` back to `adev` — the very object whose
     lifetime is ending;
   - :250-252 `mutex_trylock(&adev->lock_reset)`, `adev->in_gpu_reset = 1`;
   - :254-260 up to 500 ms of `RREG32_NO_KIQ` mailbox peeks
     (AI_MAILBOX_POLL_FLR_TIMEDOUT = 500, mxgpu_ai.h);
   - :269-270 `amdgpu_device_gpu_recover(adev, NULL)` — full reset attempt.

4. **The free.** Driver detach: `amdgpu_driver_unload_kms`
   (amdgpu_kms.c:75) → `amdgpu_device_fini` → per-IP `hw_fini` →
   `soc15_common_hw_fini` → `put_irq` (soc15.c:845) … and afterwards the
   drm device is released (`drm_dev_put(dev)` at amdgpu_drv.c:974 on the
   probe-error path; the unload path drops the final reference the same
   way), which frees `dev->dev_private` — the kzalloc'd `adev`
   (amdgpu_kms.c:180-183). A `flr_work` queued before `put_irq` (the irq
   that scheduled it is now disabled, but the queue entry persists) can be
   running or get picked up by a worker at any point after that:
   lock/unlock on freed `struct lock` memory, MMIO through stale mappings,
   `amdgpu_device_gpu_recover` on freed `adev` ⇒ use-after-free. Even when
   `adev` is not yet freed (recovery/suspend variant), the surviving work
   triggers an unintended second `amdgpu_device_gpu_recover` racing the
   fini/resume cycle (`gpu_recover` takes `adev->lock_reset` at
   amdgpu_device.c:3388+, so the two recoveries serialize into a
   back-to-back double reset rather than a deadlock).

5. **The fix idiom is available and already used next door.**
   `cancel_work_sync` is implemented by the DFly shim
   (sys/dev/drm/include/linux/workqueue.h:131; linux_workqueue.c) and is
   already used at teardown for `adev->reset_work` (amdgpu_irq.c:279/310)
   — this is an omission, not a port limitation.

## Honest boundary

Same guest as DF-4975: no AMD GPU (only QEMU std VGA chip=0x1111234), no
SR-IOV VF, amdgpu not compiled into the running kernel (env.txt in the
DF-4975 pack). The UAF additionally needs a host-timed FLR notification
just before teardown. Cannot be exercised from this guest ⇒ **untested**
(DB poc enum has no `not_testable`), with the defect established by the
trace above.

## Exploit chain

None to uid0. The freed-object handler is not attacker-shaped (fixed
function, fixed target object); realistic outcome is a panic or memory
corruption during teardown ⇒ availability impact, host-timed. Rated Low.

## Fix validation

fix_status: **not_testable** — `fix.diff` adds
`cancel_work_sync(&adev->virt.flr_work)` to `xgpu_ai_mailbox_put_irq`;
cannot be built/booted on this guest (hardware absent). The change
guarantees no `flr_work` remains queued/running once the mailbox irq
sources are torn down, which removes the hazard by construction.

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