# DF-3205 — krpc_call zero-entropy XIDs + no reply-source filtering

**File:** `sys/vfs/nfs/krpc_subr.c:201,:256-262,:282,:348-351,:380,:432-435`
**Severity:** Medium · **CWE-330** · verified in VERIFY mode on the QEMU guest
with the verbatim-interposition kernel-compiled rig `dfrig13_krpc.c`
(sha256-gated `#include` of the production `krpc_subr.c`; no socket seams —
the rig calls the real `krpc_call`/`krpc_portmap` over the real UDP stack on
lo0, and the attacker is a real userland process).

## What was reproduced (baseline kernel #1)

1. **Blind predicted-XID spoof of the first RPC of a boot** (`blindboot.sh`,
   run as the first rig activity after a fresh boot): the client call used
   xid `0xffffff00` — computed by the attacker purely from the source
   constants (`static_xid = ~0xFF`, krpc_subr.c:201, incremented once per
   call at :282) without ever reading a request payload — and the forged
   portmap GETPORT reply was **accepted**: `krpc_portmap err=0 port bytes
   ca fe`, i.e. the attacker chose the port the diskless client will
   contact next (the first step of becoming its NFS authority).
2. **Determinism dump** (test 5): six sequential calls carried xids
   `0xffffff01..0xffffff06` (+1 per call, continuing the counter) and every
   request came from reserved UDP port **1023** (the deterministic sweep
   :256-262 rebinding the same free port per call).
3. **Wrong-origin acceptance** (test 1): a reply sent from `127.0.0.2`
   (not the server address in the request) was accepted — payload
   `ATTA…` from `ATTACKER-PAYLOAD`. `krpc_call` never connect()s the
   socket and frees/discards the soreceive source address (:348-351,
   :432-435); the only content filter is the xid compare at :380.
4. **End-to-end portmap authority hijack** (test 4): spoofed GETPORT
   reply → client adopts the attacker-chosen port (`ca fe`).
5. **Live side note**: the resend loop (:315 `for(;;)`) has no global
   deadline — a run whose attacker stops replying spins forever printing
   `RPC timeout for server 127.0.0.1` every ~5 s (observed twice).

## Fix validation (kernel #2, fix_all.diff; single-finding hunk = fix.diff)

Same tests on the fixed kernel: xids are `karc4random()`-backed and
non-sequential (`0x07d0b1bd, 0x01e5b8d7, 0xb88a504a, …`; blind predictions
all `differ`), the wrong-origin reply from 127.0.0.2 is dropped (call
completes on the legitimate-origin follow-up with payload `SERV…`), the
blind portmap spoof is rejected (client takes the follow-up answer,
port bytes `77 77`), and honest replies still work (6/6, no regression).
See `run.fixed.log` / `fx_*.log`.

## Reproduce (on the DragonFly guest)

```
sh build.sh          # verbatim sha256 gate + make nativekernel + installkernel; reboot
sh blindboot.sh      # decisive: first-call-of-boot blind spoof (fresh boot only!)
sh run.sh            # determinism dump + wrong-origin + portmap (sniffed) tests
# fix validation:
sh build.sh af834f6e30225b1af68cc83cbcdaf25e29e0e39ca6deaec6f3c13663a661b886 ; reboot
sh run_fixed.sh
```

## Files in this folder

| file | purpose |
|---|---|
| `dfrig13_krpc.c` | verbatim-interposition kernel harness (all rig tests) |
| `attacker.c` | userland malicious RPC server (dump/spoofsrc/short/short_p/pmis/pmap, `-b` blind) |
| `build.sh` / `run.sh` / `blindboot.sh` / `run_fixed.sh` | exact build & run commands |
| `run.log` / `run.blindboot.log` | baseline decisive outputs |
| `run.fixed.log` | fixed-kernel A/B outputs |
| `dump.log spoof.log pmap.log pmap_blind.log` | attacker-side logs (xids, client ports) |
| `fx_dump.log fx_spoof.log fx_pmap.log` | fixed-kernel attacker-side logs |
| `build.log` / `build_fixed.log` | FULL untrimmed kernel build logs (gate lines included) |
| `fix.diff` / `fix_all.diff` / `krpc_subr.fixed.c` | validated fixes (single-finding / combined / full fixed file) |
| `env.txt` | guest, compiler, gate hashes, reachability notes |
| `VERDICT.md` / `manifest.json` / `verdict.json` | narrative + machine verdict |
