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

als_intr registered before channels exist (and kept after they are freed): IRQ in the attach/detach window runs chn_intr(NULL)/chn_intr(freed) β€” NULL-deref/UAF panic; als_resource_free releases the I/O region before tearing down the handler

Summary

The IRQ is registered and card interrupts enabled BEFORE pcm_addchan creates the channels (ch->channel NULL in the window) and the handler has no NULL guard - chn_intr immediately evaluates CHN_LOCKOWNED(NULL): panic. The RF_SHAREABLE allocation means a sibling device's interrupt on the shared line enters the handler with no not-mine early-out during the whole window. Teardown mirrors it: pcm_unregister frees the channels while the handler remains, and the I/O region is released before teardown_intr. Hook-after-addchan + teardown-before-release fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4551 Β· 5 files
FileTypeDescriptionSize
als_intr_window.c β€” 1.7 KB view raw
guest_hw_evidence.txt β€” 480 B view raw
fix.diff β€” 1.7 KB view raw
VERDICT.md β€” 3.5 KB ↓ raw
README.md β€” 1.1 KB ↓ raw

DF-4551 β€” als_intr() runs before channel objects exist (and after they are freed): chn_intr(NULL) NULL-deref panic + teardown ordering

File: sys/dev/sound/pci/als4000.c lines 783, 648-658, 852-853, 861-863, 872, 741-748.

Build / Run

Documentation/analysis PoC β€” the trigger is hardware behavior (an interrupt arriving in the attach or detach window), not a program:

cc -Wall -c als_intr_window.c    # compile check only

On real hardware the reproducer is: install an ALS4000 sharing its IRQ line with any active device (IRQ is requested RF_SHAREABLE, als4000.c:777), then kldload snd_als4000 / use the card and kldunload snd_als4000 repeatedly while the other device generates interrupts. An IRQ landing between snd_setup_intr (als4000.c:783) and pcm_addchan (:852) executes chn_intr(sc->pch.channel == NULL) -> CHN_LOCKOWNED(c) on NULL (channel.c:683-694) -> panic.

Guest result

Not reproducible: no ALS4000 PCI device in the QEMU guest (guest_hw_evidence.txt) β€” snd_als4000.ko loads but never attaches, so als_intr is never registered or called.

VERDICT.md
↓ download raw

VERDICT β€” DF-4551

Status: not_reproduced (hardware-gated). Impact: none on this guest (class: local kernel NULL-deref/UAF panic on real hardware β€” availability only, no control over data; needs the physical card).

Root cause (path:line)

Attach window β€” als_pci_attach (als4000.c:808-864) β†’ als_resource_grab (als4000.c:764-806):

  1. sc->irq allocated RF_SHAREABLE (als4000.c:776-777) and snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr, sc, &sc->ih) runs at als4000.c:783-787 β€” the handler is live from here on.
  2. als_init enables card interrupts: als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000) (als4000.c:703-704), and mixer_init immediately pokes the mixer for every device (mixer.c:713-726 loop calls into alsmix_set β†’ als_mix_wr) β€” plausibly raising IRQ status bits.
  3. The channels the handler dispatches to do not exist yet: sc->pch / sc->rch are zeroed softc members; ch->channel is only set by alschan_init during pcm_addchan (als4000.c:852-853, via als4000.c:204-232).

als_intr (als4000.c:640-677) has no ownership filter and no NULL guard:

intr = als_intr_rd(sc);                      // :646
if (intr & 0x80) { ... chn_intr(sc->pch.channel); }   // :648-651  NULL here
if (intr & 0x40) { ... chn_intr(sc->rch.channel); }   // :654-657  NULL here

chn_intr(c) (sys/dev/sound/pcm/channel.c:683-694) immediately evaluates CHN_LOCKOWNED(c) β†’ dereferences the NULL pcm_channel β†’ panic. Because the IRQ is shared, another device's interrupt also enters als_intr (no "not mine" early-out β€” als_intr_rd result bits 0x80/0x40 gate only the channel dispatch, and the handler always ACKs).

Failure paths widen the window: if mixer_init or pcm_register fails, bad_attach β†’ als_resource_free runs late relative to the live handler; during suspend als_pci_suspend calls als_playback_stop/als_capture_stop and als_uninit under the lock while IRQs are already enabled.

Teardown windows β€” als_pci_detach (als4000.c:866-881):

  • pcm_unregister(dev) (:872) destroys/frees the pcm_channel objects; als_uninit (:877) disables IRQ generation afterwards, and bus_teardown_intr only happens inside als_resource_free (:745-748) β€” an in-flight or shared-line IRQ in that gap dispatches chn_intr on freed channel objects (UAF).
  • als_resource_free releases the I/O region (:741-744) before tearing down the interrupt (:745-748); a racing handler then performs bus_space_read_1/write_1 (als4000.c:121-145) on a released mapping / reallocated port range, and ACKs (als_intr_wr) to ports rman no longer owns.

Why not reproduced

No ALS4000 PCI device exists in the QEMU guest (ALS_PCI_ID0 0x40004005 β€” guest_hw_evidence.txt); snd_als4000.ko loads but als_pci_attach and therefore snd_setup_intr never run. There is no QEMU device model for the ALS4000, so the interrupt cannot be synthesized. Confidence likely (the NULL-window is provable from ordering; the practical trigger needs the card and a shared/raised IRQ).

Exploit chain

None beyond panic/DoS (hardware availability race). Not memory corruption under attacker control.

Fix validation

not_testable (no hardware).

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

not_testable: no ALS4000 hardware in the guest; fix.diff is line-accurate against als4000.c:764-863.

fix.diff
↓ fix.diffper-fix-DF-4551

Confirmed kernel references

Detail

Exploit chain

real hw: card shares IRQ with busy device -> kldload/kldunload snd_als4000 during interrupts -> NULL-deref panic (attach window) or handler over freed pcm_channel objects (detach window)

Evidence (decisive lines)

VERDICT.md (window-by-window ordering analysis with channel.c:683-6994 NULL path); guest_hw_evidence.txt (no vendor 4005 PCI device; module load produces no attach); fix.diff (register IRQ after pcm_addchan + teardown_intr before I/O release)

PoC changes

No seed existed; authored analysis artifact als_intr_window.c (no runnable trigger exists without the card).

Verified recommended fix

Move snd_setup_intr after pcm_addchan and reorder als_resource_free to teardown_intr before releasing the I/O region (see fix.diff).

Verdict

Hardware-gated lifetime/ordering defect, proven from source ordering: snd_setup_intr(als_intr) runs in als_resource_grab (als4000.c:783) and als_init enables card IRQs (:703-704) before pcm_addchan creates the channels (:852-853), so any IRQ in the window dispatches chn_intr(sc->pch.channel == NULL) -> immediate NULL deref in CHN_LOCKOWNED (channel.c:683-694) -> panic; the IRQ is RF_SHAREABLE (:777) and the handler has no ownership filter. Detach keeps the handler past pcm_unregister's channel destruction (:872) and als_resource_free releases the I/O region (:742) before bus_teardown_intr (:745), so a racing handler runs over freed channels and released bus_space. Not reproducible in QEMU: no ALS4000 PCI device exists (guest_hw_evidence.txt); driver never attaches, handler never registers. Availability-only impact (panic/DoS) on real hardware.