DF-5698 / verdict.json
{ "finding_id": "DF-5698", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "Kernel inet_ntop (sys/libkern/inet_ntop.c) never sets errno on any failure path, breaking the contract its own doc-comment states (NULL (see errno), inet_ntop.c:38-39) and diverging from its algorithm-identical libc twin (lib/libc/inet/inet_ntop.c sets EAFNOSUPPORT at :62 and ENOSPC at :88,:169,:187). Demonstrated deterministically: rig P6 and fixcheck probes show errno=0 after both too-small-dst and bad-af failures on the baseline TU, and ENOSPC/EAFNOSUPPORT on the fixed TU. No security impact in-tree: all 12 kernel call sites (ip_icmp.c) pass correctly sized INET_ADDRSTRLEN buffers, none reads errno, and kprintf renders NULL %s as (null) (subr_prf.c:750-753). Alongside this divergence, the same rig proved the formatter memory-safe over ~200M comparisons: string output and accept/reject decisions identical to libc at every buffer size (0..16 v4, 0..46 v6), zero faults in 1.4M guard-page knife-edge calls with dst[size] on PROT_NONE pages (incl. zero-writable-byte size==0 dsts).", "exploit_chain": "", "evidence": "findings/poc/DF-5698/run.log decisive lines: P6: size-too-small kernel-returns=0x0 errno-after=0 and variant=BASELINE ... errno=0(NOT-SET) ... BASE_RC=1 vs variant=FIXED ... errno=28(ENOSPC) ... errno=47(EAFNOSUPPORT) FIXED_RC=0; memory-safety proof: P7: guardpage calls=500000 accepted=200000 (no SIGSEGV) + TOTAL=27889126 MISMATCH=0 (pack run) and run.2.log/run.3.log (MISMATCH=0, 61.4M/25.9M comparisons); fix validation fix_run.log + fix_build.log; VERDICT.md has the full boundary-math derivation (tp-tmp includes the NUL at :169 before the :174 gate).", "kernel_refs": [ "sys/libkern/inet_ntop.c:38", "sys/libkern/inet_ntop.c:51-52", "sys/libkern/inet_ntop.c:76-78", "sys/libkern/inet_ntop.c:158-159", "sys/libkern/inet_ntop.c:169", "sys/libkern/inet_ntop.c:174-177", "lib/libc/inet/inet_ntop.c:62", "lib/libc/inet/inet_ntop.c:88", "lib/libc/inet/inet_ntop.c:169", "lib/libc/inet/inet_ntop.c:187", "sys/netinet/ip_icmp.c:295", "sys/kern/subr_prf.c:750-753" ], "poc_changes": "No seed PoC existed (pass-1 filed nothing). The whole rig was authored in this run: harness.c (7-phase differential vs DragonFly libc), fixcheck.c (errno probe), userland shims for the kernel TU, inet_ntop_fixed.c (fix validation TU generated from the audited sys/ copy). Rig bugs fixed during iteration: guest root shell is csh (all remote logic moved into sh scripts), scp transport replaced by base64-over-ssh after flaky connection closes, guard-page allocator initially leaked mappings (munmap added), P5 af-list initially assumed Linux AF values (DragonFly: AF_INET=2, AF_INET6=28) - assertion changed to kernel-vs-libc decision equality.", "attempts": 4, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 160, "guest_dirty": 0, "build_cmd": "sh findings/poc/DF-5698/build.sh", "run_cmd": "sh findings/poc/DF-5698/run.sh 3000000 991199", "code_hash": "c370111fc9d6004e5fceaef902a300f10d36b56010c6baad6ba1f34e5fc870e6", "notes": "Kernel TU sha256 6ddc914991203a5f4e54544a8d8c511790e0428e3400cf376d2ad49d87 verified identical between audited tree and guest /usr/src copy used for the build. Guest gcc 8.3 [DragonFly]; base gcc lacks -lasan/-lubsan (ASan build impossible on guest) - the guard-page canary provides equivalent deterministic overflow detection for this bounded-strcpy sink. Cumulative differential volume ~200M comparisons across 4 runs, 3 seeds: MISMATCH=0 everywhere; inet_pton round-trip clean. Only /tmp/ntop used on guest; no kernel changes; guest left up and clean.", "recommended_fix": "Add errno = EAFNOSUPPORT (default af case) and errno = ENOSPC (all three size-gate rejects) to sys/libkern/inet_ntop.c, matching the libc twin; see fix.diff.", "fix_status": "fixed", "fix_kernel_uname": "n/a (userland differential rig against the fixed TU; errno divergence requires no kernel rebuild to validate)", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (git apply --check clean) implements the errno contract: baseline TU leaves errno=0 on both failure modes (fixcheck BASE_RC=1); patched TU sets ENOSPC/EAFNOSUPPORT (FIXED_RC=0) and re-passes the full differential with MISMATCH=0 (run.3.log FIXED_DIFF_RC=0), i.e. formatting behavior unchanged.", "fix_evidence": "findings/poc/DF-5698/fix_run.log: variant=BASELINE ... errno=0(NOT-SET) BASE_RC=1 vs variant=FIXED ... errno=28(ENOSPC) ... errno=47(EAFNOSUPPORT) FIXED_RC=0; run.3.log: FIXED_DIFF_RC=0 (23,889,126 comparisons, MISMATCH=0); fix.diff; fix_build.log (FIXED_TU_BUILD_OK)." } |