# DF-3554 — netgraph (v1) ng_tee: two cross-connected tees + left2right entry = constructible forwarding cycle → unbounded synchronous ng_send_data recursion → hard machine wedge (DF-3546 class, tee instance)

v1 twin of DF-3553 (ng7). `ngt_rcvdata` (sys/netgraph/tee/ng_tee.c:277-341)
is **positional** (no hook-order table): in left → out right (+dup
left2right), in right → out left (+dup right2left), in left2right → out
left. Cross-connecting two tees (`T1.left ↔ T2.left`,
`T1.right ↔ T2.right`) closes a packet cycle; the socket node's `tap` hook
wired to `T1.left2right` is the **entry edge**. v1 netgraph forwards
**synchronously** — `ng_send_data()` (sys/netgraph/netgraph/ng_base.c:1678)
calls the peer's `rcvdata` directly in the caller's thread, with no hop
bound and no cycle detection — and the tee additionally performs an
`m_dup()` + `m_freem()` per hop for the disconnected dup hook.

## Observed failure (stock v1 modules, 4/4 runs)

One 1-byte `sendto()` never returns and the machine dies a **hard system
wedge**: ssh (and ICMP) stop being answered within seconds; serial console
silent; no panic banner, no dump; only a QEMU reset recovers. Terminal
states varied across runs:

  * one CPU spinning at 100% with interrupts disabled in `siocngetc+0x41`
    (kernel debugger entry waiting for console input) while the other CPUs
    sat stopped in `Xcpustop` / `cpu_smp_stopped` — i.e. a debugger/panic
    entry whose console could not be serviced;
  * all CPUs in `cpu_idle` yet the network stack catatonic (no TCP answers);
  * CPU0 in `memmove` (mbuf copy path) + CPU1 in an HPET `DELAY` loop,
    others idle — cross-CPU deadlock live-lock.

In-kernel stack walk of the wedging thread (QEMU monitor virtual-memory
dump, symbols from the guest's own `nm`):

    ... m_free+0x1ce / m_freem+0x15            (per-hop m_dup churn)
    0xffffffff826022f0  netgraph.ko+0x22f0     (ng_send_data)
    0xffffffff82656405  ng_tee.ko+0x405        (ngt_rcvdata)
    0xffffffff82602277  netgraph.ko+0x2277     (ng_send_data)
    0xffffffff8263e34f  ng_socket.ko+0x34f     (ngd_send — sendto entry)
    0xfffff80000706174  "tap\0"                (injection sockaddr residue)

i.e. the sendto → ngd_send → ng_send_data → ngt_rcvdata → ng_send_data →
m_dup/m_free forwarding recursion live on the wedged machine.

## Trigger privilege

ROOT ONLY (verified for the identical v1 gate in DF-3546's
unpriv_check.txt: `ngc_attach` caps_priv_check, uid 1001 → errno 43).
Severity Low (privileged DoS), same class as DF-3540/DF-3546/DF-3553.

## Build / Run (in-guest, root, stock modules)

    sh build.sh     # cc -O2 -o /root/poc/tee_cycle_v1 tee_cycle.c
    sh run.sh       # kldload netgraph ng_socket ng_tee (v1 stock) + trigger
    # EXPECTED (stock): sendto never returns; machine wedges (see above);
    # ssh dies; vm.sh status -> down; only reset recovers.

## Fix validation (fix.diff — v1 core depth bound in ng_send_data)

| build | tee-cycle trigger |
|---|---|
| stock v1 netgraph.ko | hard wedge x4 (ssh dead, unrecoverable) |
| patched netgraph.ko (depth bound 16, -Werror clean) | `STEP sendto errno=62 (ELOOP)`, `RESULT no-panic survived 30s`, `RUN_RC=0`, guest healthy (fix1_cycle.log) |

fix_status: fixed.

## Files

    tee_cycle.c     trigger (one source; no -DNG7 = v1 ABI — this finding)
    build.sh run.sh exact commands
    run.log         stock run trigger output (graph built, injection hung)
    wedge_forensics.txt  RIP/RSP samples, CPU states, stack walk, symbol map
    boot_tail.txt   serial console tail at wedge time (no panic output)
    fix1_cycle.log  patched-core run: sendto ELOOP, survives
    fix_build.log   patched v1 netgraph.ko build (clean)
    fix.diff        v1 core NG_SEND_DEPTH_MAX bound in ng_send_data
    env.txt         guest uname / module state
