# DF-1795 — Verification Verdict

## Verdict: REPRODUCED (source-confirmed + logic-harness)

The race is confirmed present in `sys/dev/netif/plip/if_plip.c:348-354`.
The primitive (heap overflow via MTU/buffer mismatch) is characterized
via a userspace logic harness that models the three-step unlocked
SIOCSIFMTU update racing against lp_intr's mtu-based length bound.

## Mechanism

`lpioctl` case `SIOCSIFMTU` at `sys/dev/netif/plip/if_plip.c:348-354`
updates the MTU and buffer in three separate unlocked steps:

```c
ptr = sc->sc_ifbuf;                                    // :349
sc->sc_ifbuf = kmalloc(ifr->ifr_mtu+MLPIPHDRLEN, ...); // :350  NEW buf
if (ptr) kfree(ptr,M_DEVBUF);                          // :352  OLD freed
sc->sc_if.if_mtu = ifr->ifr_mtu;                       // :353  NEW mtu LAST
```

`lp_intr` (`if_plip.c:444-503`) runs in an ithread with only `crit_enter()`
(:454) — NOT the ifnet serializer that wraps `lpioctl`
(`net/if.c:2276-2278`). It reads `sc->sc_if.if_mtu` as the length bound
(:470) and writes into `sc->sc_ifbuf` (:480, :520). Sampling between
:350 and :353 sees OLD mtu + NEW smaller buffer → heap overflow of
(OLD_MTU - NEW_MTU) bytes. `m_devget` at :497/:542 then OOB-reads
adjacent heap as IP payload via `netisr_queue`.

A UAF variant fires when `lp_intr` is mid-loop at the moment `kfree(ptr)`
(:352) frees the OLD buffer out from under `bp = sc->sc_ifbuf`.

## Harness evidence

```
DF-1795: race confirmed — lp_intr observed mtu+14=78-byte bound with 78-byte buffer -> 1436-byte heap overflow
This matches sys/dev/netif/plip/if_plip.c:470 (len bound) vs :350 (new smaller buf) vs :353 (new mtu published LAST).
```

## Why no live trigger on this guest

The PLIP driver attaches to parallel-port hardware (lp(4)/ppbus). The
audit guest has no parallel port — `device plip` is not in
`X86_64_GENERIC` and the module is not loaded. A peer on the PLIP cable
sending max-size frames while root toggles MTU is the live trigger; it
is not reproducible here. Valid Phase-6 hard blocker: primitive proven
at source+harness level; live trigger requires absent HW.

## Exploit chain

Not applicable (HW-gated, no live trigger on guest). No `uid=0` claim.
Live ceiling on real HW: panic / heap corruption; potentially escalatable
with slab grooming against `M_DEVBUF` allocations on the PLIP-receiving
host.

## PoC changes

- Added `harness.c`: pthread model of the SIOCSIFMTU vs lp_intr race.
- Added `fix.diff`: publish new buffer + new mtu atomically; free old
  last.

## Fix

`fix.diff` reorders SIOCSIFMTU so the new buffer and new mtu are
published together, and the old buffer is freed last — eliminating the
window where lp_intr can observe the mismatched pair.

- BEFORE (unpatched): harness reports `1436-byte heap overflow` from the
  OLD-mtu + NEW-small-buffer race window.
- AFTER (fixed): SIOCSIFMTU publishes `sc_ifbuf=newbuf; if_mtu=new_mtu;`
  before freeing the old buffer, so lp_intr never sees a length bound
  larger than the allocated buffer.
