# DF-4939 — if_ic: unbounded kernel heap leak on every SIOCSIFFLAGS down→up cycle

**File:** `sys/dev/netif/ic/if_ic.c` (icioctl, lines 179–202)
**Verdict: REPRODUCED** (leak) on the QEMU guest, DragonFly 6.5-DEVELOPMENT
X86_64_GENERIC, using a virtual-I2C-bridge KLD harness (`lpbbfake.c`, included).
**Fix VALIDATED**: patched `if_ic.ko` shows a byte-flat M_DEVBUF budget across
the same 200-cycle workload that leaked ~600 KB unfixed.

## Prerequisite (no I2C hardware needed)

The `ic` driver only attaches as a child of `iicbus`, which needs an I2C
master controller. The harness KLD registers a fake `lpbb` bit-bang bridge on
ISA with virtual lines (SDA always-ACK, SCL always-high), producing
`lpbb0 → iicbb0 → iicbus0 → ic0` on any machine.

## Build

    # on the guest (root):
    mkdir -p /root/harness && cd /root/harness
    cp <pack>/lpbbfake.c <pack>/Makefile.harness ./Makefile   # see Makefile.harness
    make                       # -> lpbbfake.ko

## Run

    ./run.sh

which performs, in order:

1. `kldload lpbbfake.ko; kldload iicbb; kldload iicbus; kldload if_ic`
2. `ifconfig ic0 10.99.99.1 10.99.99.2 netmask 255.255.255.252 up`
3. baseline `vmstat -m` (M_DEVBUF row)
4. 200 × (`ifconfig ic0 down; ifconfig ic0 up`)
5. `vmstat -m` again → compare M_DEVBUF InUse count/KB

## Expected (unfixed kernel)

    devbuf  219   214K     (before)
    devbuf  619   814K     (after 200 cycles)   <- +400 allocs, +600K (2 x 1504 B/cycle)

Second round of 100 cycles adds another +200 allocs / +300K (run.2.log) —
the leak is unbounded and linear in the number of cycles. With
`ifr_mtu = 65535` (max allowed by if.c:2272), each cycle leaks 2 × 65539 B,
so a root shell looping `ifconfig ic0 down; ifconfig ic0 up` exhausts kernel
heap (local DoS / memory-pressure amplifier; SIOCSIFFLAGS requires
SYSCAP_RESTRICTEDROOT, if.c:2135).

## Fix validation (fix.diff)

Apply `fix.diff` to a guest copy of `/usr/src/sys/dev/netif/ic/if_ic.c`,
rebuild `if_ic.ko`, reboot, repeat step 3–5: M_DEVBUF stays **219 / 214K →
219 / 214K** (byte-flat). Observed and recorded in run.log (section
`FIX-CYCLES-DONE`).

## Root cause

`icioctl` SIOCSIFFLAGS down-branch (if_ic.c:179–187) releases the iicbus and
clears IFF_RUNNING but never frees (nor NULLs) `sc->ic_obuf`/`sc->ic_ifbuf`.
The up-branch (if_ic.c:188–202) unconditionally kmallocs two fresh buffers
and overwrites the pointers, orphaning the previous pair. Repeat forever.
