DF-3672 / verdict.json
{ "finding_id": "DF-3672", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-certain heap OOB read: the production MPT_TAG_2_REQ macro (mpt.h:360) indexes mpt->tgt_cmd_ptrs[tag_id >> 18] with no bounds check while the checked variant exists only under INVARIANTS (mpt.h:951-963). tag_id is a fully user-controlled u_int32 in the XPT_CONT_TARGET_IO CCB payload which pass(4) forwards to mpt_target_start_io (mpt_cam.c:4355); tag_id>>18 spans 0..16383 against an array of at most 256 entries, then the fetched value is dereferenced as request_t* (MPT_TGT_STATE -> req->req_vbuf) -> wild kernel read/panic, or a groomed arbitrary-address read oracle via mpt_tgt_dump_req_state. Not executable on the audit guest: mpt is compiled into X86_64_GENERIC (line 93) but the QEMU guest has zero vendor-0x1000 PCI devices, so no mpt softc ever attaches and no pass node on an mpt bus exists. Precondition check + full reasoning in VERDICT.md; expected impact on real FC target-mode hardware: local panic/DoS (operator-group user), potential info-leak oracle.", "exploit_chain": "operator-group user -> open /dev/passN on mpt FC target-mode bus -> CAMIOCOMMAND XPT_CONT_TARGET_IO with csio.tag_id=0xFFFC0000 -> xpt_merge_ccb copies payload -> mpt_target_start_io -> tgt_cmd_ptrs[1023] OOB read (array <=256 entries) -> garbage request_t* -> MPT_TGT_STATE double-deref -> wild kernel read -> panic; groomed heap variant -> default: branch -> mpt_tgt_dump_req_state prints attacker-addressed words to console (read oracle)", "evidence": "VERDICT.md (full path:line trace); env.txt + run.log (guest: mpt in GENERIC, 0 LSI PCI devices); mpt_tag_oob.c (trigger); fix.diff (unconditional bounds-checked accessor + NULL guards)", "kernel_refs": [ "sys/dev/disk/mpt/mpt.h:357-361", "sys/dev/disk/mpt/mpt.h:951-963", "sys/dev/disk/mpt/mpt_cam.c:4354-4356", "sys/dev/disk/mpt/mpt_cam.c:3711-3712", "sys/dev/disk/mpt/mpt_cam.c:4251-4256", "sys/dev/disk/mpt/mpt_cam.c:1343", "sys/dev/disk/mpt/mpt_cam.c:1652", "sys/bus/cam/scsi/scsi_pass.c:473-478", "sys/bus/cam/cam_xpt.c:3888-3900" ], "poc_changes": "n/a (pass-1 seed absent; authored from scratch: single CAMIOCOMMAND with XPT_CONT_TARGET_IO and tag_id 0xFFFC0000)", "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": 42, "guest_dirty": 0, "build_cmd": "cc -O -o mpt_tag_oob mpt_tag_oob.c", "run_cmd": "./mpt_tag_oob /dev/passN (blocked: no mpt hardware on guest; presence probe run instead)", "code_hash": "bf9398b90ef42780465c5268ffb8e644b1f2fd8cc868fb8d773057ec1f0d1b42", "notes": "Guest check: device mpt at sys/config/X86_64_GENERIC:93 (compiled in), kldstat -v shows mpt linked into the kernel, pciconf shows 0 vendor-0x1000 devices => driver never attaches => runtime-unreachable on this guest only. Same unchecked macro pattern exists in FreeBSD head (verified against upstream mpt_cam.c/mpt.h) \u2014 reportable upstream as well.", "recommended_fix": "Make the bounds-checked mpt_tag_2_req() inline unconditional (return NULL on out-of-range tag / missing array / NULL slot) and NULL-guard every MPT_TAG_2_REQ call site, completing the CCB with CAM_REQ_CMP_ERR", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored against read-only sys/ (unconditional checked accessor + call-site guards). Cannot build-and-run a single-fix kernel validation: the bug cannot be triggered even on the baseline guest (no MPT hardware), so baseline-vs-patched comparison is meaningless here. fix.diff is compile-consistent by inspection.", "fix_evidence": "fix.diff; absence of hardware documented in env.txt" } |