# DF-5698 VERDICT — kernel inet_ntop: errno contract unimplemented (Info)

## Bottom line

**Reproduced (informational divergence, impact=none).** The kernel copy of
`inet_ntop` at `sys/libkern/inet_ntop.c` never sets `errno`, breaking the
contract its own doc-comment states ("return: pointer to presentation format
address (`dst'), or NULL (see errno)" — sys/libkern/inet_ntop.c:38-39) and
diverging from its byte-identical-algorithm libc twin (`lib/libc/inet/inet_ntop.c`),
which sets `EAFNOSUPPORT` (lib/libc/inet/inet_ntop.c:62) and `ENOSPC`
(lib/libc/inet/inet_ntop.c:88, :169, :187). Proven deterministically by the
rig's P6/fixcheck probes: baseline leaves `errno=0` after both failure modes.

**No memory-safety defect exists in this file.** Exhaustive differential
testing (≈172.5M comparisons, 3 independent seeds) found zero divergence from
the libc twin in output strings, in the accept/reject decision at *every*
buffer size (0..16 v4, 0..46 v6), and zero guard-page faults across 900k
knife-edge calls with destination buffers bounded by `PROT_NONE` pages
(including zero-writable-byte `size==0` destinations).

## Why this is Info and not higher

- All 12 in-tree kernel call sites are `kprintf` arguments in
  `sys/netinet/ip_icmp.c` (ip_icmp.c:295, :474, :476, :608-609, :859-864,
  :880-883, :1134-1135), each passing a stack buffer of `INET_ADDRSTRLEN`
  (=16), which is exactly the worst-case need of `inet_ntop4`
  ("255.255.255.255" = 15 chars + NUL; the gate `l >= size` at
  sys/libkern/inet_ntop.c:76 accepts exactly at size ≥ strlen+1).
- None of them reads `errno` after the call; NULL returns are impossible for
  their (af, size) pairs anyway, and kprintf renders a NULL `%s` as "(null)"
  (sys/kern/subr_prf.c:750-753).
- No `AF_INET6` kernel caller exists in-tree, so `inet_ntop6` is currently
  exercised only via this rig and by out-of-tree KLDs.
- The missing `errno` matters only to KLD authors who follow the POSIX/libc
  contract published in the file's own header comment — a correctness trap,
  not an exploitable condition.

## Root cause

The file was synced from FreeBSD r213103-era libkern (header:
sys/libkern/inet_ntop.c:18) without the `errno` assignments the libc twin
carries. The four failure returns that should set `errno`:

- `sys/libkern/inet_ntop.c:51-52` — default af case (should be `EAFNOSUPPORT`)
- `sys/libkern/inet_ntop.c:76-78` — inet_ntop4 size gate (should be `ENOSPC`)
- `sys/libkern/inet_ntop.c:158-159` — inner v4-embed size gate (`ENOSPC`)
- `sys/libkern/inet_ntop.c:174-176` — inet_ntop6 final size gate (`ENOSPC`)

## Boundary-math proof (why no overflow exists)

- `inet_ntop4` formats into a 16-byte staging buffer (never truncated), then
  requires `l < size` (i.e. `size >= strlen+1`) before `strlcpy(dst, tmp, size)`
  — bounded exactly (sys/libkern/inet_ntop.c:75-80).
- `inet_ntop6` builds in a 46-byte staging buffer whose maximum possible
  content is 39 chars + NUL (8×4 hex + 7 colons); the final gate compares
  `tp - tmp` (NUL included, since `*tp++ = '\0'` at :169 precedes it) against
  `size` before `strcpy` (sys/libkern/inet_ntop.c:174-177) — accepts exactly
  when `strlen+1 <= size`. The trailing-`:` write at :168 and the embedded-v4
  call at :158 were hand-verified to stay within the staging buffer.
- Empirical: 900k guard-page knife-edge calls (dst[size] on a PROT_NONE page;
  size==0 with zero writable bytes) — zero SIGSEGV, all accepted lengths equal
  canonical lengths (P7 in run.log/run.2.log/run.3.log).

## Fix validation

`fix.diff` adds the four `errno` assignments plus `<sys/errno.h>`
(`git apply --check` clean against sys/). Validated in the same rig:
`fixcheck` on the baseline TU prints `errno=0(NOT-SET)` for both failure
modes (exit 1); on the fixed TU `errno=28(ENOSPC)` / `errno=47(EAFNOSUPPORT)`
(exit 0). The fixed TU re-ran the full 23.9M-comparison differential with
`MISMATCH=0` — the fix changes nothing except the documented errno contract.

## Environment

DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (X86_64_GENERIC),
guest gcc 8.3 [DragonFly]. Kernel TU sha256
`6ddc914991203a5f4e54544a8d8c511790e0428e3400cf376d2ad49d87` identical between
the audited tree and the guest /usr/src copy used for the build. Guest left
clean (only /tmp/ntop used; no kernel changes; no panic).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
