DF-5676 / verdict.json
{ "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" } |