{
  "finding_id": "DF-5690",
  "status": "reproduced",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "verdict": "prop_array_util.cs _nocopy helpers export borrowed-pointer semantics into _KERNEL builds: prop_array_get_cstring_nocopy (prop_array_util.c:205-218) returns an unpinned pointer into the prop_strings storage that dangles as soon as the slot is replaced/removed (prop_array.c:650-657) or the array released, and prop_array_set_cstring_nocopy (prop_array_util.c:220-235 -> prop_string.c:188-200) stores caller-owned memory forever (free skipped at prop_string.c:85-87) while ps_size is frozen at creation though externalize/prop_string_cstring re-read the buffer to its CURRENT NUL (prop_object.c:195-226, prop_string.c:302-304). Deterministically proven on the same-source page-guard rig: dangling read TRAPS (T1), freed-buffer read TRAPS (T2b), strcpy overrun into the ps_size+1 allocation TRAPS (T3), and externalize leaks whatever occupies the callers reused buffer at copyout time (<string>PWNED-BY-BUFFER-REUSE</string>, T2) - the prop_array_copyout_ioctl info-leak semantics. Kernel impact today is none reachable: the only in-tree kernel caller of the array get variant is #if 0 (dm_ioctl.c:273-316) and the set variant has zero kernel callers, so this is filed as Info hardening one caller away from a kernel UAF-read/leak/overflow class; same semantics ship in the userland libprop built from this source. The numeric get-side guard algebra and the full type-guard discipline of this file were simultaneously machine-confirmed clean (811-check differential matrix, 0 failures).",
  "exploit_chain": "",
  "evidence": "run.log section 2 (baseline): T1 ... TRAP(SIGSEGV) OK, T2 ... exit=0 OK with the leaked <string>PWNED-BY-BUFFER-REUSE</string> externalize output, T2b ... TRAP(SIGSEGV) OK, T3 ... TRAP(SIGSEGV) OK, control T4 clean; section 1: MATRIX: 811 checks, 0 failures, 6 known(DF-2240) rows; section 3 (fixed): NOLIFE(fixed): all expectations met with frozen <string>transient-buffer</string>. run.2.log: 3/3 stable reruns. build.log: md5-identical guest/host sources, redirected allocator macros, fix.diff applied.",
  "kernel_refs": [
    "sys/libprop/prop_array_util.c:205-218",
    "sys/libprop/prop_array_util.c:220-235",
    "sys/libprop/prop_array.h:145-155",
    "sys/libprop/prop_string.c:85-87",
    "sys/libprop/prop_string.c:188-200",
    "sys/libprop/prop_string.c:101-105",
    "sys/libprop/prop_string.c:302-304",
    "sys/libprop/prop_array.c:650-657",
    "sys/libprop/prop_object.c:195-226",
    "sys/dev/disk/dm/dm_ioctl.c:273-316"
  ],
  "poc_changes": "Authored fresh (no seed). Harness revisions: (1) matrix values initially not wrapped in <integer> tags (FATAL, fixed); (2) free-then-externalize T2 was non-deterministic because externalizes own mmap recycled the just-freed address zero-filled (observed as an empty <string></string> - itself evidence the read hits recycled heap); replaced by deterministic T2 buffer-reuse/leak and T2b no-intervening-alloc freed-read cases; (3) DF-2240 roundtrip rows reclassified from FAIL to KNOWN after tracing the add_uint signed-creator through externalize.",
  "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": 20,
  "guest_dirty": 0,
  "build_cmd": "sh /tmp/df5690/build.sh",
  "run_cmd": "sh /tmp/df5690/run.sh",
  "code_hash": "17bdba3de4e8d08708305849aad55d7d08bce8c2c787e83ad64a1a7807e928b6",
  "notes": "Reproduced refers to the memory-safety/leak primitives on the same source in the established userland rig (page-guard allocator; guest cc 8.3 lacks usable ASAN). impact=none because zero live kernel callers exist today (verified via rg over sys/); severity Info per DF-5682 precedent for one-caller-away hardening findings. Negative results machine-confirmed in the same run: get-side range guards exact, all type guards present, OOB/NULL clean, set/add refcount dance correct, DF-5678 unreachable via these helpers. Cross-ref for dm_ioctl.c re-audit: dm_check_version (dm_ioctl.c:1074-1086) ignores prop_array_get_uint32 failure leaving int dm_version[3] uninitialized. sys/ untouched; guest scratch removed; no panic, no kernel rebuild.",
  "recommended_fix": "Make prop_string_create_cstring_nocopy copy in _KERNEL builds (see fix.diff); callers of get_cstring_nocopy must keep the parent object alive or use the copy variant.",
  "fix_status": "fixed",
  "fix_kernel_uname": null,
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Fix validated on the same source in-rig (DF-5682 precedent; Info finding with zero kernel callers, pure C-level control-flow change on a path the rig compiles identically, so no kernel rebuild performed): baseline traps T2b/T3 and leaks T2; patched copy (exact fix.diff, activated by the rig-only -DPROP_RIG_FORCE_NOCOPY_COPY which is undefined in real builds) externalizes the frozen <string>transient-buffer</string>, reads intact content with no trap, and copies 15 bytes with no overrun; T1 still traps as the documented caller contract (get-side borrowing is an API contract, addressed by caller discipline/documentation, not the fix); T4 control unchanged in both.",
  "fix_evidence": "run.log section 2 vs section 3: T2b ... TRAP(SIGSEGV) -> T2b: NO TRAP, intact content \"transient-buffer\"; T3 ... TRAP(SIGSEGV) -> T3: NO TRAP copy=BBBBBBBBBBBBBBB... (len=15); NOLIFE(base): all expectations met and NOLIFE(fixed): all expectations met; fix.diff adds the _KERNEL copying branch at prop_string.c:188."
}