β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4706

amr_pci_attach registers amr_pci_intr before the state it consumes exists: a shared-IRQ-line interrupt in the attach window dispatches through the still-NULL sc->amr_get_work -> boot/kldload panic (setup-side mirror of DF-1820)

Summary

bus_setup_intr connects the handler before amr_attach assigns the dispatch table, init locks, or DMA; the RF_SHAREABLE handler has no status check and no INTEN gate - it unconditionally calls amr_done whose first action is the indirect call through the NULL amr_get_work. Any co-line device interrupt in the millisecond window jumps to 0. Hardware-gated. Move-setup_intr-after-attach + INTEN-gate fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4706 Β· 4 files
FileTypeDescriptionSize
README.md β€” 2.4 KB ↓ raw
VERDICT.md β€” 2.8 KB ↓ raw
fix.diff β€” 1.9 KB view raw
verdict.json β€” 2.5 KB view raw

DF-4706 β€” amr_pci_attach registers amr_pci_intr before the state it

consumes exists β†’ NULL function-pointer call from a shared-line IRQ

What

sys/dev/raid/amr/amr_pci.c:263-268 β€” bus_setup_intr(..., amr_pci_intr, sc, ...) is called before everything the handler consumes exists:

  • sc->amr_get_work is assigned only inside amr_attach() (amr.c:206-216), which runs at amr_pci.c:346;
  • the softc locks are lockinit'd at amr_pci.c:324-325;
  • the mailbox/sg/ccb DMA blocks are allocated at amr_pci.c:326/334/338.

amr_pci_intr (amr_pci.c:461-470) does no interrupt-status check and no state gate β€” it unconditionally calls amr_done(sc), whose first action is the indirect call sc->amr_get_work(sc, &mbox) (amr.c:1782). The IRQ was allocated RF_SHAREABLE (amr_pci.c:257-258), so the handler is invoked for any device sharing the line, not just the AMR card.

Consequence

Any interrupt on the shared IRQ line during the attach window (bus_setup_intr … amr_attach's pointer assignment) β€” e.g. a NIC on the same legacy INTA line, or the card itself left asserting by firmware/previous driver instance β€” executes amr_pci_intr β†’ amr_done β†’ indirect call through the NULL sc->amr_get_work β†’ jump to address 0 β†’ kernel panic at boot or kldload amr.

Distinct from known DF-1820 (teardown ordering: amr_pci_free tears the interrupt down LAST while freeing DMA) β€” this is the setup side of the same missing interrupt-lifecycle discipline: registered too early, gated by nothing.

Reachability / gating

Requires LSI MegaRAID/amr PCI hardware (probe must match amr_device_ids[]). The trigger is probabilistic (an unrelated device's interrupt must land in a millisecond-scale window), or deterministic with an amr card that asserts IRQ before INTEN. QEMU does not emulate LSI MegaRAID β€” verified absent on the audit guest (no 0x101e/0x1000:1960 PCI device, /dev/amr* absent), so this is hw-gated inconclusive.

Conceptual trigger (needs hardware)

# device sharing the IRQ line with the amr card generates traffic;
# repeat kldload in a loop to widen the window:
while :; do kldload amr; kldunload amr; done
# expected on hit: panic "Fatal trap 12/... calling 0x0" from
#   amr_pci_intr -> amr_done -> sc->amr_get_work

Fix

Register the interrupt AFTER amr_attach() succeeds (and/or gate the handler on AMR_STATE_INTEN), see fix.diff.

VERDICT.md
↓ download raw

VERDICT β€” DF-4706

status: inconclusive (hardware-gated; QEMU does not emulate LSI MegaRAID) Β· reproduced: 0 Β· analysis confidence: likely

Was it reproduced?

No β€” and it cannot be on this guest: pciconf -l shows no 0x101e/0x1000:1960 RAID controller and /dev/amr* does not exist, so amr_pci_attach never runs (verified this run, see ../DF-4705/guest_session.log). kldload amr on the guest loads nothing with a matching device. The defect is established by code trace:

  1. amr_pci_attach bzeros the softc (amr_pci.c:195) β€” every member NULL, including the amr_get_work function pointer (amrvar.h:254).
  2. IRQ is allocated RF_SHAREABLE|RF_ACTIVE (amr_pci.c:257-258) and the handler connected (amr_pci.c:263-265) β€” from this instant the ISR is live for any device asserting the shared line.
  3. amr_pci_intr (amr_pci.c:461-470) has no status check / no AMR_STATE_INTEN gate; it calls amr_done(sc) unconditionally.
  4. amr_done (amr.c:1782) immediately dispatches through sc->amr_get_work β€” still NULL until amr_attach assigns it (amr.c:206-216, reached only at amr_pci.c:346).
  5. β†’ indirect call to address 0 β†’ fatal trap, boot-time or kldload-time panic.

Window: the three bus_dma_tag_creates + lockinits + three DMA alloc/load rounds between amr_pci.c:278-340 β€” milliseconds, extended under DMA32 memory pressure. The interrupt need not be the amr card's own (RF_SHAREABLE) β€” any co-line device (NIC, USB controller) firing in the window panics the machine.

Not a re-report of DF-1820: DF-1820 is the teardown half (amr_pci_free ordering, intr torn down LAST while DMA is freed); this finding is the setup half (intr registered FIRST, before the dispatch table it calls exists). Different lines, different fix.

Also note the same unguarded handler is why DF-1820's teardown window is exploitable β€” a complete fix gates amr_pci_intr on driver state and fixes BOTH orderings; fix.diff here moves setup_intr after amr_attach() and adds the AMR_STATE_INTEN gate.

Exploit chain

None beyond DoS (NULL function-pointer call β†’ panic). The called address is fixed (0), not attacker-chosen; no control over registers at the call site beyond sc.

Fix validation

Impossible on this guest (no amr hardware β†’ neither baseline nor patched kernel can exercise the path). fix_status: not_testable.

Guest state

Guest untouched; stayed up the whole run.

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_testable
baseline no→ patch + rebuild →patched clean

fix validation impossible on this guest: amr_pci_attach never runs without MegaRAID hardware (absent); diff authored from the line-accurate ordering trace

fix.diff (setup_intr relocation + AMR_STATE_INTEN gate)
↓ fix.diffper-fix-DF-4706

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md 5-step trace with amr_pci.c:195,257-268,324-346 and amr.c:1782,206-216; guest gate evidence in ../DF-4705/guest_session.log (no amr PCI device, /dev/amr* absent)

PoC changes

no seed; conceptual trigger in README (kldload loop) β€” requires hardware absent from the guest

Verified recommended fix

move bus_alloc_resource_any(SYS_RES_IRQ)+bus_setup_intr after amr_attach() succeeds and gate amr_pci_intr on AMR_STATE_INTEN (see fix.diff)

Verdict

amr_pci_attach (amr_pci.c:263-268) connects amr_pci_intr before anything the handler consumes exists: sc->amr_get_work is assigned only inside amr_attach (amr.c:206-216) reached at amr_pci.c:346, locks are lockinit'd at amr_pci.c:324-325, mailbox/sg/ccb DMA at 326-339. The IRQ is RF_SHAREABLE (amr_pci.c:257-258) and amr_pci_intr (461-470) has no status check and no AMR_STATE_INTEN gate, calling amr_done(sc) whose first action is the indirect call sc->amr_get_work(sc,&mbox) (amr.c:1782) β€” any co-line device interrupt in the attach window jumps to NULL: boot/kldload panic. Setup-side mirror of known DF-1820 (teardown-side); not a re-report. Cannot be exercised on the audit guest: no LSI MegaRAID PCI device exists in QEMU (verified: no 0x101e/0x1000:1960 device, /dev/amr* absent).