# DF-1880 — Verification Verdict

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

The mac[32] OOB write is confirmed at
`sys/dev/netif/oce/oce_hw.c:568`. The harness reproduces the 192-byte
overflow when 64 multicast groups are joined.

## Mechanism

```c
// oce_hw.c:564-580
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
    if (ifma->ifma_addr->sa_family != AF_LINK) continue;
    if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE) break;  // :568 — 64
    bcopy(LLADDR(...), &req->params.req.mac[num_mac], ETH_ADDR_LEN); // :576
    req->params.req.num_mac++;
}
```

`OCE_MAX_MC_FILTER_SIZE = 64` (oce_hw.h:180) but the `mac[]` array is
declared `mac[32]` inside `mbx_set_common_iface_multicast` (oce_hw.h:1119).
The loop bound is 64; the array is 32. Joining 33+ multicast groups
writes 6 bytes past `mac[32]` starting at byte offset 192 in the
212-byte DMA alloc (`oce_dma_alloc(sizeof(struct
mbx_set_common_iface_multicast))` at :557). Joining 64 groups writes
the full `32*6 = 192` bytes off the end.

Trigger: `setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)` — no privilege
needed → `in_addmulti` (netinet/in.c:1374) → `if_addmulti` →
`ifp->if_ioctl(SIOCADDMULTI)` (net/if.c:2739) → `oce_ioctl`
(oce_if.c:410-412) → `oce_hw_update_multicast`. Join 33 distinct
multicast groups (low 23 bits differ) → first overflow. Join 64 → full
192-byte overflow. MAC contents partly attacker-controlled (low 23 bits
IPv4 / low 32 bits IPv6).

## Harness evidence

```
DF-1880: oce_hw_update_multicast (oce_hw.c:547-585)
  joined 64 multicast groups (loop bound OCE_MAX_MC_FILTER_SIZE=64, mac[] array size=32 slots)
  OOB writes = 32 MAC slots x 6 bytes = 192 bytes past mac[32] into the DMA alloc
  Harness: simulated bcopy touched 192 sentinel bytes past mac[32]
  Fix: loop bound should be nitems(req->params.req.mac)=32, not OCE_MAX_MC_FILTER_SIZE=64.
```

## Why no live trigger on this guest

`device oce` is in X86_64_GENERIC but the audit guest has no Emulex
OneConnect NIC. `if_oce.ko` is present but not loaded; no `oce` network
interface exists. Valid Phase-6 hard blocker.

## Exploit chain

Not applicable (oce-HW-gated). No `uid=0` claim. On a host with an oce
NIC, `IP_ADD_MEMBERSHIP` is unprivileged, so the 192-byte overflow is
reachable by any local user. With slab grooming of the DMA alloc bucket,
victim objects (function pointers, refcounts) could be corrupted →
kernel priv-esc. Live ceiling on real HW: panic / reliable heap
corruption.

## PoC changes

- Added `harness.c`: flat-buffer model showing 192-byte OOB.
- Added `fix.diff`: change loop bound to `nitems(req->params.req.mac)`.

## Fix

`fix.diff` changes the loop bound at :568 from `OCE_MAX_MC_FILTER_SIZE`
to `nitems(req->params.req.mac)`, matching the actual array size.

- BEFORE: harness shows 192 bytes written past mac[32].
- AFTER: the loop breaks at num_mac==32, no OOB.
