⬢ DragonFlyBSD Kernel Audit
DF-5676 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5676",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "OOB read past the NUL sentinel in _prop_object_internalize_context_alloc MATCH() is real and realized: same-source userland rig crashes with SIGSEGV on a guard page placed at the sentinel (gcc-widened 4-byte load of ?xml at xml+1, cmpl $0x6c6d783f,0x1(%rbx) in the rig object; libc memcmp completes the vectorized read). In the shipped kernel the same MATCHes compile to bcmp calls whose aligned-longword implementation reads past the object only within the same page, cannot fault, and masks the result \u2014 no kernel impact on current codegen; latent UB that becomes a kernel OOB read if bcmp/memcmp is ever vectorized. Fixed and validated userland-side: baseline rig SIGSEGVs, fixed rig (sentinel pre-check + strncmp MATCH + per-byte EOF validation for the find_tag <!-- test) runs all probes clean.",
  "exploit_chain": "userland program calls prop_array_internalize/prop_dictionary_internalize on a heap buffer whose < lands within 15 bytes of the buffer end (e.g. plist string built in place): vectorized memcmp reads past the allocation -> SIGSEGV or silent heap overread; kernel ioctl paths (DM/vquotactl/tbridge) reach the same code but the kernel bcmp realization is page-contained and masked -> no kernel fault/leak on current codegen.",
  "evidence": "run.log (guard-page SIGSEGV exit 139 on baseline rig; fixed rig all probes clean exit 0; rig objdump showing cmpl $0x6c6d783f,0x1(%rbx)); kernel objdump of _prop_object_internalize_context_alloc at 0xffffffff809e1510 showing the two callq bcmp; sys/libkern/bcmp.c aligned-longword analysis in VERDICT.md.",
  "kernel_refs": [
    "sys/libprop/prop_object.c:733-761",
    "sys/libprop/prop_object.c:391-393",
    "sys/libkern/bcmp.c:57-110"
  ],
  "poc_changes": "Harness written fresh: guard-page mmap placement so the NUL sentinel is the last byte of a mapped page; three probes (sentinel-immediate, 5-byte prefix match, 9-byte prefix match) to cover byte-wise vs widened realizations.",
  "attempts": 2,
  "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": 300,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh  (guest root /tmp/df5676: same-source libprop rig + guard harness)",
  "run_cmd": "sh run.sh  (baseline probe, objdump codegen dump)",
  "code_hash": "ef4cc11132136ff843ffbc84c853dacc230017707323132c0133154ed0a78625",
  "notes": "Impact deliberately Low: kernel realization cannot fault (aligned 8-byte loads never cross pages; result masked) \u2014 but the C-level defect (memcmp range exceeds the object) is certain and the userland crash is reproduced. DF-2227 (the unbounded attr-value scan at prop_object.c:484-487) is a different, unbounded defect and was not re-reported.",
  "recommended_fix": "Validate xml[1] against EOF before MATCH and use strncmp (stops at NUL in either operand); validate cp[1]/cp[2] bytes individually in the find_tag <!-- test before the widenable comparison.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff (EOF pre-check + strncmp MATCH + per-byte <!-- validation) applied to the same-source rig on the guest: baseline SIGSEGVs on probe 1, fixed lib runs all three probes cleanly (exit 0). Kernel rebuild not performed \u2014 kernel-side realization is page-contained/masked (no observable kernel behavior to validate); the identical source change compiles in both builds (rig builds with the same headers).",
  "fix_evidence": "run.log tail: == baseline (expect SIGSEGV) == base exit=139 and == fixed (expect no fault) == ... no fault observed, fix exit=0; fix.diff"
}