# DF-0617 — PoC: ng_ether_rcv_upper bridge_input UAF

Use-after-free (CWE-416) in `ng_ether_rcv_upper()`: `bridge_input_p()` return
value is discarded at `sys/netgraph7/ether/ng_ether.c:658`, making the
`if (m == NULL)` check at line 659 dead code. When `bridge_input()` consumes
the mbuf (IFF_MONITOR, packet-for-bridge-MAC, BPDU, bridge_forward, etc.), the
freed/dangling mbuf pointer is passed to `ether_demux_oncpu()` at line 664.

## Files

- `uaf_ng_ether.c` — **code-level harness** that replicates the exact control
  flow of `ng_ether_rcv_upper()` (lines 640–666) with a poisoned-freed-memory
  allocator. Two modes:
  - `--buggy`: replicates the current kernel code (return value discarded) → UAF
  - `--fixed`: replicates the one-line fix (`m = bridge_input_p(ifp, m)`) → no UAF
- `fix.diff` — git-apply-able one-line fix.
- `VERDICT.md` — full narrative (mechanism, results, exploit chain, fix validation).

## Why a code-level harness (not a runtime trigger)

A runtime trigger was attempted: netgraph7 modules (core, socket, ether) and
`if_bridge.ko` all **build and load cleanly** as KLDs on this guest. A bridge
with `vtnet0` as a member + `IFF_MONITOR` was set up successfully. However, the
guest becomes unresponsive shortly after the topology is active — the ng_ether
input-orphan hooks (`ng_ether_input_orphan_p`) interfere with normal `vtnet0`
traffic processing under the bridge. This instability is **not** caused by the
DF-0617 bug (which is in `ng_ether_rcv_upper`, reachable only via the upper
hook). Per the DF-0265/DF-0594/DF-0616 precedent, a code-level harness provides
a deterministic proof.

## Build & run

```
cc -O2 -Wall -o uaf_ng_ether uaf_ng_ether.c
./uaf_ng_ether            # both modes
./uaf_ng_ether --buggy    # buggy only
./uaf_ng_ether --fixed    # fixed only
```

Or use the scripts: `./build.sh && ./run.sh`

## Expected output

```
--- BUGGY mode (current kernel: ng_ether.c:658 discards return value) ---
    ether_demux_oncpu: m->m_flags=0xdededede  m->m_len=-555819298  m->m_data=0xdededededededede
    *** UAF DETECTED: mbuf was freed (all fields poisoned to 0xde) but still dereferenced! ***

--- FIXED mode (m = bridge_input_p(ifp, m); — return value captured) ---
  Result: freed_count=1, uaf_detected=0

Verdict: REPRODUCED — the one-line fix (m = bridge_input_p(...)) eliminates the UAF
```

## Fix

The fix is one line at `ng_ether.c:658`:

```diff
-		bridge_input_p(ifp, m);
+		m = bridge_input_p(ifp, m);
```

This matches the canonical correct pattern at `sys/net/if_ethersubr.c:1252`.
The patched module's disassembly confirms the behavior change: the return value
is captured (`mov %rax,%rbx`), tested (`test %rax,%rax`), and
`ether_demux_oncpu` is conditionally skipped when `bridge_input_p` returns NULL.

## Vulnerability class

UAF (CWE-416). The freed object is a kernel mbuf. In a VPN/PPPoE topology
where the ng_ether upper hook is already connected on a bridged NIC, a remote
peer can trigger this by sending any packet through the netgraph path. The
deterministic IFF_MONITOR NULL-return path frees the mbuf; the dangling pointer
is then dereferenced in `ether_demux_oncpu`. With mbuf-zone heap grooming, this
could escalate from DoS to kernel info leak or code execution.
