_prop_stack_push growth allocation is kmalloc(M_WAITOK) executed while callers hold the global proplib dictionary lockmgr (release walk) or deep per-object mtx chains (equals walk); and push-failure handling is kernel-dead code since M_WAITOK never returns NULL on DragonFly
Summary
The 17th+ walk level heap-allocates while the dict release walk holds the GLOBAL keysym-tree lockmgr exclusive and the equals walk holds up to 2xdepth per-object mtx: on the allocator slow path the thread blocks with those locks held - a system-wide stall of all proplib dictionary operations of pressure-gated duration (soft DoS, no hard deadlock), plus an undocumented sleepable-context requirement on release/equals. And kmalloc(M_WAITOK) never returns NULL on DFly (panics instead), so the push-failure->emergency-release machinery is kernel-dead code - corroborating DF-5677's reachability note. pre-reserve-outside-locks fix proposed (do NOT add M_NULLOK before DF-5677's fix lands).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5705 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 2.9 KB | β raw | |
| verdict.json | file | 3.0 KB | view raw |
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:
- Blocking allocation while proplib locks are held. The push sites run
under locks taken by the callers:
- release walk:
prop_object_releaseholds the typepot_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-objectstruct mtxshared 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 makesprop_object_equalsunsafe to call from any non-sleepable context (an uncodified API constraint). eelem == NULLis unreachable in the kernel. DragonFlykmallocwithM_WAITOKand withoutM_NULLOKnever 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"). Soprop_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_emergencymachinery (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_MALLOCis plainmallocand 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.
Fix verification
not_testableNo fix validation performed (finding untested; the hardening direction is multi-site, not a validated one-liner).
Confirmed kernel references
- sys/libprop/prop_stack.c:49-53
- sys/libprop/prop_object_impl.h:260-263
- sys/libprop/prop_object.c:1077-1094
- sys/libprop/prop_object.c:1174
- sys/libprop/prop_dictionary.c:380-383
- sys/libprop/prop_dictionary.c:397-409
- sys/libprop/prop_array.c:129-132
- sys/libprop/prop_array.c:240-243
- sys/kern/kern_slaballoc.c:873-877
- sys/kern/kern_slaballoc.c:1723-1724
Detail
Evidence (decisive lines)
README.md (full path:line analysis); no run artifacts (untested by design)
PoC changes
No PoC authored (hardening finding; PoC skipped per contract for Low severity).
Verified recommended fix
Grow the walk stack outside lock-held regions (pre-reserve push headroom before taking pot_lock in the release walk; same for the equals walk) and document prop_object_release/prop_object_equals as sleepable-context-only; keep M_WAITOK (never M_NULLOK) until the emergency path is fixed.
Verdict
Not PoC-verified (Low/hardening): the mechanics are source-certain but the manifestation requires orchestrated kernel memory pressure against a >=17-deep proplib dictionary free (global keysym lockmgr held across the M_WAITOK growth allocation at prop_stack.c:50 via prop_object.c:1077-1094 -> prop_dictionary.c:380) or a >=17-deep equals walk (per-object mtx chain held across the push at prop_object.c:1174). Impact ceiling is a memory-pressure-bounded global stall of all proplib dictionary operations (soft DoS, no hard deadlock: pageout takes no proplib locks) plus the undocumented sleepable-context requirement imposed on prop_object_release/prop_object_equals callers. Second, source-certain observation: DragonFly kmalloc(M_WAITOK) without M_NULLOK never returns NULL (kern_slaballoc.c:873-877 panics on limit, :1723-1724 panics on map exhaustion), so prop_stack.c:52-53 is kernel-dead code and the push-failure -> emergency-release machinery (prop_array.c:129-132, prop_dictionary.c:380-383) cannot fire from stack growth in-kernel β corroborating DF-5677's kernel-unreachable reachability note. Left untested deliberately: Low-severity hardening finding, honest manifestation requires an artificial pressure rig whose value would not change the classification.
No comments yet.