DF-4653 — puc_setup_intr publishes ihand before ihandarg: shared-IRQ window dispatches the child handler with a NULL argument (kernel panic) ====================================================================== Finding file: sys/dev/misc/puc/puc.c (pass 2) Decisive lines: 691-692 (publication order), 406-409 (dispatch), 229-238 (early RF_SHAREABLE registration of puc_intr). What the bug is --------------- `puc_pci_attach()` allocates its IRQ `RF_ACTIVE | RF_SHAREABLE` (puc.c:229-230) and registers `puc_intr` immediately (puc.c:236-238, serializer NULL). From that instant, `puc_intr` is dispatched on ANY event on that line — including events raised by *sibling* devices sharing the IRQ, and including the whole child-attach phase that follows. Each child (sio) registers its handler during `device_probe_and_attach()` (puc.c:351) via `bus_setup_intr(..., INTR_MPSAFE, siointr, com, ...)` (sys/dev/serial/sio/sio.c:1236), which lands in `puc_setup_intr`. That function publishes the two fields in the wrong order: puc.c:691 sc->sc_ports[i].ihand = ihand; /* gate FIRST */ puc.c:692 sc->sc_ports[i].ihandarg = arg; /* argument SECOND */ while `puc_intr` gates on exactly that publication: puc.c:407 if (sc->sc_ports[i].ihand != NULL && ...) puc.c:409 (sc->sc_ports[i].ihand)(sc->sc_ports[i].ihandarg); An interrupt dispatched on another CPU between the two stores (handlers are INTR_MPSAFE and unsynchronized — puc itself passes NULL serializer and KKASSERTs child serializers are NULL at puc.c:693) observes `ihand == siointr` with the still-NULL `ihandarg` (softc is bzero'd at puc.c:214) and executes `siointr(NULL)`, which immediately dereferences its argument: sio.c:1750-1755 `siointr(arg) -> siointr1((struct com_s *)arg) -> com->...` (first deref at sio.c:1797+). Result: kernel NULL-deref panic during boot. The plain stores also carry no compiler barriers, so the compiler may sink or swap them. (The teardown side at puc.c:711-712 has the correct store order — gate cleared first — but is equally barrier-less; it is dead in practice because no child driver in the tree implements detach.) Model / evidence ---------------- trigger.c reproduces the interleaving as a userspace model of the exact kernel store/load pattern (thread A = CPU0 in puc_setup_intr, thread B = CPU1 in puc_intr on a shared-IRQ event): phase A — kernel store order, faithful back-to-back stores phase B — kernel store order, window widened by one sched_yield() (instrumentation; isolates the ORDER as the defect) phase C — fixed store order (argument first, gate last), same widening B > 0 while C == 0 proves the publication order is the defect; A > 0 shows it is observable even at faithful width. run.sh builds and runs the model on the audit guest. The KERNEL manifestation (siointr(NULL) panic) is hardware-gated: reproduction needs a puc-supported multiport serial card whose sibling/shared IRQ activity (or the card itself) fires in the window during sio_attach — no such card exists on the audit guest (run.log), so the kernel verdict is not_reproduced with the ordering defect proven from source + model. Fix --- fix.diff publishes ihandarg first and ihand last (with compiler barriers), and snapshots the pair symmetrically in puc_intr / puc_teardown_intr.