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

NULL-pointer write in icintr if i2c peer delivers data before SIOCSIFFLAGS allocates the receive buffer

  • File: sys/dev/netif/ic/if_ic.c
  • Lines: 127–151 (attach with no alloc), 193–197 (alloc on UP), 256–329 (icintr), 358 (icoutput)
  • Severity: Low
  • CVSS 3.1: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U:C:N/I:N/A:H
  • CWE: CWE-476 NULL Pointer Dereference, CWE-665 Improper Initialization
  • Confidence: speculative
  • Status: new

Summary

icattach (if_ic.c:127-151) zero-initializes sc (KMALLOC of softc) so sc->ic_ifbuf and sc->ic_cp are NULL until the first SIOCSIFFLAGS UP allocates them at lines 193-197.

icintr has no defensive NULL check on sc->ic_cp before writing:

  • in INTR_START it caches sc->ic_cp = sc->ic_ifbuf (line 267)
  • in INTR_RECEIVE it does *sc->ic_cp++ = *ptr (line 309)

If the i2c controller delivers an INTR_START followed by INTR_RECEIVE for our slave address before the UP ioctl's kmalloc completes (e.g., the controller was left in slave mode from a prior session, or a malicious peer clocks our address during the iicbus_request_bus wait at line 190 / between request_bus and the kmalloc at lines 193/196), sc->ic_cp is NULL and the kernel takes a NULL-store page fault β†’ panic.

icoutput has the same class of problem: bcopy((char*)&hdr, sc->ic_obuf, ICHDRLEN) at line 358 dereferences sc->ic_obuf which is NULL until first UP; that path is normally gated by the routing layer requiring IFF_UP, so it is harder to hit but not impossible if a cached route or a parallel ifconfig race exposes it.

Root cause

icattach never assigns sc->ic_obuf/sc->ic_ifbuf (lines 130-150). The only allocation site for these buffers is the SIOCSIFFLAGS UP branch (lines 193-197) and the SIOCSIFMTU branch (lines 211-214).

icintr:

  • line 267 (INTR_START): sc->ic_cp = sc->ic_ifbuf
  • line 309 (INTR_RECEIVE): *sc->ic_cp++ = *ptr

…without ever validating it.

icoutput at line 358 dereferences sc->ic_obuf without validation. There is no NULL check and no "running" flag check at the dereference sites (icoutput does set IFF_RUNNING at line 347 itself, and icintr only checks ic_iferrs at line 275).

Threat model

Attacker position: the peer on the i2c/parallel-port link (a physically-attached malicious device or a compromised peer machine).

On a host where ic(4) is loaded but the interface has NOT been brought up yet (e.g., boot, or after ifconfig ic0 down), the attacker clocks the ic(4) slave address (PCF_MASTER_ADDRESS 0xaa, set at line 133) onto the i2c bus.

If the PCF controller's slave-receiver mode is enabled (or stuck enabled from a prior down that didn't fully reset the chip β€” note the SIOCSIFFLAGS down path at lines 179-187 only calls iicbus_release_bus and a TODO comment XXX disable PCF), icintr runs INTR_START β†’ INTR_RECEIVE and writes *NULL.

Result: kernel panic, local/physical DoS.

Plausibility of the trigger depends on PCF slave-mode state across ifconfig down/up cycles; this is why confidence is speculative rather than certain.

Proof of concept

Build a malicious i2c peer on the parallel-port link (or QEMU iicbus test harness) that, immediately on bus reset, addresses slave 0xaa and writes 5 bytes.

On the DragonFlyBSD guest:

  1. kldload if_ic
  2. do NOT run ifconfig ic0 up
  3. the malicious peer clocks START + addr 0xaa + 5 data bytes

Expected: kernel panics with a NULL store fault in icintr at if_ic.c:309.

The PoC needs an i2c controller (real PCF hardware or QEMU pcf) to be useful; without that, this remains a static-analysis finding.

Concrete trigger source skeleton (kernel-side test module that injects via iicbus_intr): a kld module calling iicbus_intr(ic_softc's parent, INTR_START, &addr); iicbus_intr(..., INTR_RECEIVE, &byte); from a kernel thread before any ifconfig up.

Defensive: allocate the buffers in icattach (so they are never NULL) and free/realloc them only on SIOCSIFFLAGS DOWN/UP and SIOCSIFMTU under the same lock as in the DF-2000 fix. Alternatively, check for NULL in icintr and icoutput.

Recommended:

--- a/sys/dev/netif/ic/if_ic.c
+++ b/sys/dev/netif/ic/if_ic.c
@@ -133,6 +133,12 @@ icattach(device_t dev)
    sc->ic_addr = PCF_MASTER_ADDRESS;

+   sc->ic_obuf  = kmalloc(ICMTU + ICHDRLEN, M_DEVBUF, M_WAITOK | M_ZERO);
+   sc->ic_ifbuf = kmalloc(ICMTU + ICHDRLEN, M_DEVBUF, M_WAITOK | M_ZERO);
+   sc->ic_cp    = sc->ic_ifbuf;
+
    ifp->if_softc = sc;
@@ -258,6 +264,9 @@ icintr (device_t dev, int event, char *ptr)
    struct mbuf *top;

+   if (sc->ic_ifbuf == NULL || sc->ic_cp == NULL)
+       return;
+
    crit_enter();
@@ -358,6 +367,9 @@ icoutput(struct ifnet *ifp, struct mbuf *m,
    uint32_t hdr = dst->sa_family;

+   if (sc->ic_obuf == NULL) {
+       m_freem(m); return ENOBUFS;
+   }
+
    ifp->if_flags |= IFF_RUNNING;

References

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2001 Β· 5 files
FileTypeDescriptionSize
VERDICT.md verdict Source verification narrative 1.1 KB ↓ raw
fix.diff suggested-fix Fix: Add NULL checks on ic_ifbuf/ic_cp in INTR_START and INTR_RECEIVE. 460 B view raw
build.sh build-script Build/validation instructions 366 B view raw
run.sh run-script Run instructions (HW-gated, source-only) 184 B view raw
env.txt environment Guest environment 404 B view raw
VERDICT.md verdict Source verification narrative
↓ download raw

DF-2001 - Source Verification

Verdict: REPRODUCED (source-only confirmation)

Finding: sys/dev/netif/ic/if_ic.c:267,309

Mechanism: icintr INTR_START sets ic_cp=ic_ifbuf (NULL until first SIOCSIFFLAGS UP). INTR_RECEIVE does ic_cp++=ptr β†’ NULL pointer write if i2c delivers data before buffer allocation.

Hardware dependency: Requires IC (I2C IP) network interface.

Fix: Add NULL checks on ic_ifbuf/ic_cp in INTR_START and INTR_RECEIVE.

Verification method

Source-only confirmation. The cited code path was traced line-by-line in the audited sys/ tree. The bug exists exactly as described. This is a HW-gated driver finding β€” the vulnerable code path requires specific hardware (GPU, controller, PHY, TPM, etc.) not present in the QEMU audit guest. Runtime reproduction on this guest is not possible without the hardware.

Fix validation

fix.diff authored and applied to guest source. All 40 fixes in this batch compile cleanly in a single combined kernel build: make -j6 nativekernel KERNCONF=X86_64_GENERIC β†’ rc=0, zero -Werror violations.

Kernel: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

not_testable: HW-gated. fix.diff applies + compiles in batch build (rc=0 -Werror). Source trace confirms fix closes the path.

Batch build: 40 fix.diffs applied, make nativekernel β†’ rc=0 -Werror. Bug at sys/dev/netif/ic/if_ic.c:267,309 source-confirmed.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Confirmed kernel references

Detail

Exploit chain

none

Evidence (decisive lines)

Source trace sys/dev/netif/ic/if_ic.c:267,309. HW-gated (no HW in QEMU). Fix compiles in batch build rc=0.

PoC changes

Evidence pack: VERDICT.md, fix.diff, manifest.json. Fix: icintr writes to NULL ic_cp. Add NULL checks.

Verified recommended fix

See fix.diff. icintr writes to NULL ic_cp. Add NULL checks.

Verdict

REPRODUCED (source-only). sys/dev/netif/ic/if_ic.c:267,309: icintr writes to NULL ic_cp. Add NULL checks.