6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Ahsan Atta [ Upstream commit 5c6f845e77ec35f9b7b047cc8f9789bf397cdd3e ] The service_table list is protected by service_lock when entries are added or removed (in adf_service_add() and adf_service_remove()), but several functions iterate over the list without holding this lock. A concurrent adf_service_register() or adf_service_unregister() call could modify the list during traversal, leading to list corruption or a use-after-free. Fix this by holding service_lock across all list_for_each_entry() iterations of service_table in adf_dev_init(), adf_dev_start(), adf_dev_stop(), adf_dev_shutdown(), adf_dev_restarting_notify(), adf_dev_restarted_notify(), and adf_error_notifier(). The lock ordering is safe: callers of the static helpers (adf_dev_up() and adf_dev_down()) acquire state_lock before service_lock, and no event_hld callback or service_lock holder ever acquires state_lock in the reverse order. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Signed-off-by: Ahsan Atta Co-developed-by: Maksim Lukoshkov Signed-off-by: Maksim Lukoshkov Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/intel/qat/qat_common/adf_init.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index 9c1b587a87e5b..718bedff9c16e 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -140,16 +140,19 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) * This is to facilitate any ordering dependencies between services * prior to starting any of the accelerators. */ + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { dev_err(&GET_DEV(accel_dev), "Failed to initialise service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); return 0; } @@ -212,16 +215,19 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev) adf_heartbeat_start(accel_dev); + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (service->event_hld(accel_dev, ADF_EVENT_START)) { dev_err(&GET_DEV(accel_dev), "Failed to start service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->start_status); } + mutex_unlock(&service_lock); clear_bit(ADF_STATUS_STARTING, &accel_dev->status); set_bit(ADF_STATUS_STARTED, &accel_dev->status); @@ -289,6 +295,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) qat_comp_algs_unregister(); clear_bit(ADF_STATUS_COMP_ALGS_REGISTERED, &accel_dev->status); + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (!test_bit(accel_dev->accel_id, service->start_status)) @@ -301,6 +308,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(accel_dev->accel_id, service->start_status); } } + mutex_unlock(&service_lock); if (hw_data->stop_timer) hw_data->stop_timer(accel_dev); @@ -349,6 +357,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) &accel_dev->status); } + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (!test_bit(accel_dev->accel_id, service->init_status)) @@ -360,6 +369,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) else clear_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); adf_heartbeat_shutdown(accel_dev); @@ -390,6 +400,7 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) struct service_hndl *service; struct list_head *list_itr; + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) @@ -397,6 +408,7 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -405,6 +417,7 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) struct service_hndl *service; struct list_head *list_itr; + mutex_lock(&service_lock); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) @@ -412,6 +425,7 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } -- 2.53.0