# DF-5691 — prop_dictionary_util.c `_nocopy` twins + typed-helper matrix

**File under audit:** `sys/libprop/prop_dictionary_util.c` (pass 2)
**Sibling findings:** DF-5690 (array `_nocopy` class, zero live kernel
callers), DF-2240 (array `add_uint` signed-creator), DF-5674
(prop_dictionary.c keysym).

## What this pack proves

1. **matrix** (`./df5691_plain matrix`) — every typed helper in the file
   (get/set int8/16/32/64, uint8/16/32/64, bool, cstring, get_dict,
   set_and_rel, immutable-dict rows, NULL-dict rows, wrong-type sweep)
   differentially checked against the derived guard contract. Expected:
   0 failures; the DF-2240 class is *absent* here (set_uintN uses the
   unsigned creator at prop_dictionary_util.c:167) and the matrix
   documents the known-good differential rows.
2. **nolife BASELINE** (`./df5691_guard nolife`) — the `_nocopy` twins'
   lifetime hazards on the page-guard allocator (fork per case,
   SIGSEGV = deterministic trap):
   - T1: `get_cstring_nocopy` borrow → same-key replace (the LIVE
     `dm_dev_status_ioctl` pattern, dm_ioctl.c:397→:409) → deref traps.
   - T1b: `dm_table_deps_ioctl` pattern (borrow name+uuid → replace BOTH
     keys, dm_ioctl.c:619-620) → deref traps.
   - T2: dict release while borrowed → trap.
   - T2b: `set_cstring_nocopy` → caller frees buffer → externalize reads
     freed page → trap.
   - T3: frozen `ps_size` vs current NUL → `prop_string_cstring` strcpy
     overruns its ps_size+1 allocation → trap.
   - T4/DML/NCD controls: copy variant fine; the EXACT in-tree dm
     sequence without post-replace deref does NOT trap (the in-tree
     dangle is real but latent — documented).
3. **nolife FIXED** (`./df5691_fixed nolife-fixed`) — same battery with
   fix.diff active (rig-only `-DPROP_RIG_FORCE_NOCOPY_COPY`): T2b/T3 no
   longer trap; T1/T1b/T2 still trap (documented caller-side borrow
   contract, same treatment as DF-5690 T1).

## Build / run (guest, root, /tmp/df5691)

```
sh build.sh     # 3 same-source libprop rigs: plain / guard / fixed
sh run.sh       # matrix + nolife baseline + nolife fixed
```

## Expected output

- `MATRIX: <n> checks, 0 failures, 1 known(DF-2240-diff) rows`
- baseline: T1/T1b/T2/T2b/T3 → `TRAP(SIGSEGV) OK`; T4/DML/NCD →
  `exit=0 OK`; `NOLIFE(base): all expectations met`
- fixed: T2b/T3 → `exit=0 OK` (frozen copy); `NOLIFE(fixed): all
  expectations met`
