# DF-4975 — AI (Vega20-class) mailbox RCV interrupt enabled before flr_work is initialized (DF-4755 twin)

File: `sys/dev/drm/amd/amdgpu/mxgpu_ai.c` (pass 2, GLM 5.3)

## What

`xgpu_ai_mailbox_get_irq()` enables the BIF mailbox RCV interrupt source
(`amdgpu_irq_get` at mxgpu_ai.c:351 → `xgpu_ai_set_mailbox_rcv_irq`
mxgpu_ai.c:273-285 → VALID_INT_EN=1 in BIF_BX_PF0_MAILBOX_INT_CNTL)
**before** `INIT_WORK(&adev->virt.flr_work, ...)` at mxgpu_ai.c:360.

Any `IDH_FLR_NOTIFICATION` delivered by the host/PF inside that window is
handled by `xgpu_ai_mailbox_rcv_irq` (mxgpu_ai.c:287-310), which — because
`amdgpu_sriov_runtime(adev)` is already true at this point (proof below) —
executes `schedule_work(&adev->virt.flr_work)` (mxgpu_ai.c:296) on a
**zeroed** (kzalloc'd, never INIT_WORK'ed) `work_struct`.

Under DragonFly's DRM workqueue shim (`sys/dev/drm/include/linux/workqueue.h:47-56`,
`sys/dev/drm/linux_workqueue.c:120-140`), `queue_work()` on a zeroed work has
`on_queue == false`, so the work is STAILQ-inserted (the NULL link pointers
are simply overwritten) and later executed by the worker at
linux_workqueue.c:89 via `work->func(work)` where `func == NULL`
→ **call through NULL function pointer → kernel panic**.

Structurally identical to DF-4755 (mxgpu_vi.c:600-609), which pass 2 filed
for the VI generation. This is the AI-generation twin; mxgpu_ai.c had no
prior finding of this class.

## Why the schedule_work path is live during the window

`xgpu_ai_mailbox_rcv_irq` only schedules when `amdgpu_sriov_runtime(adev)`
(AMDGPU_SRIOV_CAPS_RUNTIME) is set. Boot order proves it is set *before*
`late_init` → `get_irq` runs:

1. `amdgpu_device_init()` → `amdgpu_virt_request_full_gpu(adev, true)`
   (amdgpu_device.c:1497) clears RUNTIME (amdgpu_virt.c:259).
2. End of `amdgpu_device_init()` → `amdgpu_virt_release_full_gpu(adev, true)`
   (amdgpu_device.c:1696) **sets RUNTIME** (amdgpu_virt.c:282).
3. Only later, `amdgpu_device_ip_late_init()` (amdgpu_device.c:2700) →
   `soc15_common_late_init` (soc15.c:800-804) → `xgpu_ai_mailbox_get_irq()`
   → irq_get(rcv) at mxgpu_ai.c:351 … INIT_WORK at mxgpu_ai.c:360.

`adev` itself is kzalloc'd (amdgpu_kms.c:180), so `flr_work` is all-zero
until line 360. The race window is the irq enable → INIT_WORK distance
(two register write round trips + a second `amdgpu_irq_get`), into which a
host-timed (or merely unlucky) FLR notification lands.

The same window re-opens on resume: `amdgpu_device_resume()` re-runs
`amdgpu_device_ip_late_init()` (amdgpu_device.c:2936) → `get_irq()` again.

## Threat model

SR-IOV VF guest only (Vega20-class AI asic). The trigger actor is the
host/PF (trusted boundary), so this is host-timed DoS/panic — same rating
basis as DF-4755: **Low**. Impact when it fires: guest kernel panic
(NULL func call in a workqueue worker thread).

## Recommended fix (fix.diff in this pack)

Initialize the work before enabling the interrupt source:

```diff
 int xgpu_ai_mailbox_get_irq(struct amdgpu_device *adev)
 {
 	int r;
 
+	INIT_WORK(&adev->virt.flr_work, xgpu_ai_mailbox_flr_work);
+
 	r = amdgpu_irq_get(adev, &adev->virt.rcv_irq, 0);
 	if (r)
 		return r;
 	r = amdgpu_irq_get(adev, &adev->virt.ack_irq, 0);
 	if (r) {
 		amdgpu_irq_put(adev, &adev->virt.rcv_irq, 0);
 		return r;
 	}
-
-	INIT_WORK(&adev->virt.flr_work, xgpu_ai_mailbox_flr_work);
 
 	return 0;
 }
```

## Reproduce / verification status

Not runnable on the audit guest: requires an AI-generation (Vega20-class)
ASIC in SR-IOV VF mode; the QEMU guest has only a QEMU std VGA device
(see env.txt). Verification is by complete code-order trace (VERDICT.md).
