DF-0055 / fix_run.log
================================================================
DF-0055 โ FIX VALIDATION: PoC on the single-fix kernel #1
================================================================
Patched kernel : DragonFly 6.5-DEVELOPMENT #1: Thu Jul 2 15:06:40 UTC 2026
sha256(/boot/kernel/kernel) = ac173e083fb91582c3a82c53de61a5a9a898582ed58c2c1701e34cc77403ed6f
Unpatched base : DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026
sha256(/boot/kernel/kernel) = 5dc83dac19ad09effd6241c33e0c0669d41b6497ee92d87d3a2e45f287bc22ad
Fix : findings/poc/DF-0055/fix.diff (move prop_object_release of
ev->ev.ev_dict from udev_event_externalize:571 into
udev_clean_events_locked before TAILQ_REMOVE + NULL the field)
Build : make -j6 nativekernel KERNCONF=X86_64_GENERIC -> rc=0
(full output: fix_build.log, 35573 lines, 0 errors)
The patched kernel carries the proplib _PROP_ASSERT(ocnt != 0) guard
(prop_object.c:1085) and the malloc chunk_mark_free double-free detector โ
i.e. the SAME diagnostics that caught the baseline panic on #0. So a
surviving UAF/double-free would fire identically. It does not.
----------------------------------------------------------------
RUN 1 (patched #1, 80 create/destroy cycles = 160 udev events)
----------------------------------------------------------------
[*] DF-0055: udev_event_externalize shared-dict UAF trigger
[*] forking two /dev/udev readers (two softcs/markers)...
[*] generating device attach/detach events (tap create/destroy)...
[*] each event is externalized by reader #1 (frees shared dict),
then by reader #2 (UAF: prop_object_retain on freed dict).
[!] no panic after 80 create/destroy cycles (160 udev events).
[!] On a non-INVARIANTS kernel the freed dict may have been
silently reused; check dmesg / boot.log for slab/objcache
warnings or delayed corruption. Run with more iterations or
an INVARIANTS kernel to force an immediate fault.
RUN1_EXIT=0 (ssh RC=0, guest UP)
----------------------------------------------------------------
RUN 2 (patched #1)
----------------------------------------------------------------
[*] DF-0055: udev_event_externalize shared-dict UAF trigger
[*] forking two /dev/udev readers (two softcs/markers)...
[*] generating device attach/detach events (tap create/destroy)...
[*] each event is externalized by reader #1 (frees shared dict),
then by reader #2 (UAF: prop_object_retain on freed dict).
[!] no panic after 80 create/destroy cycles (160 udev events).
[!] On a non-INVARIANTS kernel the freed dict may have been
silently reused; check dmesg / boot.log for slab/objcache
warnings or delayed corruption. Run with more iterations or
an INVARIANTS kernel to force an immediate fault.
RUN2_EXIT=0 (ssh RC=0, guest UP)
----------------------------------------------------------------
RUN 3 (patched #1)
----------------------------------------------------------------
[*] DF-0055: udev_event_externalize shared-dict UAF trigger
[*] forking two /dev/udev readers (two softcs/markers)...
[*] generating device attach/detach events (tap create/destroy)...
[*] each event is externalized by reader #1 (frees shared dict),
then by reader #2 (UAF: prop_object_retain on freed dict).
[!] no panic after 80 create/destroy cycles (160 udev events).
[!] On a non-INVARIANTS kernel the freed dict may have been
silently reused; check dmesg / boot.log for slab/objcache
warnings or delayed corruption. Run with more iterations or
an INVARIANTS kernel to force an immediate fault.
RUN3_EXIT=0 (ssh RC=0, guest UP)
================================================================
boot.log panic check (after all 3 runs, full file)
================================================================
$ grep -ciE "panic:|fatal trap|already free|ocnt != 0|Memory modified" boot.log
0
The only new boot.log lines are benign tap<N> MAC-address messages from the
create/destroy cycles (no kernel warnings of any kind).
================================================================
dmesg check (after all 3 runs)
================================================================
$ dmesg | grep -iE "slab|objcache|corrupt|freed chunk|modified|overflow|underflow"
(empty โ no slab/objcache/corruption warnings across 480 udev events)
================================================================
CONCLUSION โ FIX VALIDATED
================================================================
- BASELINE (#0 unpatched): PoC panics deterministically on the FIRST reader-2
event with `ocnt != 0` in prop_object_release (refcount-underflow form of
the shared-dict UAF). Guest goes DOWN. (see run.baseline.log)
- PATCHED (#1 single-fix): PoC runs to completion 3/3 times (480 udev events
total), exits 0, guest stays UP, ZERO panics/warnings in boot.log or dmesg.
The fix correctly serializes the single release of the shared event dict into
udev_clean_events_locked (after ALL readers have advanced past the event) and
removes the premature release from udev_event_externalize, so each reader's
externalize is now net-zero on ev_dict (retain on prop_dictionary_set, release
when the temp dict is dropped) and the final reap releases exactly once.
fix_status = fixed