DF-5240 / fix.diff
--- a/sys/bus/cam/cam_xpt.c 2026-10-01 14:56:29.592194264 +0000 +++ b/sys/bus/cam/cam_xpt.c 2026-10-01 14:56:29.608194060 +0000 @@ -5391,9 +5391,40 @@ kprintf("xpt_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); - kfree(scan_info, M_CAMXPT); + /* + * If XPT_SCAN_LUN CCBs have already been + * issued, they are owned by their probe + * peripherals and still reference scan_info + * via ccb_h.ppriv_ptr0 -- and work_ccb is + * then the most recently issued SCAN_LUN + * CCB, NOT the PATH_INQ CCB. Freeing + * either of them here is a use-after-free. + * + * When nothing has been issued yet (i == 0) + * it is safe to free scan_info and the + * PATH_INQ CCB directly. Otherwise account + * for the CCBs that will never be issued so + * that the last completing CCB runs the + * normal 'done' cleanup exactly once; the + * done path also sees request_ccb == NULL + * and therefore does not re-complete the + * request CCB completed below. + */ + if (i == 0) { + kfree(scan_info, M_CAMXPT); + xpt_free_ccb(&work_ccb->ccb_h); + } else { + int notissued; + + notissued = max_target - i + 1; + if (initiator_id > i) + notissued--; + if (scan_info->counter > notissued) + scan_info->counter -= + notissued; + scan_info->request_ccb = NULL; + } request_ccb->ccb_h.status = status; - xpt_free_ccb(&work_ccb->ccb_h); xpt_done(request_ccb); break; } @@ -5506,8 +5537,15 @@ xpt_free_ccb(&scan_info->cpi->ccb_h); request_ccb = scan_info->request_ccb; kfree(scan_info, M_CAMXPT); - request_ccb->ccb_h.status = CAM_REQ_CMP; - xpt_done(request_ccb); + /* + * request_ccb is NULL when the scan was + * aborted mid-loop (it was already + * completed by the aborting thread). + */ + if (request_ccb != NULL) { + request_ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(request_ccb); + } break; } |