dsp: asm: validate payload size before access

Payload size is not checked before payload access. Check size
to avoid out-of-boundary memory access.

Change-Id: Iaa39ee4ea5489bb5579e7b7d5dfada12d88c5809
Signed-off-by: Xiaojun Sang <xsang@codeaurora.org>
This commit is contained in:
Xiaojun Sang 2019-02-12 17:42:38 +08:00 committed by Gerrit - the friendly Code Review server
parent 3d4358fbe6
commit e8909e3e1a

View file

@ -1722,14 +1722,14 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
return 0;
}
if (data->payload_size > sizeof(int)) {
if (data->payload_size >= 2 * sizeof(uint32_t)) {
pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
__func__, payload[0], payload[1], data->opcode,
data->token, data->payload_size, data->src_port,
data->dest_port, asm_token._token.session_id, dir);
pr_debug("%s:Payload = [0x%x] status[0x%x]\n",
__func__, payload[0], payload[1]);
} else if (data->payload_size == sizeof(int)) {
} else if (data->payload_size == sizeof(uint32_t)) {
pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
__func__, payload[0], data->opcode,
data->token, data->payload_size, data->src_port,
@ -1743,7 +1743,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
case ASM_CMD_SHARED_MEM_MAP_REGIONS:
case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
case ASM_CMD_ADD_TOPOLOGIES:
if (payload[1] != 0) {
if (data->payload_size >= 2 * sizeof(uint32_t)
&& payload[1] != 0) {
pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n",
__func__, payload[0], payload[1],
asm_token._token.session_id);
@ -1761,8 +1762,12 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
wake_up(&ac->mem_wait);
dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
if (data->payload_size >= 2 * sizeof(uint32_t))
dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
__func__, payload[0], payload[1]);
else
dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
__func__, data->payload_size);
break;
default:
pr_debug("%s: command[0x%x] not expecting rsp\n",
@ -1791,8 +1796,13 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
break;
}
case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{
pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
__func__, payload[0], payload[1]);
if (data->payload_size >= 2 * sizeof(uint32_t))
pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
__func__, payload[0], payload[1]);
else
pr_debug("%s: Payload size of %d is less than expected.\n",
__func__, data->payload_size);
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
wake_up(&ac->mem_wait);
@ -1801,8 +1811,12 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
break;
}
default:
pr_debug("%s: command[0x%x]success [0x%x]\n",
__func__, payload[0], payload[1]);
if (data->payload_size >= 2 * sizeof(uint32_t))
pr_debug("%s: command[0x%x]success [0x%x]\n",
__func__, payload[0], payload[1]);
else
pr_debug("%s: Payload size of %d is less than expected.\n",
__func__, data->payload_size);
}
if (ac->cb)
ac->cb(data->opcode, data->token,
@ -1955,8 +1969,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
&(session[session_id].session_lock), flags);
return -EINVAL;
}
dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
__func__, payload[0], payload[1], data->opcode);
if (data->payload_size >= 2 * sizeof(uint32_t))
dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
__func__, payload[0], payload[1], data->opcode);
else
dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
__func__, data->payload_size);
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
switch (payload[0]) {
@ -2001,32 +2019,40 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
__func__, ac->session,
data->opcode, data->token,
payload[0], payload[1],
data->src_port, data->dest_port);
if (payload[1] != 0) {
if (data->payload_size >=
2 * sizeof(uint32_t) &&
payload[1] != 0) {
pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
__func__, ac->session,
data->opcode, data->token,
payload[0], payload[1],
data->src_port, data->dest_port);
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
__func__, payload[0], payload[1]);
__func__,
payload[0],
payload[1]);
if (wakeup_flag) {
if ((is_adsp_reg_event(payload[0]) >=
0) ||
(payload[0] ==
ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
(payload[0] ==
ASM_STREAM_CMD_SET_PP_PARAMS_V3))
atomic_set(&ac->cmd_state_pp,
payload[1]);
if ((is_adsp_reg_event(payload[0]) >= 0)
|| (payload[0] ==
ASM_STREAM_CMD_SET_PP_PARAMS_V2)
|| (payload[0] ==
ASM_STREAM_CMD_SET_PP_PARAMS_V3))
atomic_set(
&ac->cmd_state_pp,
payload[1]);
else
atomic_set(&ac->cmd_state,
payload[1]);
atomic_set(
&ac->cmd_state,
payload[1]);
wake_up(&ac->cmd_wait);
}
spin_unlock_irqrestore(
&(session[session_id].session_lock),
flags);
return 0;
} else {
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
}
if ((is_adsp_reg_event(payload[0]) >= 0) ||
(payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
@ -2048,11 +2074,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
(uint32_t *)data->payload, ac->priv);
break;
case ASM_CMD_ADD_TOPOLOGIES:
pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
__func__, payload[0], payload[1]);
if (payload[1] != 0) {
if (data->payload_size >=
2 * sizeof(uint32_t) &&
payload[1] != 0) {
pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
__func__, payload[0], payload[1]);
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
__func__, payload[0], payload[1]);
__func__, payload[0], payload[1]);
if (wakeup_flag) {
atomic_set(&ac->mem_state, payload[1]);
wake_up(&ac->mem_wait);
@ -2071,8 +2099,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
(uint32_t *)data->payload, ac->priv);
break;
case ASM_DATA_EVENT_WATERMARK: {
pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
__func__, payload[0], payload[1]);
if (data->payload_size >= 2 * sizeof(uint32_t))
pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
__func__, payload[0], payload[1]);
else
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
break;
}
case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
@ -2084,11 +2116,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
/* error or malformed APR packet. Otherwise */
/* response will be returned as */
/* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */
if (payload[1] != 0) {
pr_err("%s: ASM get param error = %d, resuming\n",
__func__, payload[1]);
rtac_make_asm_callback(ac->session, payload,
if (data->payload_size >= 2 * sizeof(uint32_t)) {
if (payload[1] != 0) {
pr_err("%s: ASM get param error = %d, resuming\n",
__func__, payload[1]);
rtac_make_asm_callback(ac->session,
payload,
data->payload_size);
}
} else {
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
}
break;
case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
@ -2096,11 +2134,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
__func__, ac->session,
data->opcode, data->token,
data->src_port, data->dest_port);
if (payload[1] != 0)
pr_err("%s: ASM get param error = %d, resuming\n",
__func__, payload[1]);
atomic_set(&ac->cmd_state_pp, payload[1]);
wake_up(&ac->cmd_wait);
if (data->payload_size >= 2 * sizeof(uint32_t)) {
if (payload[1] != 0)
pr_err("%s: ASM get param error = %d, resuming\n",
__func__, payload[1]);
atomic_set(&ac->cmd_state_pp, payload[1]);
wake_up(&ac->cmd_wait);
} else {
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
}
break;
default:
pr_debug("%s: command[0x%x] not expecting rsp\n",
@ -2116,9 +2159,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
switch (data->opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2:{
struct audio_port_data *port = &ac->port[IN];
dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
__func__, payload[0], payload[1],
data->token);
if (data->payload_size >= 2 * sizeof(uint32_t))
dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
__func__, payload[0], payload[1],
data->token);
else
dev_err(ac->dev, "%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
if (ac->io_mode & SYNC_IO_MODE) {
if (port->buf == NULL) {
pr_err("%s: Unexpected Write Done\n",
@ -2130,10 +2177,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
}
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
buf_index = asm_token._token.buf_index;
if (lower_32_bits(port->buf[buf_index].phys) !=
payload[0] ||
msm_audio_populate_upper_32_bits(
port->buf[buf_index].phys) != payload[1]) {
if (data->payload_size >= 2 * sizeof(uint32_t) &&
(lower_32_bits(port->buf[buf_index].phys) !=
payload[0] ||
msm_audio_populate_upper_32_bits(
port->buf[buf_index].phys) !=
payload[1])) {
pr_debug("%s: Expected addr %pK\n",
__func__, &port->buf[buf_index].phys);
pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n",
@ -2168,14 +2217,32 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
} else if (generic_get_data) {
generic_get_data->valid = 1;
if (generic_get_data->is_inband) {
pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
__func__, payload[1], payload[2], payload[3]);
generic_get_data->size_in_ints = payload[3]>>2;
for (i = 0; i < payload[3]>>2; i++) {
generic_get_data->ints[i] =
payload[4+i];
pr_debug("%s: ASM callback val %i = %i\n",
__func__, i, payload[4+i]);
if (data->payload_size >= 4 * sizeof(uint32_t))
pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
__func__,
payload[1],
payload[2],
payload[3]);
else
pr_err("%s: payload size of %x is less than expected.\n",
__func__,
data->payload_size);
if (data->payload_size >=
(4 + (payload[3]>>2))
* sizeof(uint32_t)) {
generic_get_data->size_in_ints =
payload[3]>>2;
for (i = 0; i < payload[3]>>2; i++) {
generic_get_data->ints[i] =
payload[4+i];
pr_debug("%s: ASM callback val %i = %i\n",
__func__, i,
payload[4+i]);
}
} else {
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
}
pr_debug("%s: callback size in ints = %i\n",
__func__,
@ -2262,11 +2329,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
data->src_port, data->dest_port);
break;
case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
__func__,
payload[0], payload[1], payload[2]);
ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) |
payload[1]);
if (data->payload_size >= 3 * sizeof(uint32_t)) {
dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
__func__,
payload[0], payload[1], payload[2]);
ac->time_stamp =
(uint64_t)(((uint64_t)payload[2] << 32) |
payload[1]);
} else {
dev_err(ac->dev, "%s: payload size of %x is less than expected.n",
__func__, data->payload_size);
}
if (atomic_cmpxchg(&ac->time_flag, 1, 0))
wake_up(&ac->time_wait);
break;
@ -2276,10 +2349,14 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
__func__, ac->session,
data->opcode, data->token,
data->src_port, data->dest_port);
pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
__func__,
payload[0], payload[1], payload[2],
payload[3]);
if (data->payload_size >= 4 * sizeof(uint32_t))
pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
__func__,
payload[0], payload[1], payload[2],
payload[3]);
else
pr_debug("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
break;
case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2:
q6asm_process_mtmx_get_param_rsp(ac, (void *) payload);
@ -2287,8 +2364,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
case ASM_STREAM_PP_EVENT:
case ASM_STREAM_CMD_ENCDEC_EVENTS:
case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE:
pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]",
__func__, payload[0], payload[1]);
if (data->payload_size >= 2 * sizeof(uint32_t))
pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]",
__func__, payload[0], payload[1]);
else
pr_debug("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
i = is_adsp_raise_event(data->opcode);
if (i < 0) {
spin_unlock_irqrestore(
@ -2300,6 +2381,14 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
* package is composed of event type + size + actual payload
*/
payload_size = data->payload_size;
if (payload_size > UINT_MAX -
sizeof(struct msm_adsp_event_data)) {
pr_err("%s: payload size = %d exceeds limit.\n",
__func__, payload_size);
spin_unlock(&(session[session_id].session_lock));
return -EINVAL;
}
pp_event_package = kzalloc(payload_size
+ sizeof(struct msm_adsp_event_data),
GFP_ATOMIC);
@ -2320,16 +2409,29 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
&(session[session_id].session_lock), flags);
return 0;
case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2:
pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
__func__, ac->session, payload[0], payload[2],
payload[1]);
if (data->payload_size >= 3 * sizeof(uint32_t))
pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
__func__, ac->session,
payload[0],
payload[2],
payload[1]);
else
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
wake_up(&ac->cmd_wait);
break;
case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
__func__, ac->session, payload[0], payload[2],
if (data->payload_size >= 3 * sizeof(uint32_t))
pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
__func__, ac->session,
payload[0], payload[2],
payload[1]);
if (payload[0] == 0) {
else
pr_err("%s: payload size of %x is less than expected.\n",
__func__, data->payload_size);
if (payload[0] == 0 &&
data->payload_size >=
2 * sizeof(uint32_t)) {
atomic_set(&ac->cmd_state, 0);
/* ignore msw, as a delay that large shouldn't happen */
ac->path_delay = payload[1];