# DF-3487 — ng_pppoe_rcvdata() PRIMED branch: heap OOB read via unvalidated
`ph->length` (DF-0414 sibling sink)

Kernel heap info leak (and adjacent-packet disclosure) in
`sys/netgraph7/pppoe/ng_pppoe.c` `ng_pppoe_rcvdata()` PPPOE_PRIMED branch —
the counterpart of DF-0414 which covered only `ng_pppoe_rcvdata_ether()`.

## Reproduce (guest, root)

```sh
# 1. build + load netgraph core, pppoe node, harness
cd /usr/src/sys/netgraph7/netgraph && make obj depend && make
kldload /usr/obj/usr/src/sys/netgraph7/netgraph/netgraph.ko
cd /usr/src/sys/netgraph7/pppoe && make obj depend && make
kldload /usr/obj/usr/src/sys/netgraph7/pppoe/ng_pppoe.ko
cd /root/ppsbx && make && kldload ./ppsbx.ko

# 2. create pppoe node, put session hook "svc1" into PPPOE_PRIMED
sysctl debug.ppsbx_cmd=1        # MKPEER eth0<->ethernet, CONNECT sess<->svc1,
                                # NGM_PPPOE_OFFER(svc1) => PRIMED

# 3. fire the crafted 32-byte PADI (ph->length=1024, SRV tag len=900)
sysctl debug.ppsbx_cmd=2

# 4. observe
sysctl debug.ppsbx_len                       # baseline: 944  (patched: 0)
sysctl -b debug.ppsbx_capture | od -A x -t x1 | less
```

## Expected output (baseline, stock kernel)

* `debug.ppsbx_len: 944` — the node emits a PADO whose Service-Name tag
  contains **896 bytes read out of bounds** past the 32-byte PADI mbuf
  (the copy runs to m_data+924 of a header mbuf whose real payload ends at
  byte 32; ~668 bytes past the 256-byte mbuf *object*).
* Leaked bytes contain adjacent-mbuf-zone memory: kernel heap pointers
  (e.g. `08 29 63 17 01 f8 ff ff` = 0xfffff80117632908 — mbuf m_next /
  m_data), mbuf headers, ifnet names (`.vn1`), and on an active-traffic
  boot a complete captured TCP packet (`45 00 00 2c ... 0a 00 02 0f`,
  seq numbers of the ssh session itself). See `leak1.txt`,
  `leak_sample.txt` (boot A rich sample), `run.log`.
* 3/3 runs identical per boot (`leak1/2/3.txt`).
* With `fix.diff` applied to ng_pppoe.c and the module rebuilt:
  `debug.ppsbx_len: 0`, empty capture, no crash (`fixleak1/2.txt`,
  `fixrun.log`).

## Threat model

The PRIMED branch parses a PADI that the PPPoE server daemon sends back
down its session hook. Any daemon that relays/echoes PADI payload from an
unauthenticated LAN peer (relay/proxy deployments; the normal
PADI→PADO flow re-embeds peer-supplied tags) delivers attacker-controlled
`ph->length` to this path. Direct trigger also possible by the
netgraph-controlling user (root via ngctl/ng_socket). Impact: kernel heap
disclosure (pointers + cross-object data) and potential panic when the tag
walk crosses an unmapped page (ph->length up to 0xFFFF).

## Files

* `ppsbx.c` — harness kernel module (setup + fire + PADO capture)
* `Makefile`, `run.sh`, `build.sh`
* `leak1/2/3.txt`, `leak_sample.txt`, `run.log`, `env.txt` — baseline
* `fixleak1/2.txt`, `fixrun.log` — patched-module runs
* `fix.diff` — one-hunk fix (validated)
* `VERDICT.md`, `manifest.json`, `verdict.json`
