# DF-4940 — if_ic: icoutput forces IFF_RUNNING and transmits without the up-path state machine

**File:** `sys/dev/netif/ic/if_ic.c` (icoutput, line 347)
**Verdict: REPRODUCED** on the QEMU guest (state-machine corruption with
two security-relevant consequences, both demonstrated), **fix VALIDATED**.

## The bug

`icoutput()` begins with an unconditional `ifp->if_flags |= IFF_RUNNING;`
(if_ic.c:347). ip_output() calls `ifp->if_output()` with **no** IFF_UP gate
(sys/netinet/ip_output.c:698,742), and `if_down()` deletes no routes
(sys/net/if.c:1818→if_down_dispatch, which only clears IFF_UP, purges the
ifq and purges *cloned* routes — sys/netinet/in.c:1450 in_if_down). So any
routed packet on a *down* ic0 executes the transmit path on an interface
that never ran the up branch. Consequences:

1. `ic0` is flagged RUNNING while DOWN. The next SIOCSIFFLAGS "up" sees
   `IFF_UP && !IFF_RUNNING` false (if_ic.c:188) and therefore **skips both
   the buffer allocation and iicbus_request_bus() forever** — on a
   never-allocated interface this *sustains* the DF-2001 NULL-deref state;
2. the packet is transmitted over the iicbus without `if_ic` owning the bus
   (ownership is only taken in icioctl; iicbus data transfers do not check
   `sc->owner` — sys/bus/iicbus/iiconf.c:298-311), corrupting any transfer
   in flight by the actual bus owner;
3. a subsequent SIOCSIFFLAGS "down" calls `iicbus_release_bus()` for a bus
   `if_ic` no longer owns; the parent-level release callback fires before
   the ownership check (iiconf.c:131-140) — a spurious controller-level
   release (benign with the stock null callback, but wrong).

## Prerequisite

Same virtual-I2C-bridge harness as DF-4939 (`lpbbfake.c`, included) — no
I2C hardware needed.

## Build / Run

    sh build.sh    # builds lpbbfake.ko on the guest
    sh run.sh

## Expected (unfixed kernel)

    after ifconfig ic0 down:   flags=808810<POINTOPOINT,...>          (no RUNNING)
    ping -c1 10.99.99.2        (TX while DOWN — netstat shows Opkts=1)
    after ping:                flags=808850<POINTOPOINT,RUNNING,...>  (RUNNING forced while DOWN!)
    ifconfig ic0 up            -> allocates NOTHING (devbuf 819 -> 819)
    [control: normal down/up]  -> allocates 2 buffers (devbuf 819 -> 821)

## With fix.diff (patched if_ic.ko)

    ping -c1 10.99.99.2        -> "sendto: Network is down" (ENETDOWN)
    flags after ping:          808810 (no phantom RUNNING)
    ifconfig ic0 up            -> flags 808851 (proper UP+RUNNING); buffers reused (no leak, by DF-4939's fix)

## Fix

Delete the forced `ifp->if_flags |= IFF_RUNNING;` and refuse to transmit
unless the interface completed the up branch (UP+RUNNING and non-NULL
buffers) — see fix.diff.
