nvmet_subsys_attr_tbkas_store() walks subsys->ctrls to force a reconnect while holding only nvmet_config_sem. The list is protected by subsys->lock: nvmet_alloc_ctrl() adds entries and nvmet_ctrl_free() removes them under that lock, and the controller free path does not take nvmet_config_sem. Writing attr_tbkas concurrently with a controller going away, for example on a keep-alive timeout or a fatal error, can therefore dereference a controller that has already been removed and freed. Take subsys->lock around the list walk, as nvmet_subsys_attr_qid_max_store() already does for the same loop. delete_ctrl() only schedules the asynchronous teardown, so it does not recurse into subsys->lock. Fixes: e98e45e9465b ("nvmet: Add traffic based keep alive support to configfs") Cc: stable@vger.kernel.org Signed-off-by: Shao Mingyin --- Tested on 7.3.0-next-20261030 (QEMU/TCG guest, KASAN) with the loop transport. One process hammers the attr_tbkas store while another connects and deletes loop controllers, so the walk over subsys->ctrls races with nvmet_ctrl_free(): unpatched patched general protection fault, no report in 60 s, KASAN wild-memory-access 0xdead... 39591 iterations in nvmet_subsys_attr_tbkas_store diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index b3a5242d06f9..c17f2c8ab2df 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -1466,8 +1466,10 @@ static ssize_t nvmet_subsys_attr_tbkas_store(struct config_item *item, subsys->tbkas = tbkas; /* Force reconnect */ + mutex_lock(&subsys->lock); list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) ctrl->ops->delete_ctrl(ctrl); + mutex_unlock(&subsys->lock); up_write(&nvmet_config_sem); return count;