DF-0974 / fix.diff
diff --git a/sys/bus/cam/cam_xpt.c b/sys/bus/cam/cam_xpt.c index 0000000..1111111 100644 --- a/sys/bus/cam/cam_xpt.c +++ b/sys/bus/cam/cam_xpt.c @@ -2893,6 +2893,13 @@ if ((device->flags & CAM_DEV_UNCONFIGURED) != 0) return (1); + /* + * cur_entry may have been deregistered (callback NULLed) between + * task enqueue and now; skip dispatch if so. + */ + if (cur_entry->callback == NULL) + return (1); + xpt_compile_path(&path, NULL, device->target->bus->path_id, @@ -2921,6 +2928,10 @@ cur_entry = (struct async_node *)arg; + /* See note in xptsetasyncfunc(). */ + if (cur_entry->callback == NULL) + return (1); + xpt_compile_path(&path, /*periph*/NULL, bus->sim->path_id, CAM_TARGET_WILDCARD, @@ -3351,11 +3362,29 @@ */ added &= ~cur_entry->event_enable; if (csa->event_enable == 0) { + /* + * Mark the entry inert but DO NOT free it. + * + * A task pending in xpt_action_sasync_cb() may + * still hold a pointer to this entry (captured + * at task-creation time at line ~3383). Freeing + * here would yield a use-after-free when that + * task runs and dereferences task->data1. + * + * The entry is removed from the list so future + * traversals skip it; the callback pointer is + * NULLed so any in-flight task's dispatch + * becomes a no-op. The memory is intentionally + * leaked (bounded by the number of deregister + * calls) until struct async_node gains a proper + * refcount. + */ + cur_entry->event_enable = 0; + cur_entry->callback = NULL; SLIST_REMOVE(async_head, cur_entry, async_node, links); atomic_add_int( &csa->ccb_h.path->device->refcount, -1); - kfree(cur_entry, M_CAMXPT); } else { cur_entry->event_enable = csa->event_enable; } |