⬢ DragonFlyBSD Kernel Audit
DF-3961 / trigger.c
← back to finding ↓ download raw
/*
 * DF-3961 trigger sketch - hptiop.c SG-capacity heap overflow
 * ============================================================
 * Sink:   hptiop_post_req_itl/mv/mvfrey write nsegs scatter-gather entries
 *         into ((struct hpt_iop_request_scsi_command *)srb)->sg_list,
 *         i.e. into the fixed 600-byte srb->req[] buffer (hptiop.h:42,452).
 *         Capacity: offsetof(sg_list)=48; (600-48)/16 = 34 entries.
 *         Entry k writes bytes [48+16k, 48+16k+16):
 *           k=34 -> [592,608) overwrites srb->hba   (offset 600)
 *           k=35 -> [608,624) overwrites srb->ccb, srb->next
 *           k=36 -> [624,640) overwrites srb->dma_map, srb->phy_addr
 *           k=37 -> [640,656) overwrites srb->srb_flag, srb->index
 *           k>=39 -> walks into the NEXT srb (stride HPT_SRB_MAX_SIZE=672)
 * Bound:  the ONLY gate is hptiop_post_scsi_command():
 *           if (error || nsegs > hba->max_sg_count)   // hptiop.c:2660
 *         with hba->max_sg_count taken verbatim from the HBA firmware
 *         get_config reply (hptiop.c:1933). No check against the 34-entry
 *         capacity, and io_dmat is created with nsegments=max_sg_count
 *         (hptiop.c:1952), so the DMA layer will happily produce that many.
 *
 * Precondition (both required):
 *   1. HBA firmware reports max_sg_count >= 35 in the get_config reply
 *      (device-fed; a compromised/misbehaving HBA or DMA attacker controls it;
 *       what stock RR3xxx/4xxx firmware reports is undocumented).
 *   2. A CAM initiator supplies a CCB yielding >= 35 segments:
 *        - bus_dmamap_load path is capped by MAXPHYS=128K on x86_64
 *          (scsi_da.c:1248 pins si_iosize_max=MAXPHYS) -> max ~34 segments,
 *          JUST under the limit, so plain disk I/O does not overflow;
 *        - the CAM_SCATTER_VALID path (hptiop.c:2329-2342) passes
 *          ccb->csio.sglist_cnt straight through as nsegs - no in-tree
 *          initiator sets it on this system, but any kernel component
 *          (or future/out-of-tree periph) that does can supply 35+.
 *
 * Values written per entry (hptiop.c:2475-2479):
 *   psg->pci_address = (u64)segs[idx].ds_addr  - physical address of the
 *                      I/O buffer pages (semi-controlled by the originator)
 *   psg->size        = segs[idx].ds_len        - attacker-chosen length
 *   psg->eot         = 0
 * Corrupted srb->ccb then reaches `ccb = (union ccb *)srb->ccb` and
 * xpt_done(ccb) in the completion callbacks -> wild kernel pointer.
 *
 * This PoC cannot run on the audit guest (no HighPoint HBA, see env.txt).
 * On hardware, the minimal trigger is:
 *   - emulate/reflash an RR44xx so get_config returns max_sg_count=64, then
 *   - issue one CCB with 35+ scatter segments to any hptiop target, e.g.
 *     via a small kernel module calling xpt_action with a hand-built csio
 *     (CAM_SCATTER_VALID|CAM_SG_LIST_PHYS), or
 *   - dd if=/dev/daX of=/dev/null bs=1m on a kernel whose MAXPHYS exceeds
 *     136 KB (34 * 4K), which yields >= 35 segments from the load path.
 */
int main(void) { return 0; }  /* informational sketch - see VERDICT.md */