drivers: soc: Synchronize apr callback and voice svc release

Issue is seen when apr callback is received while voice_svc_release
is in process of freeing the driver private data.
Avoid invalid access of private data pointer by putting
the callback and release functions in the same locked context.

Change-Id: I93af13cab0a3c7e653a9bc9fa7f4f86bfa0502df
Signed-off-by: smanag <smanag@codeaurora.org>
[haggertk: Backport to 3.4/msm8974]
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
This commit is contained in:
smanag 2017-11-14 14:57:57 +05:30 committed by Francescodario Cuzzocrea
parent b3fb83770e
commit 8ddb93e2b8

View file

@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
@ -68,7 +69,11 @@ static void *dummy_q6_mvm;
static void *dummy_q6_cvs;
dev_t device_num;
spinlock_t voicesvc_lock;
static bool is_released;
static int voice_svc_dummy_reg(void);
static int voice_svc_dummy_dereg(void);
static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data,
void *priv);
@ -82,6 +87,11 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
pr_err("%s: data or priv is NULL\n", __func__);
return -EINVAL;
}
spin_lock(&voicesvc_lock);
if (is_released) {
spin_unlock(&voicesvc_lock);
return 0;
}
prtd = (struct voice_svc_prvt*)priv;
@ -115,7 +125,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
GFP_ATOMIC);
if (response_list == NULL) {
pr_err("%s: kmalloc failed\n", __func__);
spin_unlock(&voicesvc_lock);
return -ENOMEM;
}
@ -139,6 +149,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
spin_unlock(&voicesvc_lock);
return 0;
}
@ -536,6 +547,21 @@ err:
return -EINVAL;
}
static int voice_svc_dummy_dereg(void)
{
pr_debug("%s\n", __func__);
if (dummy_q6_mvm != NULL) {
apr_deregister(dummy_q6_mvm);
dummy_q6_mvm = NULL;
}
if (dummy_q6_cvs != NULL) {
apr_deregister(dummy_q6_cvs);
dummy_q6_cvs = NULL;
}
return 0;
}
static int voice_svc_open(struct inode *inode, struct file *file)
{
struct voice_svc_prvt *prtd = NULL;
@ -559,6 +585,7 @@ static int voice_svc_open(struct inode *inode, struct file *file)
mutex_init(&prtd->response_mutex_lock);
file->private_data = (void*)prtd;
is_released = 0;
/* Current APR implementation doesn't support session based
* multiple service registrations. The apr_deregister()
* function sets the destination and client IDs to zero, if
@ -582,8 +609,18 @@ static int voice_svc_release(struct inode *inode, struct file *file)
return -EINVAL;
}
if (reg_dummy_sess) {
voice_svc_dummy_dereg();
reg_dummy_sess = 0;
}
mutex_destroy(&prtd->response_mutex_lock);
spin_lock(&voicesvc_lock);
kfree(file->private_data);
is_released = 1;
spin_unlock(&voicesvc_lock);
return 0;
}
@ -641,6 +678,7 @@ static int voice_svc_probe(struct platform_device *pdev)
goto add_err;
}
pr_debug("%s: Device created\n", __func__);
spin_lock_init(&voicesvc_lock);
goto done;
add_err: