⬢ DragonFlyBSD Kernel Audit
DF-5240 / fix.diff
← back to finding ↓ download raw
--- 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;
 			}