โฌข DragonFlyBSD Kernel Audit
DF-0055 / panic.txt
โ† back to finding โ†“ download raw
============================================================
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.)
============================================================