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)
PoC verification
Evidence pack
findings/poc/DF-2260 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedConfirmed 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.
No comments yet.