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_STARTit cachessc->ic_cp = sc->ic_ifbuf(line 267) - in
INTR_RECEIVEit 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:
kldload if_ic- do NOT run
ifconfig ic0 up - 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.
Recommended fix
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
sys/dev/netif/ic/if_ic.c:127-151βicattachnever allocatesic_obuf/ic_ifbufsys/dev/netif/ic/if_ic.c:193-197βUPbranch is the only allocation sitesys/dev/netif/ic/if_ic.c:267,309βicintrderefs NULLic_cpsys/dev/netif/ic/if_ic.c:358βicoutputderefs NULLic_obufsys/dev/netif/ic/if_ic.c:179-187βdownpath doesn't fully reset PCF (TODOXXX disable PCF)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2001 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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_testablenot_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.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- i
- c
- /
- i
- f
- _
- i
- c
- .
- c
- :
- 2
- 6
- 7
- ,
- 3
- 0
- 9
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.
No comments yet.