From: Ahsan Atta <ahsan.atta@intel.com> mainline inclusion from mainline-v7.2-rc1 commit 5c6f845e77ec35f9b7b047cc8f9789bf397cdd3e category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/16601 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- 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 <ahsan.atta@intel.com> Co-developed-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com> Signed-off-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com> Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Conflicts: drivers/crypto/qat/qat_common/adf_init.c [There is no adf_error_notifier in 5.10 and file is drivers/crypto/qat/qat_common/adf_init.c instead of drivers/crypto/intel/qat/qat_common/adf_init.c] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- drivers/crypto/qat/qat_common/adf_init.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/crypto/qat/qat_common/adf_init.c b/drivers/crypto/qat/qat_common/adf_init.c index 5c78433d19d4..59dfde7f761b 100644 --- a/drivers/crypto/qat/qat_common/adf_init.c +++ b/drivers/crypto/qat/qat_common/adf_init.c @@ -116,16 +116,19 @@ 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); hw_data->enable_error_correction(accel_dev); ret = hw_data->enable_vf2pf_comms(accel_dev); @@ -163,16 +166,19 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) return -EFAULT; } + 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); @@ -218,6 +224,7 @@ void adf_dev_stop(struct adf_accel_dev *accel_dev) qat_asym_algs_unregister(); } + 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)) @@ -230,6 +237,7 @@ void adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(accel_dev->accel_id, service->start_status); } } + mutex_unlock(&service_lock); if (wait) msleep(100); @@ -276,6 +284,7 @@ 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)) @@ -287,6 +296,7 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev) else clear_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); hw_data->disable_iov(accel_dev); @@ -315,6 +325,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)) @@ -322,6 +333,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; } @@ -330,6 +342,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)) @@ -337,5 +350,6 @@ 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.18.0.huawei.25