Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd21a80b authored by Meng Wang's avatar Meng Wang Committed by Gerrit - the friendly Code Review server
Browse files

ASoC: dsp: add spin lock to protect ac



There is a possible use after free for the ac variable
that causes kernel panic. Add spinlock to fix race
condition to avoid this issue.

Change-Id: I71638c269f572ff1eaad87682fa000cd1dad407d
Signed-off-by: default avatarMeng Wang <mwang@codeaurora.org>
parent 688d7b16
Loading
Loading
Loading
Loading
+109 −29
Original line number Diff line number Diff line
@@ -92,8 +92,13 @@ struct asm_mmap {
};

static struct asm_mmap this_mmap;

struct audio_session {
	struct audio_client *ac;
	spinlock_t session_lock;
};
/* session id: 0 reserved */
static struct audio_client *session[ASM_ACTIVE_STREAMS_ALLOWED + 1];
static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1];

struct asm_buffer_node {
	struct list_head list;
@@ -545,8 +550,8 @@ static int q6asm_session_alloc(struct audio_client *ac)
{
	int n;
	for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
		if (!session[n]) {
			session[n] = ac;
		if (!(session[n].ac)) {
			session[n].ac = ac;
			return n;
		}
	}
@@ -554,24 +559,39 @@ static int q6asm_session_alloc(struct audio_client *ac)
	return -ENOMEM;
}

static bool q6asm_is_valid_audio_client(struct audio_client *ac)
static int q6asm_get_session_id_from_audio_client(struct audio_client *ac)
{
	int n;
	for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
		if (session[n] == ac)
			return 1;
		if (session[n].ac == ac)
			return n;
	}
	return 0;
}

static bool q6asm_is_valid_audio_client(struct audio_client *ac)
{
	return q6asm_get_session_id_from_audio_client(ac) ? 1 : 0;
}

static void q6asm_session_free(struct audio_client *ac)
{
	int session_id;
	unsigned long flags;

	pr_debug("%s: sessionid[%d]\n", __func__, ac->session);
	session_id = ac->session;
	rtac_remove_popp_from_adm_devices(ac->session);
	session[ac->session] = NULL;
	spin_lock_irqsave(&(session[session_id].session_lock), flags);
	session[ac->session].ac = NULL;
	ac->session = 0;
	ac->perf_mode = LEGACY_PCM_MODE;
	ac->fptr_cache_ops = NULL;
	ac->cb = NULL;
	ac->priv = NULL;
	kfree(ac);
	ac = NULL;
	spin_unlock_irqrestore(&(session[session_id].session_lock), flags);

	return;
}
@@ -1083,8 +1103,6 @@ void q6asm_audio_client_free(struct audio_client *ac)
	pr_debug("%s: APR De-Register\n", __func__);

/*done:*/
	kfree(ac);
	ac = NULL;
	mutex_unlock(&session_lock);

	return;
@@ -1219,6 +1237,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
	if (n <= 0) {
		pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n);
		mutex_unlock(&session_lock);
		kfree(ac);
		goto fail_session;
	}
	ac->session = n;
@@ -1295,7 +1314,6 @@ fail_apr2:
fail_apr1:
	q6asm_session_free(ac);
fail_session:
	kfree(ac);
	return NULL;
}

@@ -1310,11 +1328,11 @@ struct audio_client *q6asm_get_audio_client(int session_id)
		goto err;
	}

	if (!session[session_id]) {
	if (!(session[session_id].ac)) {
		pr_err("%s: session not active: %d\n", __func__, session_id);
		goto err;
	}
	return session[session_id];
	return session[session_id].ac;
err:
	return NULL;
}
@@ -1518,6 +1536,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
	uint32_t i = IN;
	uint32_t *payload;
	unsigned long dsp_flags;
	unsigned long flags;
	struct asm_buffer_node *buf_node = NULL;
	struct list_head *ptr, *next;
	union asm_token_struct asm_token;
@@ -1525,6 +1544,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
	struct audio_client *ac = NULL;
	struct audio_port_data *port;

	int session_id;

	if (!data) {
		pr_err("%s: Invalid CB\n", __func__);
		return 0;
@@ -1565,13 +1586,23 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
		rtac_clear_mapping(ASM_RTAC_CAL);
		return 0;
	}

	asm_token.token = data->token;
	ac = q6asm_get_audio_client(asm_token._token.session_id);
	session_id = asm_token._token.session_id;

	if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
		spin_lock_irqsave(&(session[session_id].session_lock), flags);

	ac = q6asm_get_audio_client(session_id);
	dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET);

	if (!ac) {
		pr_debug("%s: session[%d] already freed\n",
			 __func__, asm_token._token.session_id);
			 __func__, session_id);
		if ((session_id > 0 &&
			session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
			spin_unlock_irqrestore(
				&(session[session_id].session_lock), flags);
		return 0;
	}

@@ -1622,6 +1653,10 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
						__func__, payload[0]);
			break;
		}
		if ((session_id > 0 &&
			session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
			spin_unlock_irqrestore(
				&(session[session_id].session_lock), flags);
		return 0;
	}

@@ -1656,6 +1691,10 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
	if (ac->cb)
		ac->cb(data->opcode, data->token,
			data->payload, ac->priv);
	if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
		spin_unlock_irqrestore(
			&(session[session_id].session_lock), flags);

	return 0;
}

@@ -1723,6 +1762,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
	uint8_t buf_index;
	struct msm_adsp_event_data *pp_event_package = NULL;
	uint32_t payload_size = 0;
	unsigned long flags;
	int session_id;

	if (ac == NULL) {
		pr_err("%s: ac NULL\n", __func__);
@@ -1732,15 +1773,21 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
		pr_err("%s: data NULL\n", __func__);
		return -EINVAL;
	}
	if (!q6asm_is_valid_audio_client(ac)) {
		pr_err("%s: audio client pointer is invalid, ac = %pK\n",
				__func__, ac);

	session_id = q6asm_get_session_id_from_audio_client(ac);
	if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
		pr_err("%s: Session ID is invalid, session = %d\n", __func__,
			session_id);
		return -EINVAL;
	}

	if (ac->session <= 0 || ac->session > ASM_ACTIVE_STREAMS_ALLOWED) {
		pr_err("%s: Session ID is invalid, session = %d\n", __func__,
			ac->session);
	spin_lock_irqsave(&(session[session_id].session_lock), flags);

	if (!q6asm_is_valid_audio_client(ac)) {
		pr_err("%s: audio client pointer is invalid, ac = %pK\n",
				__func__, ac);
		spin_unlock_irqrestore(
			&(session[session_id].session_lock), flags);
		return -EINVAL;
	}

@@ -1753,7 +1800,6 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
	}

	if (data->opcode == RESET_EVENTS) {
		mutex_lock(&ac->cmd_lock);
		atomic_set(&ac->reset, 1);
		if (ac->apr == NULL) {
			ac->apr = ac->apr2;
@@ -1774,7 +1820,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
		wake_up(&ac->time_wait);
		wake_up(&ac->cmd_wait);
		wake_up(&ac->mem_wait);
		mutex_unlock(&ac->cmd_lock);
		spin_unlock_irqrestore(
			&(session[session_id].session_lock), flags);
		return 0;
	}

@@ -1788,6 +1835,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
	    (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) {
		if (payload == NULL) {
			pr_err("%s: payload is null\n", __func__);
			spin_unlock_irqrestore(
				&(session[session_id].session_lock), flags);
			return -EINVAL;
		}
		dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
@@ -1813,6 +1862,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
		ret = q6asm_is_valid_session(data, priv);
		if (ret != 0) {
			pr_err("%s: session invalid %d\n", __func__, ret);
			spin_unlock_irqrestore(
				&(session[session_id].session_lock), flags);
			return ret;
		}
		case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2:
@@ -1852,6 +1903,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
								payload[1]);
					wake_up(&ac->cmd_wait);
				}
				spin_unlock_irqrestore(
					&(session[session_id].session_lock),
					flags);
				return 0;
			}
			if ((is_adsp_reg_event(payload[0]) >= 0) ||
@@ -1882,6 +1936,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
					atomic_set(&ac->mem_state, payload[1]);
					wake_up(&ac->mem_wait);
				}
				spin_unlock_irqrestore(
					&(session[session_id].session_lock),
					flags);
				return 0;
			}
			if (atomic_read(&ac->mem_state) && wakeup_flag) {
@@ -1929,6 +1986,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
							__func__, payload[0]);
			break;
		}

		spin_unlock_irqrestore(
			&(session[session_id].session_lock), flags);
		return 0;
	}

@@ -1942,6 +2002,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
			if (port->buf == NULL) {
				pr_err("%s: Unexpected Write Done\n",
								__func__);
				spin_unlock_irqrestore(
					&(session[session_id].session_lock),
					flags);
				return -EINVAL;
			}
			spin_lock_irqsave(&port->dsp_lock, dsp_flags);
@@ -1956,6 +2019,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
					__func__, payload[0], payload[1]);
				spin_unlock_irqrestore(&port->dsp_lock,
								dsp_flags);
				spin_unlock_irqrestore(
					&(session[session_id].session_lock),
					flags);
				return -EINVAL;
			}
			port->buf[buf_index].used = 1;
@@ -2026,6 +2092,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
		if (ac->io_mode & SYNC_IO_MODE) {
			if (port->buf == NULL) {
				pr_err("%s: Unexpected Write Done\n", __func__);
				spin_unlock_irqrestore(
					&(session[session_id].session_lock),
					flags);
				return -EINVAL;
			}
			spin_lock_irqsave(&port->dsp_lock, dsp_flags);
@@ -2145,7 +2214,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
	if (ac->cb)
		ac->cb(data->opcode, data->token,
			data->payload, ac->priv);

	spin_unlock_irqrestore(
		&(session[session_id].session_lock), flags);
	return 0;
}

@@ -2321,11 +2391,16 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac)
static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
			uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id)
{
	unsigned long flags;

	dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n",
			__func__, pkt_size, cmd_flg, ac->session, stream_id);
	mutex_lock(&ac->cmd_lock);
	spin_lock_irqsave(&(session[ac->session].session_lock), flags);
	if (ac->apr == NULL) {
		pr_err("%s: AC APR handle NULL", __func__);
		spin_unlock_irqrestore(
			&(session[ac->session].session_lock), flags);
		mutex_unlock(&ac->cmd_lock);
		return;
	}
@@ -2348,6 +2423,8 @@ static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
				   WAIT_CMD);

	hdr->pkt_size  = pkt_size;
	spin_unlock_irqrestore(
		&(session[ac->session].session_lock), flags);
	mutex_unlock(&ac->cmd_lock);
	return;
}
@@ -9266,7 +9343,7 @@ int q6asm_get_apr_service_id(int session_id)
		return -EINVAL;
	}

	return ((struct apr_svc *)session[session_id]->apr)->id;
	return ((struct apr_svc *)(session[session_id].ac)->apr)->id;
}

int q6asm_get_asm_topology(int session_id)
@@ -9277,12 +9354,12 @@ int q6asm_get_asm_topology(int session_id)
		pr_err("%s: invalid session_id = %d\n", __func__, session_id);
		goto done;
	}
	if (session[session_id] == NULL) {
	if (session[session_id].ac == NULL) {
		pr_err("%s: session not created for session id = %d\n",
		       __func__, session_id);
		goto done;
	}
	topology = session[session_id]->topology;
	topology = (session[session_id].ac)->topology;
done:
	return topology;
}
@@ -9295,12 +9372,12 @@ int q6asm_get_asm_app_type(int session_id)
		pr_err("%s: invalid session_id = %d\n", __func__, session_id);
		goto done;
	}
	if (session[session_id] == NULL) {
	if (session[session_id].ac == NULL) {
		pr_err("%s: session not created for session id = %d\n",
		       __func__, session_id);
		goto done;
	}
	app_type = session[session_id]->app_type;
	app_type = (session[session_id].ac)->app_type;
done:
	return app_type;
}
@@ -9643,7 +9720,10 @@ static int __init q6asm_init(void)
	int lcnt, ret;
	pr_debug("%s:\n", __func__);

	memset(session, 0, sizeof(session));
	memset(session, 0, sizeof(struct audio_session) *
		(ASM_ACTIVE_STREAMS_ALLOWED + 1));
	for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++)
		spin_lock_init(&(session[lcnt].session_lock));
	set_custom_topology = 1;

	/*setup common client used for cal mem map */