# DF-0414 PoC — PPPoE discovery ph->length heap OOB read

## What this is

Remote-triggered kernel OOB heap read in the netgraph PPPoE discovery
branch. Single malicious PPPoE PADI/PADO/PADR/PADS with `ph->length` larger
than the actual mbuf payload drives `get_tag()` / `scan_tags()` past the mbuf
into kernel heap. Demonstrated effect: kernel panic in `get_tag+0x12`
(page fault reading `pt->tag_len` from unmapped memory). Reachable effect:
kernel heap info leak via `scan_tags`/`insert_tag` in PADR/PADO responses.

## Files

| File | Purpose |
|---|---|
| `trigger.c` | minimal C PoC: builds netgraph topology, injects 1 malicious PADI |
| `trigger_spray.c` | heap-pressure variant that reliably panics the baseline |
| `sanity.c` | well-formed PADI regression test (fix must NOT reject this) |
| `build.sh` | `cc -o trigger trigger.c -lnetgraph` |
| `run.sh` | runs `./trigger` |
| `fix.diff` | git-apply-able fix (validates `ph->length` vs mbuf size in discovery) |
| `VERDICT.md` | full narrative, mechanism, fix-validation |
| `panic.txt`, `baseline_panic.log`, `boot_full.log` | crash evidence |
| `env.txt` | guest environment |
| `manifest.json` | artifact catalog (for the static site) |

## Reproduce

On the DragonFly guest as root:

```
kldload ng_socket
kldload ng_pppoe
./build.sh
./trigger_spray            # baseline: panics in get_tag+0x12 after a few iters
```

After applying `fix.diff` and rebuilding the kernel + module:

```
./trigger_spray            # fixed: 50x EMSGSIZE per run, no panic
./sanity                   # fixed: well-formed PADI returns ENETUNREACH, NOT EMSGSIZE
```

## Expected output (baseline / bug present)

```
Fatal trap 12: page fault while in kernel mode
fault virtual address	= 0xfffff8011c100002
fault code		= supervisor read data, page not present
instruction pointer	= 0x8:0xffffffff82656022
Stopped at      get_tag+0x12:   movzwl  0x2(%rax),%edx
db>
```

## Expected output (after fix)

```
[*] spraying 20000 sockets to pressurize mbuf zone...
[+] opened 20000 sockets
[+] ng_pppoe peer created
[+] injecting malicious PADI (ph->length=0xFFFF, 24-byte mbuf)
[!] iter 0 NgSendData: Message too long        <-- EMSGSIZE from the fix
[!] iter 1 NgSendData: Message too long
...
[+] done. If kernel still up, OOB walk stayed in mapped memory.
RC=0
```

## Note about the finding's file path

The finding cites `sys/netgraph7/pppoe/ng_pppoe.c` (new netgraph7). That file
is **not built by default** — `X86_64_GENERIC` does not enable
`NETGRAPH7_PPPOE`, and `/boot/kernel/ng_pppoe.ko` is built from the
old-netgraph `sys/netgraph/pppoe/ng_pppoe.c` (verified via
`strings /boot/kernel/ng_pppoe.ko`). The identical bug pattern exists in the
old-netgraph file (which is the production-reachable code), so this PoC and
`fix.diff` target `sys/netgraph/pppoe/ng_pppoe.c`. The same validation should
be applied to the netgraph7 file as defense-in-depth.
