β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2260

XPT_DEV_MATCH reads num_patterns without bounding by pattern_buf_len: kernel heap OOB read

Summary

cam_periph_mapmem validates only pattern_buf_len non-zero and <=MAXPHYS. Never compares num_patterns*sizeof(struct dev_match_pattern) against pattern_buf_len. xpt_action XPT_DEV_MATCH iterates for(i=0;i<cdm->num_patterns;i++) cdm->patterns[i].type. xptbusmatch/xptdevicematch/xptperiphmatch read patterns[i].pattern.* fields. User sets pattern_buf_len=sizeof(struct dev_match_pattern) (one element) num_patterns=thousands kernel reads past mapped bp->b_data (pbuf MAXPHYS bytes) into adjacent kernel heap. Two impacts: (1) DoS reading past pbuf into unmapped kernel address space page-faults (2) info leak read values drive match decisions attacker crafts patterns observes matches infers neighbouring heap bytes. xpt0 mode 0600 root-only. XPT_DEV_MATCH has XPT_FC_XPT_ONLY rejected on pass devices. [VERIFIED pass2: reproduced on stock via real /dev/xpt0 - pattern_buf_len 56 with num_patterns 64 matches from stale OOB slots as a byte-inference oracle, num_patterns 0x20000000 panics at xpt_action+0x2b0; mapmem-bound plus clamp fix-validated EINVAL]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2260 Β· 5 files
FileTypeDescriptionSize
df_p1.c trigger-source userland xpt0 PoC (prime/oob/control/panic) 12.3 KB view raw
run_baseline_oob.log run-log OOB call matches pass0; control zero 1.1 KB view raw
panic_xpt_action.txt panic trap 12 at xpt_action+0x2b0 from patterns walk 2.3 KB view raw
fix.diff suggested-fix mapmem bound num_patterns by pattern_buf_len 825 B view raw
fix_xpt_clamp.diff suggested-fix defense-in-depth clamp in xpt_action position loop 461 B view raw

Fix verification

fixed
↓ fix.diffper-fix-DF-2260

Confirmed kernel references

Detail

Exploit chain

root ioctl on /dev/xpt0 -> XPT_DEV_MATCH -> unbounded patterns walk -> (a) stale-heap match oracle, (b) unmapped-KVA read -> kernel panic

Evidence (decisive lines)

run_baseline_oob.log (oracle: OOB matches pass0, control 0); panic_xpt_action.txt (trap 12 at xpt_action+0x2b0); df_p1.c

PoC changes

Authored df_p1.c from scratch (finding had no runnable PoC); first prime attempt failed due to pbuf LIFO rotation -- fixed by 8KB repeated-pattern priming and a 64-slot OOB window

Verified recommended fix

reject num_patterns exceeding pattern_buf_len/sizeof(struct dev_match_pattern) in cam_periph_mapmem and clamp the xpt_action position loop

Verdict

On the unmodified stock kernel, XPT_DEV_MATCH with pattern_buf_len covering one pattern and num_patterns=64 consumed stale primed bytes at patterns[1..63] (pass0 matches at slots 0/14/28 vs zero matches for the legal num_patterns=1 control), proving the heap OOB read and its match-result oracle; num_patterns=0x20000000 additionally page-faulted inside xpt_action (xpt_action+0x2b0) and panicked the kernel. Root-only (xpt0 0600 root:operator, group bits zero). Fix (mapmem bound + xpt clamp) A/B validated: EINVAL and zero OOB matches on the fixed kernel. A/B: Fixed kernel: OOB call and 0x20000000-pattern call both return EINVAL with zero matches; legal calls unaffected; no fault.