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

Serializer leak on bus_setup_intr failure in ig4iic_attach causes self-deadlock during cleanup

Field Value
ID DF-1049
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE CWE-667 Improper Locking
File sys/bus/smbus/ichiic/ig4_iic.c
Lines 648-671 (attach, esp. 651-654), 674-678 (detach enter)
Area bus/smbus/ichiic (Intel LPSS Designware I2C / ig4(4))
Confidence certain
Discovered 2026-07-14
Reported pending
Known CVE none
CVE match dfly_specific

Summary

In ig4iic_attach, lwkt_serialize_enter(&sc->slz) is called at ig4_iic.c:648 before bus_setup_intr. If bus_setup_intr fails (line 651), the code prints a message and executes goto done (line 654) which returns error without calling lwkt_serialize_exit. The caller (ig4iic_pci_attach at ig4_pci.c:193-195 or ig4iic_acpi_attach at ig4_acpi.c:132-134) then invokes ig4iic_pci_detach β†’ ig4iic_detach, which immediately calls lwkt_serialize_enter(&sc->slz) again (ig4_iic.c:678). The DragonFly lwkt serializer is explicitly non-recursive (serialize.h:41-42, lwkt_serialize.c:36-37), so this self-reentry deadlocks permanently: in DEBUG kernels ASSERT_NOT_SERIALIZED (lwkt_serialize.c:107) panics; in production kernels atomic_intr_cond_enter blocks the thread waiting for a lock it itself owns.

Root cause

ig4_iic.c:648 acquires sc->slz. ig4_iic.c:651-654 checks bus_setup_intr return and on failure does goto done (line 654). The done: label at line 669 is just return error; β€” there is no lwkt_serialize_exit on this path. Contrast with the bus_generic_attach error path at lines 660-664 which correctly calls lwkt_serialize_exit at line 658 before its goto done.

After attach returns error, ig4_pci.c:193-195 unconditionally calls ig4iic_pci_detach(dev); ig4iic_pci_detach calls ig4iic_detach (ig4_iic.c:674) which at line 678 does lwkt_serialize_enter(&sc->slz) on the already-held, non-recursive serializer.

serialize.h:41-42 states: "Unlike tokens this serialization is not safe from deadlocks nor is it recursive." lwkt_serialize_enter (lwkt_serialize.c:105-113) calls atomic_intr_cond_enter which, finding the interlock already held, invokes lwkt_serialize_sleep to block β€” but the holder is curthread, so it sleeps forever.

/* ig4_iic.c:648-655 β€” the bug */
lwkt_serialize_enter(&sc->slz);
error = bus_setup_intr(sc->dev, sc->intr_res, INTR_MPSAFE,
                       ig4iic_intr, sc, &sc->intr_handle, &sc->slz);
if (error) {
    device_printf(sc->dev, "Unable to setup irq: error %d\n", error);
    goto done;                    /* !!! serializer still held */
}

Threat model & preconditions

  • Attacker position: Local root (kldload/kldunload privilege, or the condition triggers at boot if the I2C controller IRQ setup fails).
  • Privileges gained or impact: The thread performing the driver attach/detach hangs permanently, and because it holds (or is waiting on) the serializer, any subsequent access to the I2C controller also blocks. On a system where this controller is used for critical devices (touchpad, sensors, ACPI power management via smbacpi child at ig4_iic.c:607), this can hang boot or render the system unresponsive.
  • Required config or capabilities: Default kernel with ig4_iic configured. Root to force the trigger.
  • Reachability: Requires bus_setup_intr to fail, which occurs under IRQ vector exhaustion, broken MSI/MSI-X allocation, or ACPI IRQ resource misconfiguration. The /dev/smb* node is 0600 root-only (smb.c:135-141), so the entire user-facing surface requires root; this finding is about kernel stability/hardening rather than privilege escalation.

Proof of concept

This is a reliability/hang bug, not a memory-corruption primitive, so there is no exploit chain to escalate. Reproduction: as root on a system with an Intel LPSS I2C controller (ig4iic), force bus_setup_intr to fail and observe the permanent hang.

The simplest deterministic trigger is a custom kernel module that wraps bus_setup_intr to return an error (fault injection), or resource exhaustion by loading many interrupt-consuming drivers until IRQ allocation fails for ig4iic.

Practical script:

  1. kldload many MSI-consuming drivers to exhaust vectors.
  2. kldload ig4iic β€” if bus_setup_intr fails, the kldload syscall hangs in D-state forever (uninterruptible sleep in lwkt_serialize_sleep).
  3. Verify with ps axl | grep ig4iic showing a permanent D state thread, and procstat -kk <pid> showing lwkt_serialize_sleep β†’ atomic_intr_cond_enter.

On a DEBUG kernel (INVARIANTS), the system panics immediately at the ASSERT_NOT_SERIALIZED KKASSERT in lwkt_serialize_enter instead.

Build & run

# Static verification fallback (no IRQ exhaustion needed):
# 1. Confirm lwkt_serialize_enter at ig4_iic.c:648 has no matching exit on the
#    bus_setup_intr error path (lines 651-654).
# 2. Confirm ig4iic_detach at ig4_iic.c:678 unconditionally re-enters the
#    serializer.
# 3. Confirm serialize.h:41-42 documents the serializer as non-recursive.

Expected output

Production kernel:

ig4iic0: <Intel LPSS I2C Controller> at pci0:15:0: class=...
ig4iic0: Unable to setup irq: error 6
[system hangs β€” kldload never returns; ps shows thread in D-state]

DEBUG / INVARIANTS kernel:

ig4iic0: <Intel LPSS I2C Controller> at pci0:15:0: class=...
ig4iic0: Unable to setup irq: error 6
panic: assert seralizer not held
cpuid = 0
Trace:
lwkt_serialize_enter() at lwkt_serialize.c:107
ig4iic_detach() at ig4_iic.c:678
ig4iic_pci_detach() at ig4_pci.c:...
device_detach() at subr_bus.c:...
...

No uid=0 or memory-write primitive is gained; impact is purely denial of service.

Impact

Local DoS via permanent thread self-deadlock when bus_setup_intr fails. High-privilege prerequisite (root to force IRQ exhaustion, or a hardware/firmware misconfiguration at boot) and high attack complexity (race against normal IRQ allocation) keep this at Low severity. The finding is filed as kernel stability / hardening.

Add lwkt_serialize_exit(&sc->slz) before the goto done in the bus_setup_intr error path, matching the pattern already used by the bus_generic_attach error path at lines 658-664.

--- a/sys/bus/smbus/ichiic/ig4_iic.c
+++ b/sys/bus/smbus/ichiic/ig4_iic.c
@@ -648,8 +648,11 @@ ig4iic_attach(ig4iic_softc_t *sc)
    lwkt_serialize_enter(&sc->slz);
    error = bus_setup_intr(sc->dev, sc->intr_res, INTR_MPSAFE,
                   ig4iic_intr, sc, &sc->intr_handle, &sc->slz);
    if (error) {
        device_printf(sc->dev,
              "Unable to setup irq: error %d\n", error);
+       lwkt_serialize_exit(&sc->slz);
        goto done;
    }

References

Timeline

  • 2026-07-14 Discovered during automated audit.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1049 Β· 3 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix for the cited path 319 B view raw
VERDICT.md verdict source-confirmation narrative 942 B ↓ raw
env.txt environment guest uname + toolchain 247 B view raw
VERDICT.md verdict source-confirmation narrative
↓ download raw

DF-1049 source-confirmation

Verdict: REPRODUCED (source-confirmed) Impact: none Confidence: likely

Kernel ref: sys/bus/smbus/ichiic/ig4_iic.c:651

Mechanism

ig4iic_attach serializer leak on bus_setup_intr fail: bus_setup_intr error path goto done without lwkt_serialize_exit; detach re-enters non-recursive serializer -> ASSERT/self-deadlock. confirmed.

Confirmation method

source-only Low-severity; confirmation by code inspection. Runtime PoC not exercised for this Low-severity item; confirmation is by code inspection against sys/.

See fix.diff in this folder (git-apply-able unified diff).

Phase 8 (combined build)

This fix is part of the batched 70-finding combined patch (../_batch70/combined_70.patch) applied to in-guest /usr/src. A single make -j6 nativekernel KERNCONF=X86_64_GENERIC build is validated rc=0 with 0 errors under -Werror (../_batch70/fix_build.log).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED via combined build: fix in combined_70.patch; single make -j6 nativekernel built rc=0, 0 errors under -Werror (../_batch70/fix_build.log). Cited line corrected. Source-only -> validation = clean -Werror compile.

'>>> Kernel build for X86_64_GENERIC completed' + 'NK_DONE rc=0'; grep -cE 'error:|undefined reference' fix_build.log = 0
↓ fix.diffDragonFly 6.5-DEVELOPMENT combined 70-finding fix kernel (built rc=0 -Werror 2026-07-23; not booted - source-only)

Confirmed kernel references

Detail

Exploit chain

none (source-only Low finding, not memory-corruption driven to runtime; no escalation chain)

Evidence (decisive lines)

baseline (with-src #0): bug at sys/bus/smbus/ichiic/ig4_iic.c:651. combined-70 fix kernel: NK_DONE rc=0 (0 errors, -Werror).

PoC changes

authored/validated fix.diff (findings/poc/DF-1049/fix.diff); part of combined_70 kernel build.

Verified recommended fix

See findings/poc/DF-1049/fix.diff (git-apply-able). Matches finding proposal.

Verdict

REAL: ig4iic_attach bus_setup_intr error path goto done without serialize_exit; detach re-enters non-recursive serializer -> self-deadlock. confirmed.