β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2225

_PROP_ONCE_RUN sets completion flag before calling init racing concurrent first-user into NULL-deref on uninitialized rb_tree

Summary

Kernel-mode _PROP_ONCE_RUN macro (prop_object_impl.h:303) atomically sets once-flag to 1 BEFORE calling init function f(). Concurrent thread losing cmpset sees flag==1 proceeds past guard without waiting for _prop_dict_init() to complete. Losing caller enters _prop_dict_keysym_alloc or _prop_dictionary_lock operating on global _prop_dict_keysym_tree (rbt_ops still NULL) and global mutex (struct lock not yet lockinitd) causing NULL pointer dereference panic in _prop_rb_tree_find. _prop_rb_tree_find (prop_rb.c:92-93) unconditionally dereferences rbto (NULL) to load compare function pointer panicking at small near-zero address. Userspace build uses pthread_once (correct) only kernel build has this bug. In practice first proplib call occurs during single-threaded boot closing window. Race exploitable when first use deferred to multi-threaded post-boot e.g. lazily-loaded kernel module with first two ioctl invocations on different CPUs simultaneously. Impact kernel panic DoS no privilege escalation or info leak. [VERIFIED pass2: window-state trigger Fatal trap 12 VA 0x8 at _prop_rb_tree_find+0x18; in-situ 6-thread race harness-limited, single-threaded boot closes the wild window; two-phase once fix-validated survivor waits for completer]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2225 Β· 12 files
FileTypeDescriptionSize
run.log β€” 819 B view raw
run_stock_m1_boot.log β€” 13.7 KB view raw
panic.txt β€” 470 B view raw
run_stock_m0_boot.log β€” 17.4 KB view raw
run_fixed_m1_boot.log β€” 13.1 KB view raw
fix.diff β€” 616 B view raw
dfh.c β€” 24.3 KB view raw
dfarm.c β€” 2.4 KB view raw
harness.diff β€” 2.2 KB view raw
build.sh β€” 139 B view raw
run.sh β€” 145 B view raw
env.txt β€” 234 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Stock: Fatal trap 12 VA 0x8 at _prop_rb_tree_find+0x18 on the window-arm trigger. Fixed kernel, same trigger: the loser waits ~250 ms for the delayed completer, once transitions 1->2, prop_dictionary_set completes, guest healthy; boot-state check prints once=2 (stock prints 1).

run_stock_m1_boot.log (panic) vs run_fixed_m1_boot.log (delayed init completed (once=2), shell continues); B1 boot control shows once=2 at first use.
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #8 DFHARNESS x86_64 (stock-behavior kernels #4/#6, fixed #8; base 6.5-DEVELOPMENT 600519)

Confirmed kernel references

Detail

Evidence (decisive lines)

panic.txt + run_stock_m1_boot.log: Stopped at _prop_rb_tree_find+0x18 movq 0x8(%r15),%rbx with fault VA 0x8 after the window-arm printout. run_stock_m0_boot.log: once==1 right after boot (window closed in practice). run_fixed_m1_boot.log: delayed init completed (once=2) and the caller survived and returned on the fixed kernel.

PoC changes

Original sketch had no runnable PoC. Built: kernel-compiled dfharness sysctl tests, prop_dictionary.c state hooks via guest-only harness.diff (window arm / delayed completer emulation), 6-CPU gated racer attempt; several scheduler iterations of the race harness (spin vs tsleep vs gated barrier) before settling on the deterministic window-state proof.

Verified recommended fix

Two-phase once flag in the kernel _PROP_ONCE_RUN: winner runs f() then stores 2; losers spin (cpu_pause) until 2.

Verdict

Mechanism certain: with the prop_dictionary globals in the exact mid-_PROP_ONCE_RUN winner state (flag set, lockinit/rb_tree_init not yet run), a concurrent first user of the real public API NULL-derefs in _prop_rb_tree_find (Fatal trap 12, VA 0x8, _prop_rb_tree_find+0x18). In-the-wild reachability stays speculative: the once flag is already consumed single-threaded at boot on this guest, and the two 6-thread live-race attempts wedged inside the test harness rather than producing an attributable record. Kernel-panic DoS only; no path to uid=0 (fixed-offset NULL read, nanosecond window).