Merge "dsp: adm: validate ADSP payload size before access"
diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c
index f6a5698..213fe80 100644
--- a/asoc/msm-lsm-client.c
+++ b/asoc/msm-lsm-client.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -195,7 +195,8 @@
}
static void lsm_event_handler(uint32_t opcode, uint32_t token,
- void *payload, void *priv)
+ void *payload, uint16_t client_size,
+ void *priv)
{
unsigned long flags;
struct lsm_priv *prtd = priv;
@@ -265,6 +266,12 @@
}
case LSM_SESSION_EVENT_DETECTION_STATUS:
+ if (client_size < 3 * sizeof(uint8_t)) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
status = (uint16_t)((uint8_t *)payload)[0];
payload_size = (uint16_t)((uint8_t *)payload)[2];
index = 4;
@@ -274,6 +281,12 @@
break;
case LSM_SESSION_EVENT_DETECTION_STATUS_V2:
+ if (client_size < 2 * sizeof(uint8_t)) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
status = (uint16_t)((uint8_t *)payload)[0];
payload_size = (uint16_t)((uint8_t *)payload)[1];
index = 2;
@@ -283,6 +296,12 @@
break;
case LSM_SESSION_EVENT_DETECTION_STATUS_V3:
+ if (client_size < 2 * (sizeof(uint32_t) + sizeof(uint8_t))) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
event_ts_lsw = ((uint32_t *)payload)[0];
event_ts_msw = ((uint32_t *)payload)[1];
status = (uint16_t)((uint8_t *)payload)[8];
@@ -321,12 +340,22 @@
prtd->event_status->payload_size = payload_size;
if (likely(prtd->event_status)) {
- memcpy(prtd->event_status->payload,
- &((uint8_t *)payload)[index],
- payload_size);
- prtd->event_avail = 1;
- spin_unlock_irqrestore(&prtd->event_lock, flags);
- wake_up(&prtd->event_wait);
+ if (client_size >= (payload_size + index)) {
+ memcpy(prtd->event_status->payload,
+ &((uint8_t *)payload)[index],
+ payload_size);
+ prtd->event_avail = 1;
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ wake_up(&prtd->event_wait);
+ } else {
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ dev_err(rtd->dev,
+ "%s: Failed to copy memory with invalid size = %d\n",
+ __func__, payload_size);
+ return;
+ }
} else {
spin_unlock_irqrestore(&prtd->event_lock, flags);
dev_err(rtd->dev,
diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c
index 093eef4..b2cbc2a 100644
--- a/asoc/sdm660-common.c
+++ b/asoc/sdm660-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -51,6 +51,18 @@
bool codec_reg_done;
+struct tdm_dai_data {
+ DECLARE_BITMAP(status_mask, 3);
+ u32 rate;
+ u32 channels;
+ u32 bitwidth;
+ u32 num_group_ports;
+ struct afe_clk_set clk_set; /* hold LPASS clock config. */
+ union afe_port_group_config group_cfg; /* hold tdm group config */
+ struct afe_tdm_port_config port_cfg; /* hold tdm config */
+};
+
+
/* TDM default config */
static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
{ /* PRI TDM */
@@ -2803,6 +2815,91 @@
}
EXPORT_SYMBOL(msm_mi2s_snd_shutdown);
+static int msm_get_tdm_mode(u32 port_id)
+{
+ int tdm_mode;
+
+ switch (port_id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ tdm_mode = TDM_PRI;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ tdm_mode = TDM_SEC;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ tdm_mode = TDM_TERT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ tdm_mode = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUINARY_TDM_RX:
+ case AFE_PORT_ID_QUINARY_TDM_TX:
+ tdm_mode = TDM_QUIN;
+ break;
+ default:
+ pr_err("%s: Invalid port id: %d\n", __func__, port_id);
+ tdm_mode = -EINVAL;
+ }
+ return tdm_mode;
+}
+
+int msm_tdm_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct tdm_dai_data *dai_data = dev_get_drvdata(cpu_dai->dev);
+ int tdm_mode = msm_get_tdm_mode(cpu_dai->id);
+
+ if (tdm_mode < 0) {
+ dev_err(rtd->card->dev, "%s: Invalid tdm_mode\n", __func__);
+ return tdm_mode;
+ }
+ dai_data->clk_set.enable = true;
+ ret = afe_set_lpass_clock_v2(cpu_dai->id, &dai_data->clk_set);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ if (pdata->mi2s_gpio_p[tdm_mode])
+ ret = msm_cdc_pinctrl_select_active_state(
+ pdata->mi2s_gpio_p[tdm_mode]);
+ return ret;
+}
+EXPORT_SYMBOL(msm_tdm_snd_startup);
+
+void msm_tdm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct tdm_dai_data *dai_data = dev_get_drvdata(cpu_dai->dev);
+ int tdm_mode = msm_get_tdm_mode(cpu_dai->id);
+ int ret;
+
+ if (tdm_mode < 0) {
+ dev_err(rtd->card->dev, "%s: Invalid tdm_mode\n", __func__);
+ return;
+ }
+ dai_data->clk_set.enable = false;
+ ret = afe_set_lpass_clock_v2(cpu_dai->id, &dai_data->clk_set);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ if (pdata->mi2s_gpio_p[tdm_mode])
+ msm_cdc_pinctrl_select_sleep_state(
+ pdata->mi2s_gpio_p[tdm_mode]);
+}
+EXPORT_SYMBOL(msm_tdm_snd_shutdown);
+
/* Validate whether US EU switch is present or not */
static int msm_prepare_us_euro(struct snd_soc_card *card)
{
diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h
index 3df6bf8..418bbf6 100644
--- a/asoc/sdm660-common.h
+++ b/asoc/sdm660-common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -134,6 +134,8 @@
void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream);
int msm_mi2s_snd_startup(struct snd_pcm_substream *substream);
void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+int msm_tdm_snd_startup(struct snd_pcm_substream *substream);
+void msm_tdm_snd_shutdown(struct snd_pcm_substream *substream);
int msm_common_snd_controls_size(void);
void msm_set_codec_reg_done(bool done);
#endif
diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c
index 3568ebb..66182dd 100644
--- a/asoc/sdm660-ext-dai-links.c
+++ b/asoc/sdm660-ext-dai-links.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -278,7 +278,9 @@
}
static struct snd_soc_ops msm_tdm_be_ops = {
- .hw_params = msm_tdm_snd_hw_params
+ .startup = msm_tdm_snd_startup,
+ .shutdown = msm_tdm_snd_shutdown,
+ .hw_params = msm_tdm_snd_hw_params,
};
static int msm_fe_qos_prepare(struct snd_pcm_substream *substream)
diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c
index ebcea54..b35179a 100644
--- a/asoc/sdm660-internal.c
+++ b/asoc/sdm660-internal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1890,7 +1890,9 @@
}
static struct snd_soc_ops msm_tdm_be_ops = {
- .hw_params = msm_tdm_snd_hw_params
+ .startup = msm_tdm_snd_startup,
+ .shutdown = msm_tdm_snd_shutdown,
+ .hw_params = msm_tdm_snd_hw_params,
};
static struct snd_soc_ops msm_wcn_ops = {
diff --git a/dsp/q6core.c b/dsp/q6core.c
index 2cf5117..ea4a169 100644
--- a/dsp/q6core.c
+++ b/dsp/q6core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -184,7 +184,7 @@
}
EXPORT_SYMBOL(q6core_send_uevent);
-static int parse_fwk_version_info(uint32_t *payload)
+static int parse_fwk_version_info(uint32_t *payload, uint16_t payload_size)
{
size_t ver_size;
int num_services;
@@ -197,6 +197,11 @@
* Based on this info, we copy the payload into core
* avcs version info structure.
*/
+ if (payload_size < 5 * sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, payload_size);
+ return -EINVAL;
+ }
num_services = payload[4];
if (num_services > VSS_MAX_AVCS_NUM_SERVICES) {
pr_err("%s: num_services: %d greater than max services: %d\n",
@@ -211,6 +216,12 @@
ver_size = sizeof(struct avcs_get_fwk_version) +
num_services * sizeof(struct avs_svc_api_info);
+ if (payload_size < ver_size) {
+ pr_err("%s: payload has invalid size %d, expected size %zu\n",
+ __func__, payload_size, ver_size);
+ return -EINVAL;
+ }
+
q6core_lcl.q6core_avcs_ver_info.ver_info =
kzalloc(ver_size, GFP_ATOMIC);
if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL)
@@ -247,6 +258,12 @@
payload1 = data->payload;
+ if (data->payload_size < 2 * sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+
switch (payload1[0]) {
case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS:
@@ -307,6 +324,11 @@
break;
}
case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
payload1 = data->payload;
pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n",
__func__, payload1[0]);
@@ -315,6 +337,11 @@
wake_up(&q6core_lcl.bus_bw_req_wait);
break;
case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
payload1 = data->payload;
q6core_lcl.param = payload1[0];
pr_debug("%s: Received ADSP get state response 0x%x\n",
@@ -325,6 +352,11 @@
wake_up(&q6core_lcl.bus_bw_req_wait);
break;
case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
payload1 = data->payload;
pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n",
__func__, payload1[0]);
@@ -337,7 +369,7 @@
pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n",
__func__);
payload1 = data->payload;
- ret = parse_fwk_version_info(payload1);
+ ret = parse_fwk_version_info(payload1, data->payload_size);
if (ret < 0) {
q6core_lcl.adsp_status = ret;
pr_err("%s: Failed to parse payload:%d\n",
diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c
index a702661..7511c46 100644
--- a/dsp/q6lsm.c
+++ b/dsp/q6lsm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -195,7 +195,8 @@
struct lsm_cmd_read_done read_done;
token = data->token;
- if (data->payload_size > sizeof(read_done)) {
+ if (data->payload_size > sizeof(read_done) ||
+ data->payload_size < 6 * sizeof(payload[0])) {
pr_err("%s: read done error payload size %d expected size %zd\n",
__func__, data->payload_size,
sizeof(read_done));
@@ -213,6 +214,7 @@
if (client->cb)
client->cb(data->opcode, data->token,
(void *)&read_done,
+ sizeof(read_done),
client->priv);
return 0;
} else if (data->opcode == APR_BASIC_RSP_RESULT) {
@@ -238,6 +240,11 @@
__func__, token, client->session);
return -EINVAL;
}
+ if (data->payload_size < 2 * sizeof(payload[0])) {
+ pr_err("%s: payload has invalid size[%d]\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
client->cmd_err_code = payload[1];
if (client->cmd_err_code)
pr_err("%s: cmd 0x%x failed status %d\n",
@@ -258,7 +265,7 @@
if (client->cb)
client->cb(data->opcode, data->token, data->payload,
- client->priv);
+ data->payload_size, client->priv);
return 0;
}
@@ -1492,6 +1499,8 @@
"proc 0x%x SID 0x%x\n", __func__, data->opcode,
data->reset_event, data->reset_proc, sid);
+ if (sid < LSM_MIN_SESSION_ID || sid > LSM_MAX_SESSION_ID)
+ pr_err("%s: Invalid session %d\n", __func__, sid);
apr_reset(lsm_common.apr);
lsm_common.apr = NULL;
atomic_set(&lsm_common.apr_users, 0);
@@ -1557,7 +1566,8 @@
}
if (client->cb)
client->cb(data->opcode, data->token,
- data->payload, client->priv);
+ data->payload, data->payload_size,
+ client->priv);
return 0;
}
diff --git a/dsp/q6voice.c b/dsp/q6voice.c
index 742f18d..e768278 100644
--- a/dsp/q6voice.c
+++ b/dsp/q6voice.c
@@ -7427,7 +7427,7 @@
static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv)
{
- uint32_t *ptr = NULL;
+ uint32_t *ptr = NULL, min_payload_size = 0;
struct common_data *c = NULL;
struct voice_data *v = NULL;
struct vss_evt_voice_activity *voice_act_update = NULL;
@@ -7498,7 +7498,7 @@
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
- if (data->payload_size) {
+ if (data->payload_size >= sizeof(ptr[0]) * 2) {
ptr = data->payload;
pr_debug("%x %x\n", ptr[0], ptr[1]);
@@ -7568,7 +7568,13 @@
} else if (data->opcode == VSS_IMEMORY_RSP_MAP) {
pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__);
- if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) {
+ if (data->payload_size < sizeof(ptr[0])) {
+ pr_err("%s: payload has invalid size[%d]\n", __func__,
+ data->payload_size);
+ return -EINVAL;
+ }
+
+ if (data->token == VOIP_MEM_MAP_TOKEN) {
ptr = data->payload;
if (ptr[0]) {
v->shmem_info.mem_handle = ptr[0];
@@ -7635,10 +7641,13 @@
pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__);
if (data->payload_size) {
+ min_payload_size = min_t(u32, (int)data->payload_size,
+ CVD_VERSION_STRING_MAX_SIZE);
version_rsp =
(struct vss_iversion_rsp_get_t *)data->payload;
memcpy(common.cvd_version, version_rsp->version,
- CVD_VERSION_STRING_MAX_SIZE);
+ min_payload_size);
+ common.cvd_version[min_payload_size - 1] = '\0';
pr_debug("%s: CVD Version = %s\n",
__func__, common.cvd_version);
diff --git a/include/dsp/q6lsm.h b/include/dsp/q6lsm.h
index efce3a6..be0e998 100644
--- a/include/dsp/q6lsm.h
+++ b/include/dsp/q6lsm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,7 +26,7 @@
#define LSM_MAX_NUM_CHANNELS 8
typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv);
+ uint32_t *payload, uint16_t client_size, void *priv);
struct lsm_sound_model {
dma_addr_t phys;
diff --git a/ipc/apr.c b/ipc/apr.c
index f1f3e49..2d3225f 100644
--- a/ipc/apr.c
+++ b/ipc/apr.c
@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/ipc_logging.h>
+#include <linux/of_device.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/scm.h>
#include <dsp/apr_audio-v2.h>
@@ -56,6 +57,7 @@
};
static bool apr_cf_debug;
+static struct delayed_work add_chld_dev_work;
#ifdef CONFIG_DEBUG_FS
static struct dentry *debugfs_apr_debug;
@@ -282,6 +284,17 @@
dispatch_event(opcode, APR_DEST_QDSP6);
}
+static void apr_add_child_devices(struct work_struct *work)
+{
+ int ret;
+
+ ret = of_platform_populate(apr_dev_ptr->of_node,
+ NULL, NULL, apr_dev_ptr);
+ if (ret)
+ dev_err(apr_dev_ptr, "%s: failed to add child nodes, ret=%d\n",
+ __func__, ret);
+}
+
static void apr_adsp_up(void)
{
if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
@@ -289,29 +302,8 @@
wake_up(&dsp_wait);
if (!is_child_devices_loaded) {
- struct platform_device *pdev;
- struct device_node *node;
- int ret;
-
- for_each_child_of_node(apr_dev_ptr->of_node, node) {
- pdev = platform_device_alloc(node->name, -1);
- if (!pdev) {
- dev_err(apr_dev_ptr, "%s: pdev memory alloc failed\n",
- __func__);
- return;
- }
- pdev->dev.parent = apr_dev_ptr;
- pdev->dev.of_node = node;
-
- ret = platform_device_add(pdev);
- if (ret) {
- dev_err(apr_dev_ptr,
- "%s: Cannot add platform device\n",
- __func__);
- platform_device_put(pdev);
- return;
- }
- }
+ schedule_delayed_work(&add_chld_dev_work,
+ msecs_to_jiffies(100));
is_child_devices_loaded = true;
}
}
@@ -1180,6 +1172,7 @@
apr_tal_init();
apr_dev_ptr = &pdev->dev;
+ INIT_DELAYED_WORK(&add_chld_dev_work, apr_add_child_devices);
return apr_debug_init();
}