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

ig4 PCI attach-error unwinder re-enters the serializer leaked by ig4iic_attach()'s bus_setup_intr error path: ig4_pci.c:195 -> ig4_pci.c:208 -> ig4_iic.c:678 lwkt_serialize_enter on a self-held, explicitly non-recursive serializer β€” boot-time kernel panic (INVARIANTS) / hard self-deadlock (release)

Summary

ig4iic_attach's bus_setup_intr failure path gotos done WITHOUT lwkt_serialize_exit (the exit exists only on success), so the PCI glue's unwinder call into ig4iic_detach - whose first statement is lwkt_serialize_enter on the serializer this same thread holds - hits the documented non-recursive contract: KKASSERT panic on INVARIANTS kernels, atomic_intr_cond_enter self-deadlock on release kernels. Harness with a failing pseudo-bus bus_setup_intr + the REAL attach/detach from stock ig4.ko: 'Unable to setup irq: error 22', serializer HELD, then the lwkt_serialize_enter assertion panic (backtrace through ig4iic_detach); one-line exit-on-error-path fix A/B validated (released, detach returns 0, guest up). Same chain reachable from the ACPI sibling; needs real ig4 hardware whose IRQ setup fails. Availability class.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5601 Β· 0 files
FileTypeDescriptionSize
empty

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Full A/B on the same rig and scenario: baseline (stock ig4.ko) panics in lwkt_serialize_enter <- ig4iic_detach; patched ig4.ko (fix.ig4_iic.diff: one added lwkt_serialize_exit on the error path, module rebuilt from the guest's /usr/src copy) prints 'serializer is released (IS_SERIALIZED: 0)' and 'ig4iic_detach() = 0 -- returned cleanly, NO PANIC (fix effective)'; the guest survives (uptime recorded in fix_run.log) and the module unload path stays clean.

fix.diff; fix_run.log (released/0/NO PANIC/uptime); fix_build.log (patched module builds -Werror clean); run.log (baseline panic)
↓ fix.diffper-fix-DF-5601

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (serial console: production 'Unable to setup irq: error 22' -> 'serializer is HELD (IS_SERIALIZED: 1)' -> panic assertion + backtrace lwkt_serialize_enter/ig4iic_detach/df5601_attach/device_doattach); fix_run.log (patched module: 'serializer is released', 'ig4iic_detach() = 0 -- NO PANIC', uptime shows guest alive); df56f2.c (scenario driver with cited line replays); fix.diff (one-line exit); fix_build.log

PoC changes

No seed PoC existed. Built from scratch. Rig lessons learned (recorded for reproducibility): (1) the scenario must NOT be armed from a sysctl handler -- device_doattach()->device_sysctl_init() self-deadlocks on the sysctl lock held by the handler (first attempt wedged the guest with no console output); arming moved to a second module's MOD_LOAD (kldload context); (2) raw device_add_child on nexus0 faults in nexus_print_child (NULL ivars) -- the pseudo-bus is parented on the ROOT bus (root_print_child is a no-op); (3) a mirror kthread reflecting I2C_EN(0x6c) into ENABLE_STATUS(0x9c) is required so set_controller() succeeds; (4) DFly kthread_create takes (func, arg, tdp, fmt).

Verified recommended fix

Add lwkt_serialize_exit(&sc->slz) on the bus_setup_intr error path in ig4iic_attach (ig4_iic.c:651-655), symmetric with the success path's exit at :658.

Verdict

REPRODUCED as a kernel panic through production code. ig4iic_attach() enters the softc serializer at ig4_iic.c:648, calls bus_setup_intr at :649, and on error does 'goto done' at :651-655 WITHOUT lwkt_serialize_exit (the exit at :658 is only on the success path), returning with the serializer held. ig4iic_pci_attach (ig4_pci.c:193-195) then calls ig4iic_pci_detach to unwind the failed attach; ig4iic_detach (ig4_iic.c:674-678) immediately does lwkt_serialize_enter(&sc->slz) on the serializer this very thread holds -- and sys/serialize.h:41-42 states the serializer is 'not safe from deadlocks nor is it recursive' (kern/lwkt_serialize.c:105-108 leads with ASSERT_NOT_SERIALIZED). On the X86_64_GENERIC guest (options INVARIANTS) this is a boot/module-load-time kernel panic; on kernels without INVARIANTS, atomic_intr_cond_enter self-deadlocks into a hard hang. Same defect reachable via the ACPI sibling (ig4_acpi.c:132-134). Rig: df56.ko provides a pseudo-bus whose bus_setup_intr returns EINVAL (stand-in for rman_activate_resource()/register_int() failures in nexus_setup_intr, nexus.c:498-532); df56f2.ko replays the ig4_pci attach prologue verbatim, runs the REAL ig4iic_attach() from the stock ig4.ko on a fake MMIO register file with a mirror kthread emulating the ENABLE_STATUS latch (so set_controller succeeds and attach reaches its IRQ-setup error path), then calls the REAL ig4iic_detach() -- exactly what ig4_pci.c:195->208 does. Baseline console: 'df56f2ig40: Unable to setup irq: error 22' (production print ig4_iic.c:652), 'DF5601: serializer is HELD ... (IS_SERIALIZED: 1)', then 'panic: assertion "!IS_SERIALIZED((s))" failed in lwkt_serialize_enter at lwkt_serialize.c:107' with backtrace lwkt_serialize_enter <- ig4iic_detach <- df5601_attach <- device_doattach. Fix validated A/B: with the one-line lwkt_serialize_exit added on the error path, the identical scenario prints 'serializer is released (IS_SERIALIZED: 0)' and 'ig4iic_detach() = 0 -- returned cleanly, NO PANIC', guest stays up. Requires real ig4 hardware whose IRQ setup fails at attach; not unprivileged-triggerable -- availability class (panic/hang), severity Low.