DragonFly port removed upstream's in_interrupt() guard from amdgpu_virt_kiq_rreg/wreg: unconditional console kprintf per SR-IOV MMIO access and msleep() from hardirq context on KIQ fence timeout
Summary
The port #if 0'd the interrupt-context half of the timeout guard (leaving a kprintf TODO) while this port - unlike Linux - runs IH processing in hardirq: every KIQ-mediated MMIO access (including per-interrupt from cz_ih_set_rptr) prints a console line, and a stalled KIQ ring takes 5ms busy-poll + msleep x80 FROM HARDIRQ - illegal sleep that panics INVARIANTS and corrupts the scheduler otherwise. DF-port-specific (upstream bails before msleep). Guest DoS on SR-IOV VF; hw-gated. Restore-the-guard-with-gd_intr_nesting_level fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4915 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 5.7 KB | β raw | |
| trigger.md | β | 1.8 KB | β raw | |
| env.txt | β | 178 B | view raw | |
| verdict.json | β | 3.4 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw |
DF-4915 β amdgpu_virt_kiq_rreg/wreg: DF port deleted upstream's in_interrupt() guard β msleep() from hardirq + per-access console kprintf
Build / Run / Expected
There is no buildable PoC on this guest: the trigger requires an AMD GPU
running as an SR-IOV VF (guest assigned a virtual function of a Tonga/
Fiji/Vega-class device). The audit QEMU guest has no AMD PCI device at all
(only i440fx + virtio + bochs VGA β see env.txt), so amdgpu never
instantiates an amdgpu_device and the code path is unreachable at runtime.
To reproduce on hardware that has the device:
- On a host with an SR-IOV-capable AMD GPU (Tonga/Fiji/Polaris/Vega10),
enable VFs (
echo N > /sys/class/drm/card0/device/sriov_numvfson Linux with the GIM/amdgpuPF driver, orvgt/SR-IOV via the vendor stack). - Pass one VF to a DragonFly guest (
pptdevs=0:x:y.z), boot the guest,kldload amdgpu. - Observe (no fault injection needed): every MMIO access routed through the
KIQ under
AMDGPU_SRIOV_CAPS_RUNTIMEprintsamdgpu_virt_kiq_rreg: implement in_interrupt() function(or the_wregtwin) to the console β including from the GPU hard-IRQ handler on CZ-class parts wherecz_ih_set_rptr()does an unconditionalWREG32(mmIH_RB_RPTR)per interrupt. This is the guaranteed-by-construction console flood. - Fault injection (panic leg): stall the KIQ ring (host-side VF FLR, or GPU
hang) so
amdgpu_fence_wait_polling()never sees the fence. The next GPU interrupt on a CZ VF (or a Tonga VF with the IH RB_OVERFLOW bit set, which takesRREG32(mmIH_RB_CNTL)intonga_ih_get_wptr) entersamdgpu_virt_kiq_wregfrom hardirq context, times out after 5 ms of busy-wait inside the hardirq, then executesmsleep(5)from hardirq context β illegal sleep in interrupt context (scheduler corruption /panic: ...on DEBUG kernels), retried up to 80Γ.
Success criterion: step 3 alone (console line per KIQ access) proves the divergence; step 4 proves the illegal-sleep panic class.
Files in this pack
VERDICT.mdβ full narrative, path:line evidence, upstream diff.trigger.mdβ the trigger procedure above, machine-copyable.env.txtβ guest hardware inventory proving absence of AMD GPU.manifest.json,verdict.jsonβ machine records.
DF-4915 β VERDICT
Status: not_reproduced (hardware-gated β no AMD GPU / SR-IOV VF in the audit guest); the code divergence itself is certain and proven by source + upstream comparison.
What the finding is
sys/dev/drm/amd/amdgpu/amdgpu_virt.c ports Linux v4.19's
amdgpu_virt_kiq_rreg() / amdgpu_virt_kiq_wreg(), but the DragonFly port
#if 0'd out upstream's interrupt-context guard:
amdgpu_virt.c:162-168(rreg) andamdgpu_virt.c:216-222(wreg):
#if 0
if (r < 1 && (adev->in_gpu_reset || in_interrupt()))
goto failed_kiq_read;
if (in_interrupt())
might_sleep();
#endif
kprintf("amdgpu_virt_kiq_rreg: implement in_interrupt() function\n");
if (r < 1 && (adev->in_gpu_reset))
goto failed_kiq_read;
while (r < 1 && cnt++ < MAX_KIQ_REG_TRY) {
msleep(MAX_KIQ_REG_BAILOUT_INTERVAL);
...
Upstream v4.19 (torvalds/linux) in the same spot:
if (r < 1 && (adev->in_gpu_reset || in_interrupt()))
goto failed_kiq_read;
...
while (r < 1 && cnt++ < MAX_KIQ_REG_TRY) {
msleep(MAX_KIQ_REG_BAILOUT_INTERVAL);
i.e. upstream never reaches msleep() from interrupt context (it bails to
failed_kiq_read/write first, as the retained comment at
amdgpu_virt.c:154-161 explicitly documents: "also don't wait anymore for IRQ
context"). The DF port dropped that half of the condition, left the comment,
and printed a TODO instead.
Why the guard matters on this port β the hardirq path is real
- GPU interrupts are serviced in hardirq context on this port:
amdgpu_irq_handler()(sys/dev/drm/amd/amdgpu/amdgpu_irq.c:189-197) callsamdgpu_ih_process()directly β no threaded-IRQ/taskqueue deferral. amdgpu_ih_process()finishes withamdgpu_ih_set_rptr()(sys/dev/drm/amd/amdgpu/amdgpu_ih.c:159), and on Carrizo VI partscz_ih_set_rptr()is an unconditionalWREG32(mmIH_RB_RPTR, ...)(sys/dev/drm/amd/amdgpu/cz_ih.c:276-279). On Tonga the doorbell path avoids it, but the IH ring-overflow branch oftonga_ih_get_wptr()doesRREG32(mmIH_RB_CNTL)+WREG32(tonga_ih.c:213-216) β and theRB_OVERFLOWbit arrives in the GPU-written wptr writeback.WREG32/RREG32expand toamdgpu_mm_wreg/amdgpu_mm_rreg(amdgpu.h:1099-1102), andamdgpu_mm_rreg()dispatches toamdgpu_virt_kiq_rreg()wheneveramdgpu_sriov_runtime(adev)is set and the accessor is notAMDGPU_REGS_NO_KIQ(sys/dev/drm/amd/amdgpu/amdgpu_device.c:131-132) β i.e. for every plain register access in a running SR-IOV VF guest.
Consequences, in order of certainty:
- Certain, no fault needed: the
kprintfatamdgpu_virt.c:169/:223fires on every KIQ-mediated register access in every SR-IOV VF guest, including from hardirq context at interrupt rate. Console/log flood + serialization of every MMIO access. - Certain from source, needs a stalled KIQ: the first
amdgpu_fence_wait_polling(..., MAX_KIQ_REG_WAIT)(amdgpu_virt.c:152,MAX_KIQ_REG_WAIT = 5000Β΅s,amdgpu.h:239) busy-polls up to 5 ms inside the hardirq; if the fence still hasn't completed and the device is not inin_gpu_reset, the port falls intomsleep(5)(amdgpu_virt.c:175,amdgpu_virt.c:229) β an illegal sleep from hardirq context (msleepβtsleep,sys/dev/drm/include/linux/delay.h:33-39), repeated up toMAX_KIQ_REG_TRY = 80times (amdgpu.h:241). Sleeping from a DF hardirq corrupts the LWKT scheduler state (context switch away from an interrupt frame); on INVARIANTS kernels this is a panic, on others it is undefined scheduler behavior.
Why not reproduced on the audit guest
The audit QEMU guest has no AMD PCI device (env.txt: zero vendor-0x1002
functions; VGA is bochs 0x1234, NIC is virtio). amdgpu.ko exists but no
amdgpu_device is ever instantiated, adev->virt.caps never gains
AMDGPU_SRIOV_CAPS_RUNTIME, and amdgpu_virt_kiq_rreg/wreg are never
invoked. The trigger is therefore hardware-gated; per the audit's honest
classification this maps to not_reproduced with impact none on this
guest, while the code-level divergence is certain by direct comparison
with upstream v4.19 (fetched and archived in this pack's analysis, quoted
verbatim above).
Exploit chain
None β denial-of-service class only (hardirq stall β₯5 ms per access, console flood, illegal-sleep panic on stalled KIQ). No memory-safety primitive.
Recommended fix
Restore upstream's interrupt-context semantics using DragonFly's
mycpu->gd_intr_nesting_level test (the port already uses this idiom
elsewhere), and delete the unconditional kprintf:
--- a/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
@@ -159,11 +159,10 @@ uint32_t amdgpu_virt_kiq_rreg(struct amdgpu_device *adev, uint32_t reg)
* also don't wait anymore for IRQ context
* */
-#if 0
- if (r < 1 && (adev->in_gpu_reset || in_interrupt()))
+#define in_interrupt() (mycpu->gd_intr_nesting_level != 0)
+ if (r < 1 && (adev->in_gpu_reset || in_interrupt()))
goto failed_kiq_read;
-
- if (in_interrupt())
- might_sleep();
-#endif
- kprintf("amdgpu_virt_kiq_rreg: implement in_interrupt() function\n");
+#undef in_interrupt
if (r < 1 && (adev->in_gpu_reset))
goto failed_kiq_read;
(the same change in amdgpu_virt_kiq_wreg). If a transition marker is
wanted, replace the kprintf with a one-shot bootverbose-gated note.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
DF-4915 β trigger procedure (SR-IOV VF AMD GPU required)
Prerequisite hardware: AMD GPU with SR-IOV (Tonga/Fiji/Polaris/Vega10) on the
host, one VF passed through to a DragonFly 6.x guest (e.g. pptdevs=5:0.0 in
/boot/loader.conf), amdgpu.ko loadable in the guest.
Leg-1 β guaranteed console flood (no fault injection)
kldload amdgpu # in the VF guest dmesg | tail # after init completes, full-access is released # any runtime driver activity that touches MMIO now routes via KIQ: tail -f /var/log/messages
Expected: amdgpu_virt_kiq_rreg: implement in_interrupt() function and/or the
_wreg twin printed for every KIQ-mediated access β including bursts aligned
with GPU interrupts (CZ-class parts: one line per interrupt via
cz_ih_set_rptr's unconditional WREG32(mmIH_RB_RPTR)).
Leg-2 β illegal msleep() from hardirq (needs stalled KIQ)
- Host: trigger a VF function-level reset while the guest driver is at
runtime (
AMDGPU_SRIOV_CAPS_RUNTIMEset), or otherwise stall the KIQ ring soamdgpu_fence_wait_polling()times out. - Guest: generate GPU interrupts (any GEM workload; on Tonga an IH ring
overflow also works β the
RB_OVERFLOWpath intonga_ih_get_wptrdoes a plainRREG32(mmIH_RB_CNTL)from the hardirq). - Expected: guest hardirq spins 5 ms in
amdgpu_fence_wait_polling, then entersmsleep(5)from interrupt context (amdgpu_virt.c:175/:229) β INVARIANTS kernels panic; others corrupt scheduler state / wedge the CPU in the interrupt path for up to ~80 x (5 ms poll + 5 ms sleep).
Why it cannot run in the audit guest
pciconf -l shows zero vendor-0x1002 functions (see env.txt); amdgpu.ko
is present but no device binds, so amdgpu_virt_kiq_rreg/wreg are never
called. Classification on this guest: not_reproduced (hardware absent).
Fix verification
not_testableFix could not be validated: no SR-IOV-capable AMD GPU in the audit guest, so neither baseline nor patched behavior is reachable; the proposed diff restores upstream v4.19 semantics verbatim.
VERDICT.md 'Recommended fix' section contains the git-apply-able diff against amdgpu_virt.c.
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:162
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:169
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:175
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:216
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:223
- sys/dev/drm/amd/amdgpu/amdgpu_virt.c:229
- sys/dev/drm/amd/amdgpu/amdgpu_irq.c:195
- sys/dev/drm/amd/amdgpu/amdgpu_ih.c:159
- sys/dev/drm/amd/amdgpu/cz_ih.c:276
- sys/dev/drm/amd/amdgpu/tonga_ih.c:213
- sys/dev/drm/amd/amdgpu/amdgpu_device.c:131
- sys/dev/drm/amd/amdgpu/amdgpu.h:239
- sys/dev/drm/include/linux/delay.h:33
Detail
Evidence (decisive lines)
VERDICT.md (upstream-vs-DF diff of the guard, hardirq path chain with path:line); trigger.md (2-leg trigger for SR-IOV hardware); env.txt (pciconf: zero vendor-0x1002 devices on guest)
PoC changes
No runnable PoC exists for this guest (hardware-gated); pack documents the exact hardware trigger procedure and the source-level proof instead of a C reproducer.
Verified recommended fix
Restore the interrupt-context early-out using mycpu->gd_intr_nesting_level != 0 in both amdgpu_virt_kiq_rreg and amdgpu_virt_kiq_wreg and drop the unconditional kprintf (see VERDICT.md diff).
Verdict
The DragonFly port of amdgpu_virt.c #if 0'd out upstream's in_interrupt() guard in amdgpu_virt_kiq_rreg/wreg (amdgpu_virt.c:162-168, 216-222) while this port runs GPU IH processing directly in hardirq (amdgpu_irq.c:189-197 -> amdgpu_ih.c:159; cz_ih.c:276-279 does an unconditional WREG32 per interrupt; tonga_ih.c:213-216 on IH overflow). Result on a running SR-IOV VF guest: an unconditional kprintf per KIQ-mediated MMIO access (console flood, incl. hardirq context) and, when the KIQ fence times out, msleep(5) from hardirq context (amdgpu_virt.c:175/229) x80 iterations - an illegal sleep from interrupt context that panics/corrupts the scheduler. Not reproduced on the audit QEMU guest because no AMD PCI device exists (env.txt), so amdgpu never binds and the functions are unreachable; the code-level divergence vs upstream v4.19 is certain (verbatim comparison archived in VERDICT.md).
No comments yet.