# DF-0900 — Signed integer overflow in RPC reply verifier length

## The bug (claim)

`sys/vfs/nfs/krpc_subr.c:425` in `krpc_call()`'s `gotreply` path adds an
attacker-controlled network-supplied `authlen` (from the RPC reply verifier
header) into a plain signed `int len`:

```c
415:   len = sizeof(*reply);                                       /* = 24 */
424:   if (reply->rp_auth.authtype != 0) {
425:       len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen); /* signed int + u32 */
426:       len = (len + 3) & ~3;                                    /* stays INT_MIN */
427:   }
428:   m_adj(m, len);                                              /* m_adj(m, INT_MIN) */
```

`len` is declared `int` at `krpc_subr.c:200`. With `authlen = 0x7FFFFFE8`,
`24 + 0x7FFFFFE8 = 0x80000000 = INT_MIN` — signed integer overflow
(undefined behavior in C); `(INT_MIN+3)&~3` stays `INT_MIN`. `m_adj(m, INT_MIN)`
takes the `len < 0` branch (`sys/kern/uipc_mbuf.c:1884`), where
`len = -len` is itself UB and leaves `INT_MIN`; the subsequent
`m->m_len -= len` corrupts the mbuf length, and downstream consumers
(`m_pullup`/copyout) read past the buffer.

## Reachability (boot-time only)

`krpc_call()` is reached **only** via:
- `krpc_portmap()` (`krpc_subr.c:163`) and
- `nfs_mountrpc.c:140,146,155,163,202,253,256` (mount-rpc helpers),

which are themselves called **only** from the diskless NFS-root bootstrap
(`bootpc_init()` at `nfs_vfsops.c:569` → `nfs_mountrpc`). The
`nfs_mountrpc.c` file header states verbatim:

> "This module is not used by normal operating code because the 'mount'
>  command has a far more sophisticated implementation."

There is **no userspace syscall** that reaches this code on a normally-booted
kernel. The attacker model is an **on-path network attacker or a malicious
NFS server** during diskless boot. The normal NFS client/server socket path
in `nfs_socket.c` already validates the equivalent length field with
`if (len < 0 || len > RPCAUTH_MAXSIZ)` (`nfs_socket.c:2347,2385`); the
boot-time `krpc_subr.c` path omits that guard.

## PoC

Because the path is unreachable from userspace on a normally-booted guest,
`overflow_demo.c` is a **userspace harness that reproduces the kernel's exact
arithmetic** (`krpc_subr.c:415-428`) to prove the signed overflow fires for
the cited `authlen` values. It also reproduces the `m_adj` single-mbuf
`m_len` corruption from `sys/kern/uipc_mbuf.c:1859-1924`.

### Build & run (DragonFlyBSD guest, as maxx)

```
cc -O2 -Wall -o overflow_demo overflow_demo.c
./overflow_demo
```

### Expected output

For `authlen = 0x7FFFFFE8` the harness prints:

```
0x7fffffe8   -2147483648             -2147483648             -2147483648             -2147483548   cited overflow trigger
  ** BUG FIRES: sizeof_reply(24) + authlen(0x7FFFFFE8) = 0x80000000 = INT_MIN (signed overflow, UB)
  ** (len+3)&~3 = INT_MIN, passed to m_adj() as req_len.
  ** m_len corrupted: 100 -> -2147483548 (OOB on next mbuf op)
```

and concludes that the path performs signed `int` arithmetic on an
attacker-controlled `u_int32_t` without any upper bound.

## Impact ceiling

Low. Realistic worst case is **boot failure / local DoS at diskless-NFS-root
boot time** (the finding's summary: "Single-mbuf: EBADRPC boot failure;
chained non-cluster: potential panic in m_pullup"). No memory-corruption
primitive reachable from an unprivileged local user — the path is
network/boot-time only and `authlen` shapes only a length, not arbitrary
content. No escalation chain applies.

## Recommended fix

Bound `authlen` against `RPCAUTH_MAXSIZ` (400) — the same guard the normal
NFS socket path already uses — before adding it to `len`. See `fix.diff`.
