AI (Vega20-class) mailbox RCV interrupt enabled before flr_work is initialized β host-timed IDH_FLR_NOTIFICATION executes schedule_work() on a zeroed work_struct β NULL function-pointer call β kernel panic (DF-4755 twin)
Summary
The exact DF-4755 ordering defect in the AI generation: amdgpu_irq_get at :351 writes VALID_INT_EN=1 nine lines before INIT_WORK at :360, and amdgpu_sriov_runtime is already true at late_init - a host-timed FLR in the window schedules the zeroed work and the shim calls func==NULL (linux_workqueue.c:89). Window re-opens on every resume. INIT_WORK-first fix. SR-IOV hw-gated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4975 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.6 KB | β raw | |
| VERDICT.md | β | 4.7 KB | β raw | |
| fix.diff | β | 806 B | view raw | |
| verdict.json | β | 3.2 KB | view raw | |
| manifest.json | β | 1020 B | view raw | |
| env.txt | β | 1.1 KB | view raw |
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:
amdgpu_device_init()βamdgpu_virt_request_full_gpu(adev, true)(amdgpu_device.c:1497) clears RUNTIME (amdgpu_virt.c:259).- End of
amdgpu_device_init()βamdgpu_virt_release_full_gpu(adev, true)(amdgpu_device.c:1696) sets RUNTIME (amdgpu_virt.c:282). - 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:
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).
DF-4975 VERDICT β VERIFIED-BY-TRACE, NOT EXECUTED (hardware absent)
status: untested | reproduced: 0 | impact: none (on this guest) | code-path confidence: certain / manifestation: likely
Why this is a real defect (line-by-line)
-
Enabling before init.
xgpu_ai_mailbox_get_irq(): - mxgpu_ai.c:351amdgpu_irq_get(adev, &adev->virt.rcv_irq, 0)β first enable of the source (refcount 0β1) callsamdgpu_irq_update()(amdgpu_irq.c:511-525) which invokes the.setcallbackxgpu_ai_set_mailbox_rcv_irq(mxgpu_ai.c:273-285) β writes VALID_INT_EN=1 tommBIF_BX_PF0_MAILBOX_INT_CNTL(mxgpu_ai.c:280-282). From this instruction onward the device can raise the mailbox RCV irq. - mxgpu_ai.c:360INIT_WORK(&adev->virt.flr_work, xgpu_ai_mailbox_flr_work)β the work is only now given afunc. Between 351 and 360 it is a zeroed blob (adevis kzalloc'd, amdgpu_kms.c:180-183; no other initializer exists βINIT_WORKforflr_workappears exactly once in the tree, at mxgpu_ai.c:360). -
The ISR path is live in that window.
xgpu_ai_mailbox_rcv_irq(mxgpu_ai.c:287-310): peeks RCV_DW0 (line 291); forIDH_FLR_NOTIFICATION(line 294) checksamdgpu_sriov_runtime(adev)(line 295) thenschedule_work(&adev->virt.flr_work)(line 296). - RUNTIME bit proven set before late_init:amdgpu_device_init()ends withamdgpu_virt_release_full_gpu(adev, true)at amdgpu_device.c:1696, which setsAMDGPU_SRIOV_CAPS_RUNTIME(amdgpu_virt.c:282).amdgpu_device_ip_late_init()(amdgpu_device.c:2700) βsoc15_common_late_init(soc15.c:800-804) βget_irq()runs strictly after. So line 295's gate is open. - In this port.processhandlers run in hard-IRQ context (same basis as DF-4756's "hard-IRQ .process handler");schedule_workfrom hardirq is supported and intended. -
What a zeroed work does under the DFly shim.
struct work_struct(sys/dev/drm/include/linux/workqueue.h:47-56):ws_entries(STAILQ),func,worker,on_queue,running,canceled. Zeroed βfunc == NULL,on_queue == false.queue_work()(sys/dev/drm/linux_workqueue.c:120-140): seeson_queue == falseβSTAILQ_INSERT_TAIL(overwrites the NULL link pointers β no immediate list corruption) β marks on_queue. Workerprocess_all_work()(linux_workqueue.c:66-100): dequeues, setsrunning = true, drops worker_lock, then line 89work->func(work)β indirect call through NULL β kernel panic. This is exactly the mechanism DF-4755 documented for the VI twin. -
Host control of the trigger. The PF writes RCV_DW0 and sets RCV_MSG_VALID; the VF cannot block that. The window is short (two register round-trips) but re-opens on every resume (
amdgpu_device_resumere-runsamdgpu_device_ip_late_init, amdgpu_device.c:2936). A host that performs an FLR around VM resume has a realistic shot at the window; an unlucky benign host can hit it too. Consequence: guest kernel panic (availability only; no control over the called pointer β it is deterministically NULL).
Why not executed on the guest (honest boundary)
The audit QEMU guest (DragonFly dfbsd 6.5-DEVELOPMENT #0 ... X86_64_GENERIC)
has:
- no AMD GPU at all β the only display device is QEMU std VGA
(vgapci0@pci0:0:2.0 ... chip=0x1111234, see env.txt);
- no SR-IOV virtual function (it is not an SR-IOV guest);
- no drm/amdgpu compiled into the running kernel
(sysctl kern.conftxt matches zero drm|ath devices).
The code path additionally requires a host/PF to time an FLR notification into the probe window. Running the guest cannot produce this by any local action; classification is therefore untested on this hardware rather than not_reproduced. The defect itself is proven by the static trace above (every hop cited path:line).
Exploit chain
None β the called pointer is NULL by construction (kzalloc), so the outcome is a deterministic panic, not controlled execution. Impact ceiling: host-timed guest DoS at probe/resume. Rated Low, mirroring DF-4755.
Fix validation
fix_status: not_testable β fix.diff (INIT_WORK moved above both
amdgpu_irq_get calls) is authored but cannot be built+exercised on this
guest for the same hardware-absence reason. The change removes the window
by construction: the work is fully initialized before the interrupt source
can deliver anything.
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
not_testablefix.diff moves INIT_WORK before the interrupt-source enable, removing the window by construction; cannot be build-validated on this guest (no AMD hardware, amdgpu not compiled in), hence not_testable.
findings/poc/DF-4975/fix.diff
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/mxgpu_ai.c:351
- sys/dev/drm/amd/amdgpu/mxgpu_ai.c:360
- sys/dev/drm/amd/amdgpu/mxgpu_ai.c:287-310
- sys/dev/drm/amd/amdgpu/mxgpu_ai.c:273-285
- sys/dev/drm/amd/amdgpu/amdgpu_irq.c:511-525
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:282
- sys/dev/drm/amd/amdgpu/amdgpu_device.c:1696
- sys/dev/drm/amd/amdgpu/amdgpu_device.c:2700
- sys/dev/drm/amd/amdgpu/amdgpu_device.c:2936
- sys/dev/drm/amd/amdgpu/amdgpu_kms.c:180
- sys/dev/drm/include/linux/workqueue.h:47-56
- sys/dev/drm/linux_workqueue.c:89
Detail
Evidence (decisive lines)
findings/poc/DF-4975/VERDICT.md (hop-by-hop trace); findings/poc/DF-4975/env.txt (guest hardware absence); decisive refs: mxgpu_ai.c:351 vs mxgpu_ai.c:360; mxgpu_ai.c:287-310; amdgpu_virt.c:282; amdgpu_device.c:1696 vs :2700; amdgpu_kms.c:180; linux_workqueue.c:89
PoC changes
No runnable PoC exists for this class: requires Vega20-class SR-IOV VF plus a PF timing an FLR notification into the probe window. Pack carries the code-order proof instead.
Verified recommended fix
Move INIT_WORK(&adev->virt.flr_work, ...) above both amdgpu_irq_get() calls in xgpu_ai_mailbox_get_irq() (see fix.diff).
Verdict
Real init-order race, proven by full code trace but not executable on the audit guest (no AMD GPU / no SR-IOV VF / amdgpu not compiled in). xgpu_ai_mailbox_get_irq() enables the mailbox RCV interrupt source (mxgpu_ai.c:351 amdgpu_irq_get -> xgpu_ai_set_mailbox_rcv_irq VALID_INT_EN=1) before INIT_WORK(&adev->virt.flr_work) at mxgpu_ai.c:360; a host/PF-timed IDH_FLR_NOTIFICATION in the window hits xgpu_ai_mailbox_rcv_irq:296 schedule_work() on a kzalloc-zeroed work_struct, and the DFly workqueue shim executes work->func(work) with func==NULL (linux_workqueue.c:89) -> kernel panic. RUNTIME caps bit is proven set before late_init (amdgpu_device.c:1696 release_full_gpu sets it via amdgpu_virt.c:282; amdgpu_device_ip_late_init at amdgpu_device.c:2700 runs later), so the schedule_work gate is open. Twin of DF-4755 (mxgpu_vi.c:600-609); mxgpu_ai.c had no prior finding of this class. Window also re-opens on resume (amdgpu_device.c:2936 re-runs late_init).
No comments yet.