# DF-5690 — prop_array_util nocopy variants: borrowed-pointer lifetime hazards (pass 2, sys/libprop/prop_array_util.c)

Info / hardening. The `_nocopy` string helpers exported by
`sys/libprop/prop_array_util.c` traffic in raw borrowed pointers with no
pinning and no kernel-appropriate ownership:

- `prop_array_get_cstring_nocopy()` (prop_array_util.c:205-218) returns a
  pointer *into* the prop_string's storage; replacing/removing the array
  slot (prop_array.c:650-657 releases the old object) or releasing the
  array frees the backing store while the caller still holds `*cpp`.
- `prop_array_set_cstring_nocopy()` (prop_array_util.c:220-235) builds the
  string via `prop_string_create_cstring_nocopy()` (prop_string.c:188-200),
  which stores a pointer to CALLER-owned memory, never copies or frees it
  (prop_string.c:85-87), and freezes `ps_size` at creation while
  `prop_string_cstring()`/externalization re-read the buffer to its
  *current* NUL (prop_string.c:302-304, 101-105).

In a kernel plist path this is a latent use-after-free read / kernel-heap
info-leak into copyout'd plists (externalize-to-user) and a heap-overflow
write (frozen `ps_size` vs current NUL). **Zero live kernel callers today**
(the only in-tree caller of the array get variant is inside `#if 0`,
dm_ioctl.c:273-316; no kernel caller of the set variant exists), hence Info.

## Reproduce (guest, root)

```
sh build.sh          # 3 same-source rigs from /usr/src/sys/libprop
sh run.sh            # matrix + nolife baseline + nolife fixed
```

## Expected

- matrix: `MATRIX: 811 checks, 0 failures, 6 known(DF-2240) rows` — the
  numeric get-side guard algebra, the type-guard discipline, OOB/NULL
  handling and set/add/externalize roundtrips are machine-confirmed clean.
- nolife BASELINE (page-guard rig): all expectations met —
  T1 dangling read TRAP, T2 externalize leaks the *reused* buffer content
  (`<string>PWNED-BY-BUFFER-REUSE</string>`), T2b freed-page read TRAP,
  T3 strcpy overrun TRAP, T4 control clean.
- nolife FIXED (+fix.diff): all expectations met — T2/T2b/T3 gone (frozen
  copy semantics), T1 still traps (documented caller contract: keep the
  parent array alive); control unchanged.

Files: df5690.c (harness), guardalloc.h (page-guard allocator),
fix.diff (kernel-build nocopy-create copies; rig activation via
-DPROP_RIG_FORCE_NOCOPY_COPY), build.log, run.log, run.2.log (3x
stability rerun), env.txt, VERDICT.md, manifest.json, verdict.json.
