# DF-2883 VERDICT — REPRODUCED (impact: none — correctness divergence)

## Root cause

Two lines interact:

- `sys/kern/subr_scanf.c:487-490` — accept path leaves `inp` pointing
  AT the last consumed character when `--inr == 0` (no advance).
- `sys/kern/subr_scanf.c:505-510` — the `[sign]0x` fixup pushback does
  `inp--; inr++` unconditionally.

Net effect when the numeric conversion ends exactly at end-of-input
with `buf` ending in `'x'`/`'X'` (input ends in `"0x"`/`"0X"`): `inp`
moves one byte BACK past the consumed `'0'`, `inr` becomes 1 with zero
characters truly remaining, and — unlike the NDIGITS sign pushback at
`:498-502`, which returns immediately — the loop continues, so all
subsequent fmt directives parse the stale byte.

## Proof

Stock kernel #0 (run.log, E4):

```
dfpoc: E4 '0x' %i%c%n: ret=2 v=0 ch='0' nread=2 (libc: ch='x')
```

Guest libc control (userland verbatim replica): `sscanf("0x","%i%c%n")`
→ `ch='x'`; engine replica → `ch='0'`. Same divergence for `"7 0x"`
(`%*d %i%c%n`: engine `ch='0'`, libc `ch='x'`). Re-run stable
(run.2.log).

## Why memory-safe / Low

The stale state is `inr = 1`, `inp = position of the consumed '0'`
which is ≥ the string start and ≤ the last real character: every
subsequent read stays inside `[str, NUL]`. No OOB read or write is
possible from this defect (the OOB-capable stale state is DF-2882's
suppress branch, a different root cause). Impact is wrong parse
results/`nread` for any caller whose input ends in `"0x"` under
`%i`/`%x`/`%p`-style formats — a silent divergence from userland
semantics that base-system parsers (kern_uuid.c:394, if_ethersubr.c:1690,
autoconf.c:263, pci.c:3657) can hit with well-formed-but-truncated
input. Low severity, certain confidence.

## Fix validation

Kernel #1 (in-guest `make nativekernel`, fix.diff hunk 3):
`E4 ... ch='x' (libc: ch='x')` — pushback exact, libc-identical
(run_patched.log). New invariant "`inr==0` ⇒ `inp` at NUL" re-audited
against every `inp` consumer (:134-137, :151-157, :258, :265-272,
CCL :324-348, STRING :365-382, INT :402-491) — all reads are guarded
by `inr > 0`; no behavioral regressions in the other experiments
(E1/E2/E3/E5/E6 outcomes are those of their own fixes/controls).
