DF-5718 / verdict.json
{ "finding_id": "DF-5718", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "Info/hardening finding from the pass-2 census of sys/libkern/strcpy.c: the netgraph text parser calls strcpy() on overlapping src/dst (ng_parse.c:1572 strcpy(p,p+1), :1621 strcpy(p,v); netgraph7 ng_parse.c:1764/1813), violating the __restrict contract both prototypes declare (sys/sys/libkern.h:131). Runtime proof: the verbatim kernel algorithm executed with the verbatim in-tree strcpy counts 5 overlapping invocations in a single parse and parses correctly (rig_intree). A block-store strcpy model (vectorized implementations store the final partial block whole, i.e. write past the NUL) applied to the exact kernel allocation (kmalloc(strlen(s+start)) with no +1, ng_parse.c:1559) SIGSEGVs on a guard page - i.e. an implementation-plausible strcpy swap turns this into a kernel heap OOB write (rig_block). DFlys own libc strcpy is byte-forward, so rig_libc (the swap control) shows no divergence on this platform, and the input path is root-gated (caps_priv_check SYSCAP_RESTRICTEDROOT, ng_socket.c:171-175): no unprivileged-reachable bug exists in the shipped kernel. The memmove treatment (fix.diff) is byte-identical (rig_fixed). gcc 8.3 -Wrestrict does not diagnose the call sites (wrestrict_shim.c).", "exploit_chain": "", "evidence": "findings/poc/DF-5718/run.log: INTREE: overlapping strcpy invocations ... : 5; BLOCK : child killed by SIGSEGV ... => equivalent to a kernel heap OOB write; FIXED : memmove treatment output identical to in-tree semantics: YES; env.txt for guest/toolchain; VERDICT.md for the full narrative", "kernel_refs": [ "sys/libkern/strcpy.c:36", "sys/sys/libkern.h:131", "sys/netgraph/netgraph/ng_parse.c:1559", "sys/netgraph/netgraph/ng_parse.c:1572", "sys/netgraph/netgraph/ng_parse.c:1621", "sys/netgraph7/netgraph/ng_parse.c:1764", "sys/netgraph7/netgraph/ng_parse.c:1813", "sys/netgraph/socket/ng_socket.c:171", "sys/dev/raid/hptrr/osm.h:168" ], "poc_changes": "The seed had no PoC; authored from scratch. The rig embeds the VERBATIM ng_get_string_token algorithm and the VERBATIM in-tree strcpy; three iterations were needed: (1) nested block-comment syntax error; (2) non-numeric MODE preprocessor tokens enabled all arms at once; (3) initial expected string was wrong because the kernels hex-escape loop never increments k (consumes ALL consecutive hex digits, ng_parse.c:1601-1616) - fixed expected to the real kernel semantics and fixed the overlap counters range check. Added MODE_FIXED (fix.diff treatment) and a hexdump printer so invisible bytes are visible.", "attempts": 3, "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": 640, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -O2 -fno-builtin -DMODE=1 -o rig_intree overlap_rig.c; cc -O2 -DMODE=2 -o rig_libc overlap_rig.c; cc -O2 -DMODE=3 -o rig_block overlap_rig.c; cc -O2 -DMODE=4 -o rig_fixed overlap_rig.c; cc -O2 -Wall -Wrestrict -c wrestrict_shim.c)", "run_cmd": "sh run.sh (./rig_intree; ./rig_libc; ./rig_block; ./rig_fixed)", "code_hash": "958cb664b43bb039f0319e2132f30f1df27b666cda02ed6884de452211e658e6", "notes": "Batch pass-2 audit of sys/libkern/{skpc,strcat,strcpy}.c; this is the sole finding of the batch (Info). skpc.c verified byte-identical to FreeBSDs reference implementation (freebsd-src release/9.3.0 sys/libkern/skpc.c, fetched during this run) with zero in-tree callers; strcat.c census found all consumers literal/bounded/root-gated. Adjacent non-finding observations recorded in VERDICT.md: ng_parse hex/octal escape loops never increment k (semantics quirk, bounded, FreeBSD-inherited); ng_parse.c:1559 alloc has zero slack for past-NUL-writing strcpy variants (demonstrated by rig_block). Guest untouched except /tmp scratch (cleaned); no panic; no kernel rebuild needed for an Info finding.", "recommended_fix": "Replace the four overlapping strcpy calls with overlap-safe memmove in both netgraph stacks (see fix.diff): memmove(p, p+1, strlen(p+1)+1) and memmove(p, v, strlen(v)+1).", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": null, "fix_verdict": "fix.diff authored (netgraph + netgraph7, strcpy->memmove at the four overlap sites) and validated at the algorithm level: rig_fixed produces byte-identical parser output to the in-tree semantics (run.log). A full patched-kernel build/reboot validation was not performed because the finding is Info/hardening with no kernel-reachable defect to baseline (input path is root-gated by caps_priv_check SYSCAP_RESTRICTEDROOT); recorded as not_testable at the kernel level.", "fix_evidence": "findings/poc/DF-5718/run.log line FIXED : memmove treatment output identical to in-tree semantics: YES; fix.diff" } |