# DF-5703 — kernel strtol/strtoul 0x-prefix endptr divergence (int-width twins of DF-5702)

## What this proves

`sys/libkern/strtol.c:70-75` (and its duplicated-body unsigned twin
`sys/libkern/strtoul.c:68-73`) accept the `0x`/`0X` prefix without checking
that a hex digit follows, so inputs whose C99 subject sequence is exactly
`"0"` (`"0x"`, `"0X"`, `"0xg"`, `"0x 1"`, `"0xz"`, …) are reported as
**no conversion** (`*endptr = nptr`, return 0) where C99 7.20.1.4p7 and
DragonFly's own userland libc (`lib/libc/stdlib/_strtol.h:86` /
`_strtoul.h:86`, both gate the prefix on `isxdigit_l(s[1])`) parse `0` and
advance `endptr` past the `'0'`.

Same Chris Torek body as the quad twins of DF-5702 (`strtoq.c`/`strtouq.c`)
— a normalized diff shows the parser cores are identical modulo type width
and comments; DF-5702's fix covered the quad twins only, leaving these
int-width twins divergent.

## Build / run (on the DragonFly guest)

```
scp -F dfbsd-qemu/config kernel_strtol_shim.h diffharness.c build.sh run.sh dfbsd-maxx:/tmp/df5703/
vm.sh run_user 'cd /tmp/df5703 && sh ./build.sh'
vm.sh run_user 'cd /tmp/df5703 && sh ./run.sh'
```

`build.sh` sed-generates userland-compilable copies of the UNTOUCHED guest
`/usr/src/sys/libkern/{strtol,strtoul}.c` (cksum-verified against the audited
repo: `370024243 4183`, `3544205135 3234`) plus fix-only variants carrying
exactly the `isxdigit((unsigned char)s[1])` guard, and links them against
guest libc.

## Expected output

SUMMARY block (deterministic — md5-identical across 3 runs):

```
  kernel-vs-libc VALUE divergences      : 0   (must be 0)
  kernel-vs-libc ENDPTR divergences     : 28  (DF-5703: 0x-prefix-no-xdigit class, valid bases)
  invalid-base semantic diffs           : 306 (legacy BSD behavior; no kernel caller passes these)
  saturation w/o ERANGE (DF-2230 class) : 65  (known, not re-reported)
  patched-kernel residual ENDPTR diffs  : 0   (fix validation; must be 0)
  patched-kernel VALUE regressions      : 0   (must be 0)
harness exit: 0
```

Each divergence row looks like:

```
ENDPTR-DIFF s="0x"  b=0  kernel=0 (off 0)  libc=0 (off 1)  libc_ERANGE=0
```

value is always 0 on both sides — the kernel is strictly fail-closed
(never accepts or advances beyond libc) — so this is a parser-desync /
interop defect (Low), not memory corruption.

## Corpus

DF-5702's 43 inputs plus the 32-bit-long boundary rows
(`2147483647/2147483648/-2147483648/-2147483649/4294967295/4294967296`,
hex forms) and the live 64-bit-long boundaries, x 9 bases x {signed,
unsigned} — the cutoff/cutlim idiom at int width is value-exact against
libc on every boundary row.
