⬢ DragonFlyBSD Kernel Audit
DF-5682 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5682",
  "status": "reproduced",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "verdict": "prop_number_unsigned() (prop_number.c:379-384) is the only prop_number accessor missing the prop_object_is_number() guard its seven siblings have; on a type-confused argument it reads the pnv_is_unsigned bitfield at +40..43, i.e. 4 bytes entirely past the 40-byte struct _prop_string allocation (16-byte _prop_bool likewise). Deterministically proven on the same-source userland rig: with a guard-page heap, the guarded control accessor returns 0 without trapping while prop_number_unsigned((prop_number_t)string) SIGSEGVs on the guard page (exit 139), and the exact fix.diff (guard added) removes the trap (returns false, exit 0). Kernel impact today is none reachable: zero in-kernel callers outside libprop (only create-side, type-safe kern_udev.c:195,215), so this is filed as hardening/defense-in-depth - one future caller away from a kernel heap OOB read on user-influencible plist data.",
  "exploit_chain": "",
  "evidence": "run.log: baseline [*] control: guarded prop_number_integer_value(str) -> 0 (no trap) then [*] probe: UNguarded prop_number_unsigned(str) ... followed by --- baseline exit=139 (SIGSEGV on guard page); fixed variant prints -> 0 (NO TRAP) exit=0. Layout math in README.md (+40..43 vs 40-byte object).",
  "kernel_refs": [
    "sys/libprop/prop_number.c:49-62",
    "sys/libprop/prop_number.c:379-384",
    "sys/libprop/prop_number.c:432",
    "sys/libprop/prop_number.c:450",
    "sys/libprop/prop_string.c:40-50",
    "sys/libprop/prop_object_impl.h:364-365"
  ],
  "poc_changes": "Authored fresh (no seed): typeconf.c harness + pguard_object_impl.h (userland _PROP_POOL_GET/_PROP_POOL_PUT replaced by a page-guard allocator for deterministic OOB detection - guest base cc 8.3 has no usable ASAN runtime) + pfix_number.c (fix under test applied to a copy of the identical source).",
  "attempts": 1,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 1,
  "guest_dirty": 0,
  "build_cmd": "sh /tmp/build5682.sh  (cc -O2 -fPIC -I. -I/usr/src/sys each .c of two guard-page libprop copies from /usr/src/sys/libprop; link typeconf, typeconf_fix)",
  "run_cmd": "sh /tmp/run5682.sh",
  "code_hash": "bd31568c9d6e8485536af8d872705bd80fed8fb2b7ae54714c1235d617d495aa",
  "notes": "Reproduced refers to the memory-safety primitive (type-confused OOB read) on the same source in the established userland rig; impact=none because no in-kernel caller exists today (verified via rg over sys/). Same defect exists verbatim in userland libprop.so built from this source. Guard-page instrumentation touches only the scratch build copies; sys/ untouched.",
  "recommended_fix": "Add the prop_object_is_number() guard to prop_number_unsigned(), matching every sibling accessor (see fix.diff).",
  "fix_status": "fixed",
  "fix_kernel_uname": null,
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Fix validated on the same source in-rig: baseline (stock prop_number.c) traps SIGSEGV exit 139 on the guard page at the +40 read; patched copy (exact fix.diff applied) returns false with no trap, exit 0, control accessor unchanged. Pure C-level change on a userland-buildable path, representative of the kernel build.",
  "fix_evidence": "run.log sections 1 (baseline exit=139 after probe: UNguarded) and 2 (fixed: -> 0 (NO TRAP), exit=0); fix.diff = the guard added at prop_number.c:379-384; pfix_number.c is the patched copy used in the rig."
}