diff options
| author | Prashanth Bhatta <bhattap@codeaurora.org> | 2016-08-20 20:02:45 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-08-27 12:09:16 -0700 |
| commit | 7d45313e05f7de3f210c09e40da5b438e397d880 (patch) | |
| tree | 24821536c7d4bb4326550a18439915d7e5ebc4eb | |
| parent | e8a0e0808d1862467d32aaee8982b5b6500ba89c (diff) | |
icnss: Handle wait being interrupted
When event needs to be processed synchronously, event posting
thread waits for the completion. After completion, result from
the event work is retrieved and event buffer would be freed. But
if waiting thread gets interrupted then wait_for_completion API
returns failure and it also frees the buffer posted for
processing. Event work queue may accesses the freed buffer and
crash the system.
Fix the issue by properly synchronizing event buffer free between
caller and event work by checking for return value of
wait_for_completion.
CRs-fixed: 1057180
Change-Id: Ic3968fd4c0232da6bc9a97d94376f540f62bd2e6
Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
| -rw-r--r-- | drivers/soc/qcom/icnss.c | 38 |
1 files changed, 31 insertions, 7 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index aff9683b394f..1993b8e43c3e 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -217,6 +217,8 @@ module_param(quirks, ulong, 0600); void *icnss_ipc_log_context; +#define ICNSS_EVENT_PENDING 2989 + enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_SERVER_ARRIVE, ICNSS_DRIVER_EVENT_SERVER_EXIT, @@ -486,6 +488,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, event->type = type; event->data = data; init_completion(&event->complete); + event->ret = ICNSS_EVENT_PENDING; event->sync = sync; spin_lock_irqsave(&penv->event_lock, flags); @@ -494,12 +497,26 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, penv->stats.events[type].posted++; queue_work(penv->event_wq, &penv->event_work); - if (sync) { - ret = wait_for_completion_interruptible(&event->complete); - if (ret == 0) - ret = event->ret; - kfree(event); + + if (!sync) + return ret; + + ret = wait_for_completion_interruptible(&event->complete); + + icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", + icnss_driver_event_to_str(type), type, penv->state, ret, + event->ret); + + spin_lock_irqsave(&penv->event_lock, flags); + if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) { + event->sync = false; + spin_unlock_irqrestore(&penv->event_lock, flags); + return ret; } + spin_unlock_irqrestore(&penv->event_lock, flags); + + ret = event->ret; + kfree(event); return ret; } @@ -2071,11 +2088,15 @@ static void icnss_driver_event_work(struct work_struct *work) penv->stats.events[event->type].processed++; + spin_lock_irqsave(&penv->event_lock, flags); if (event->sync) { event->ret = ret; complete(&event->complete); - } else - kfree(event); + continue; + } + spin_unlock_irqrestore(&penv->event_lock, flags); + + kfree(event); spin_lock_irqsave(&penv->event_lock, flags); } @@ -2372,6 +2393,9 @@ int icnss_register_driver(struct icnss_driver_ops *ops) ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER, true, ops); + if (ret == -ERESTARTSYS) + ret = 0; + out: return ret; } |
