# DF-5705 — _prop_stack_push growth allocation is M_WAITOK under held proplib locks; push-failure paths are kernel-dead code

**File audited (pass 2):** `sys/libprop/prop_stack.c`

`_prop_stack_push()` grows the walk stack with
`_PROP_MALLOC(sizeof(*eelem), M_TEMP)` — `kmalloc(size, M_TEMP, M_WAITOK)`
in the kernel (`prop_stack.c:50`, `prop_object_impl.h:260`). Two consequences:

1. **Blocking allocation while proplib locks are held.** The push sites run
   under locks taken by the callers:
   - release walk: `prop_object_release` holds the type `pot_lock()` — for
     dictionaries the **global** keysym-tree lockmgr, exclusive
     (`prop_object.c:1077-1094` → `prop_dictionary.c:397-403,408` →
     `prop_dictionary.c:380` → `prop_stack.c:50`). A deep (≥17) dict free
     that hits the allocator slow path (new slab page / reclaim) blocks with
     the global proplib dictionary lock held → every concurrent proplib
     dictionary operation system-wide (udev event processing, DM ioctls)
     stalls until memory frees. Not a hard deadlock (the pageout daemon takes
     no proplib locks), but an unbounded-duration global stall gated only by
     memory pressure.
   - equals walk (≥17 deep): up to 2×depth per-object `struct mtx` shared
     locks are held across the push (`prop_object.c:1174` ←
     `prop_array.c:240-243`). Same pressure-dependent stall of the whole
     locked chain; also makes `prop_object_equals` unsafe to call from any
     non-sleepable context (an uncodified API constraint).
2. **`eelem == NULL` is unreachable in the kernel.** DragonFly `kmalloc`
   with `M_WAITOK` and without `M_NULLOK` never returns NULL — it blocks,
   and panics on exhaustion (`kern/kern_slaballoc.c:873-877` "malloc limit
   exceeded", `:1723-1724` "kernel_map ran out of space"). So
   `prop_stack.c:52-53` (`if (eelem == NULL) return false;`) is dead code
   in the kernel — the push-failure → `_PROP_OBJECT_FREE_FAILED` →
   `prop_object_release_emergency` machinery (prop_array.c:129-132,
   prop_dictionary.c:380-383) cannot be triggered by stack-growth failure
   in-kernel. This corroborates DF-5677's "kernel-unreachable today"
   reachability note for the emergency path (which is userland-relevant
   where `_PROP_MALLOC` is plain `malloc` and *can* fail).

No PoC: triggering requires orchestrating kernel memory pressure against a
≥17-deep proplib free/equals, and the impact ceiling is a pressure-bounded
global stall (DoS). Recorded as a hardening/robustness finding with
source-cited certainty of the mechanics.

**Hardening direction** (not a one-liner): grow the stack outside
lock-held regions (e.g. reserve headroom in the release walk before taking
`pot_lock`, or pre-size), and/or document `prop_object_release` /
`prop_object_equals` as sleepable-context-only APIs once any in-kernel
nested-equals caller appears. Do **not** switch to `M_WAITOK|M_NULLOK` until
the emergency path is fixed (DF-5677) — a NULL there arms the buggy
emergency mode.
