DF-0055 / panic.txt
============================================================
DF-0055 โ kernel panic signatures (dfbsd-qemu/boot.log excerpts)
3/3 deterministic reproductions; two distinct-but-same-root-cause panics.
Both originate in udev_dev_read -> udev_event_externalize, the exact
function/lines cited in the finding (kern_udev.c:566/571).
============================================================
============================================================
RUN 1 โ double-free detected by malloc layer
============================================================
panic: memory chunk 0xfffff80067b36860 is already free!
cpuid = 1
Trace beginning at frame 0xfffff800ab6ec448
chunk_mark_free() at chunk_mark_free+0xae 0xffffffff806554ce
chunk_mark_free() at chunk_mark_free+0xae 0xffffffff806554ce
_kfree() at _kfree+0x262 0xffffffff806577f2
_prop_dictionary_free() at _prop_dictionary_free+0xe0 0xffffffff809dc290
prop_object_release() at prop_object_release+0xfd 0xffffffff809e0ebd
udev_dev_read() at udev_dev_read+0x14f 0xffffffff806636df
Debugger("panic")
CPU1 stopping CPUs: 0x00000001
stopped
Stopped at Debugger+0x7c: movb $0,0xbd77f9(%rip)
db>
Interpretation: Reader 1's udev_event_externalize() freed the shared
ev->ev.ev_dict (line 571). Reader 2's udev_event_externalize() re-derived
the same dangling pointer, prop_object_release() drove the stale refcount
to 0, _prop_dictionary_free() -> _kfree() tried to free an already-free
chunk; chunk_mark_free() caught the double-free and panicked.
============================================================
RUN 2 โ refcount underflow caught by proplib assertion
============================================================
panic: assertion "ocnt != 0" failed in prop_object_release at /usr/src/sys/libprop/prop_object.c:1085
cpuid = 1
Trace beginning at frame 0xfffff800ab9a74c8
prop_object_release() at prop_object_release+0x277 0xffffffff809e1037
prop_object_release() at prop_object_release+0x277 0xffffffff809e1037
udev_dev_read() at udev_dev_read+0x162 0xffffffff806636f2
dev_dread() at dev_dread+0xa3 0xffffffff8062c383
devfs_fo_read() at devfs_fo_read+0x100 0xffffffff80921930
kern_preadv() at kern_preadv()+0x1e5 0xffffffff806a6aa5
Debugger("panic")
CPU1 stopping CPUs: 0x00000001
stopped
Stopped at Debugger+0x7c: movb $0,0xbd77f9(%rip)
db>
Interpretation: Same UAF; the dangling ev_dict's stale refcount (after
reader 1's free + reader 2's retain-on-garbage) underflowed to 0xffffffff
on reader 2's release; the _PROP_ASSERT(ocnt != 0) guard at
prop_object.c:1085 fired. The two prop_object_release frames = the temp
`dict` releasing its "evdict" child (the dangling ev_dict).
============================================================
RUN 3 โ identical to RUN 2 (cpuid = 0 this time)
============================================================
panic: assertion "ocnt != 0" failed in prop_object_release at /usr/src/sys/libprop/prop_object.c:1085
cpuid = 0
Trace beginning at frame 0xfffff800aba354c8
prop_object_release() at prop_object_release+0x277 0xffffffff809e1037
prop_object_release() at prop_object_release+0x277 0xffffffff809e1037
udev_dev_read() at udev_dev_read+0x162 0xffffffff806636f2
dev_dread() at dev_dread+0xa3 0xffffffff8062c383
devfs_fo_read() at devfs_fo_read+0x100 0xffffffff80921930
kern_preadv() at kern_preadv()+0x1e5 0xffffffff806a6aa5
Debugger("panic")
CPU0 stopping CPUs: 0x00000002
stopped
Stopped at Debugger+0x7c: movb $0,0xbd77f9(%rip)
db>
============================================================
NOTE: panic happens after only the FIRST create/destroy cycle's event
reaches the second reader; the trigger is deterministic, not a tight race.
(udev_lk serializes the two reads; reader 2's marker is guaranteed to still
precede the event because reader 1 cannot advance reader 2's marker.)
============================================================