From 22097757a8372dbea4a84ca7a0c2c7d501f44119 Mon Sep 17 00:00:00 2001 From: Anand Ravi Date: Mon, 23 Aug 2021 12:42:46 -0700 Subject: [PATCH] msm: camera: reqmgr: Fix pagefault in workqueue get task Protect taskpool memory free operation with workqueue lock to prevent use after free issues. CRs-Fixed: 3019429 Change-Id: I0282e01e093f80578c5058a41823036370f5a88a Signed-off-by: Anand Ravi --- drivers/cam_req_mgr/cam_req_mgr_workq.c | 62 ++++++++++++++----------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/drivers/cam_req_mgr/cam_req_mgr_workq.c b/drivers/cam_req_mgr/cam_req_mgr_workq.c index 917d527c0c..517af10c4e 100644 --- a/drivers/cam_req_mgr/cam_req_mgr_workq.c +++ b/drivers/cam_req_mgr/cam_req_mgr_workq.c @@ -153,21 +153,17 @@ int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, if (!task) { CAM_WARN(CAM_CRM, "NULL task pointer can not schedule"); - rc = -EINVAL; - goto end; + return -EINVAL; } workq = (struct cam_req_mgr_core_workq *)task->parent; if (!workq) { CAM_DBG(CAM_CRM, "NULL workq pointer suspect mem corruption"); - rc = -EINVAL; - goto end; + return -EINVAL; } if (task->cancel == 1 || atomic_read(&workq->flush)) { - cam_req_mgr_workq_put_task(task); - CAM_INFO(CAM_CRM, "task aborted and queued back to pool"); rc = 0; - goto end; + goto abort; } task->priv = priv; task->priority = @@ -175,11 +171,11 @@ int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, ? prio : CRM_TASK_PRIORITY_0; WORKQ_ACQUIRE_LOCK(workq, flags); - if (!workq->job) { - rc = -EINVAL; - WORKQ_RELEASE_LOCK(workq, flags); - goto end; - } + if (!workq->job) { + rc = -EINVAL; + WORKQ_RELEASE_LOCK(workq, flags); + goto abort; + } list_add_tail(&task->entry, &workq->task.process_head[task->priority]); @@ -191,7 +187,11 @@ int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, workq->workq_scheduled_ts = ktime_get(); queue_work(workq->job, &workq->work); WORKQ_RELEASE_LOCK(workq, flags); -end: + + return rc; +abort: + cam_req_mgr_workq_put_task(task); + CAM_INFO(CAM_CRM, "task aborted and queued back to pool"); return rc; } @@ -271,24 +271,32 @@ void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **crm_workq) { unsigned long flags = 0; struct workqueue_struct *job; + struct cam_req_mgr_core_workq *workq; + int i; - CAM_DBG(CAM_CRM, "destroy workque %pK", crm_workq); - if (*crm_workq) { - WORKQ_ACQUIRE_LOCK(*crm_workq, flags); - if ((*crm_workq)->job) { - job = (*crm_workq)->job; - (*crm_workq)->job = NULL; - WORKQ_RELEASE_LOCK(*crm_workq, flags); + if (crm_workq && *crm_workq) { + workq = *crm_workq; + CAM_DBG(CAM_CRM, "destroy workque %s", workq->workq_name); + WORKQ_ACQUIRE_LOCK(workq, flags); + /* prevent any processing of callbacks */ + atomic_set(&workq->flush, 1); + if (workq->job) { + job = workq->job; + workq->job = NULL; + WORKQ_RELEASE_LOCK(workq, flags); destroy_workqueue(job); - } else { - WORKQ_RELEASE_LOCK(*crm_workq, flags); + WORKQ_ACQUIRE_LOCK(workq, flags); } - /* Destroy workq payload data */ - kfree((*crm_workq)->task.pool[0].payload); - (*crm_workq)->task.pool[0].payload = NULL; - kfree((*crm_workq)->task.pool); - kfree(*crm_workq); + kfree(workq->task.pool[0].payload); + kfree(workq->task.pool); + + /* Leave lists in stable state after freeing pool */ + INIT_LIST_HEAD(&workq->task.empty_head); + for (i = 0; i < CRM_TASK_PRIORITY_MAX; i++) + INIT_LIST_HEAD(&workq->task.process_head[i]); *crm_workq = NULL; + WORKQ_RELEASE_LOCK(workq, flags); + kfree(workq); } }