DF-4369 / fix.diff
--- a/sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c +++ b/sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c @@ -408,10 +408,25 @@ static void hpt_bus_scan_cb(struct cam_p static int hpt_rescan_bus(void); +/* + * Timeout runs in softclock context with no lock held. os_cmddone() + * callout_stop()s the timer and then releases pCmd (ldm_free_cmds) and + * ext (cmdext_put); callout_stop() does NOT interlock against a timeout + * that is already in-progress on another CPU, so by the time we run the + * command may already be back on the LDM freelist -- and if it has been + * recycled (e.g. as a flush command whose priv points at a kernel stack + * variable, or an internal LDM command) pCmd->priv no longer identifies + * our OS_CMDEXT. Serialize with the completion paths (they hold the same + * lock, which is recursive) and re-validate priv before touching it. + */ static void hpt_timeout(void *arg) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; + PVBUS_EXT vbus_ext = ext->vbus_ext; - KdPrint(("pCmd %p timeout", pCmd)); + KdPrint(("pCmd %p timeout", pCmd)); - ldm_reset_vbus((PVBUS)ext->vbus_ext->vbus); + hpt_lock_vbus(vbus_ext); + /* Command still ours (not completed-and-recycled)? */ + if (pCmd->done == os_cmddone && (POS_CMDEXT)pCmd->priv == ext) + ldm_reset_vbus((PVBUS)vbus_ext->vbus); + hpt_unlock_vbus(vbus_ext); } static void os_cmddone(PCOMMAND pCmd) |