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

Commit ce7fbe81 authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan
Browse files

soc: qmi: dfc poison overwritten fix



Fix use-after-free due to access of qmi shared memory by dfc client
after the free.

A race condition can happen after qmi null pointer check but before
its variables get updated if functions run in different thread from
the change_link that could free the qmi memory anytime. Add the
rtnl_lock to protect the qmi get and access since qmi allocate/free
is also under rtnl_lock.

CRs-Fixed: 2326554
Change-Id: Ib65025e02e90edf7e2035ccb47c8c0da882b8c40
Acked-by: default avatarNing Cai <ncai@qti.qualcomm.com>
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent 021f6068
Loading
Loading
Loading
Loading
+23 −15
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct dfc_qmi_data {
	struct work_struct svc_arrive;
	struct qmi_handle handle;
	struct sockaddr_qrtr ssctl;
	struct svc_info svc;
	int index;
	int restart_state;
};
@@ -618,12 +619,11 @@ dfc_indication_register_req(struct qmi_handle *dfc_handle,
	return ret;
}

static int dfc_init_service(struct dfc_qmi_data *data, struct qmi_info *qmi)
static int dfc_init_service(struct dfc_qmi_data *data)
{
	int rc;

	rc = dfc_bind_client_req(&data->handle, &data->ssctl,
				 &qmi->fc_info[data->index].svc);
	rc = dfc_bind_client_req(&data->handle, &data->ssctl, &data->svc);
	if (rc < 0)
		return rc;

@@ -884,19 +884,26 @@ static void dfc_svc_init(struct work_struct *work)
						 svc_arrive);
	struct qmi_info *qmi;

	qmi = (struct qmi_info *)rmnet_get_qmi_pt(data->rmnet_port);
	if (!qmi)
		goto clean_out;

	rc = dfc_init_service(data, qmi);
	rc = dfc_init_service(data);
	if (rc < 0)
		goto clean_out;

	qmi->fc_info[data->index].dfc_client = (void *)data;
	trace_dfc_client_state_up(data->index,
				  qmi->fc_info[data->index].svc.instance,
				  qmi->fc_info[data->index].svc.ep_type,
				  qmi->fc_info[data->index].svc.iface_id);
			   data->svc.instance,
			   data->svc.ep_type,
			   data->svc.iface_id);

	rtnl_lock();
	qmi = (struct qmi_info *)rmnet_get_qmi_pt(data->rmnet_port);
	if (!qmi) {
		rtnl_unlock();
		goto clean_out;
	}

	qmi->dfc_clients[data->index] = (void *)data;
	rtnl_unlock();

	pr_info("Connection established with the DFC Service\n");
	return;

clean_out:
@@ -944,7 +951,7 @@ static struct qmi_msg_handler qmi_indication_handler[] = {
	{},
};

int dfc_qmi_client_init(void *port, int index, struct qmi_info *qmi)
int dfc_qmi_client_init(void *port, int index, struct svc_info *psvc)
{
	struct dfc_qmi_data *data;
	int rc = -ENOMEM;
@@ -956,6 +963,7 @@ int dfc_qmi_client_init(void *port, int index, struct qmi_info *qmi)
	data->rmnet_port = port;
	data->index = index;
	data->restart_state = 0;
	memcpy(&data->svc, psvc, sizeof(data->svc));

	data->dfc_wq = create_singlethread_workqueue("dfc_wq");
	if (!data->dfc_wq) {
@@ -974,7 +982,7 @@ int dfc_qmi_client_init(void *port, int index, struct qmi_info *qmi)

	rc = qmi_add_lookup(&data->handle, DFC_SERVICE_ID_V01,
			    DFC_SERVICE_VERS_V01,
			    qmi->fc_info[index].svc.instance);
			    psvc->instance);
	if (rc < 0) {
		pr_err("%s: failed qmi_add_lookup - rc[%d]\n", __func__, rc);
		goto err2;
@@ -1058,7 +1066,7 @@ void dfc_qmi_wq_flush(struct qmi_info *qmi)
	int i;

	for (i = 0; i < MAX_CLIENT_NUM; i++) {
		dfc_data = (struct dfc_qmi_data *)(qmi->fc_info[i].dfc_client);
		dfc_data = (struct dfc_qmi_data *)(qmi->dfc_clients[i]);
		if (dfc_data)
			flush_workqueue(dfc_data->dfc_wq);
	}
+13 −13
Original line number Diff line number Diff line
@@ -87,8 +87,8 @@ void *qmi_rmnet_has_dfc_client(struct qmi_info *qmi)
		return NULL;

	for (i = 0; i < MAX_CLIENT_NUM; i++) {
		if (qmi->fc_info[i].dfc_client)
			return qmi->fc_info[i].dfc_client;
		if (qmi->dfc_clients[i])
			return qmi->dfc_clients[i];
	}

	return NULL;
@@ -365,6 +365,7 @@ static int
qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
{
	int idx, rc, err = 0;
	struct svc_info svc;

	ASSERT_RTNL();

@@ -375,7 +376,7 @@ qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
	idx = (tcm->tcm_handle == 0) ? 0 : 1;

	if (!qmi) {
		qmi = kzalloc(sizeof(struct qmi_info), GFP_KERNEL);
		qmi = kzalloc(sizeof(struct qmi_info), GFP_ATOMIC);
		if (!qmi)
			return -ENOMEM;

@@ -383,20 +384,20 @@ qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
	}

	qmi->flag = tcm->tcm_ifindex;
	qmi->fc_info[idx].svc.instance = tcm->tcm_handle;
	qmi->fc_info[idx].svc.ep_type = tcm->tcm_info;
	qmi->fc_info[idx].svc.iface_id = tcm->tcm_parent;
	svc.instance = tcm->tcm_handle;
	svc.ep_type = tcm->tcm_info;
	svc.iface_id = tcm->tcm_parent;

	if (((tcm->tcm_ifindex & FLAG_DFC_MASK) == DFC_MODE_MULTIQ) &&
	    (qmi->fc_info[idx].dfc_client == NULL)) {
		rc = dfc_qmi_client_init(port, idx, qmi);
	    (qmi->dfc_clients[idx] == NULL)) {
		rc = dfc_qmi_client_init(port, idx, &svc);
		if (rc < 0)
			err = rc;
	}

	if ((tcm->tcm_ifindex & FLAG_POWERSAVE_MASK) &&
	    (idx == 0) && (qmi->wda_client == NULL)) {
		rc = wda_qmi_client_init(port, tcm->tcm_handle);
		rc = wda_qmi_client_init(port, &svc);
		if (rc < 0)
			err = rc;
	}
@@ -407,12 +408,11 @@ qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
static int
__qmi_rmnet_delete_client(void *port, struct qmi_info *qmi, int idx)
{

	ASSERT_RTNL();

	if (qmi->fc_info[idx].dfc_client) {
		dfc_qmi_client_exit(qmi->fc_info[idx].dfc_client);
		qmi->fc_info[idx].dfc_client = NULL;
	if (qmi->dfc_clients[idx]) {
		dfc_qmi_client_exit(qmi->dfc_clients[idx]);
		qmi->dfc_clients[idx] = NULL;
	}

	if (!qmi_rmnet_has_client(qmi)) {
+8 −13
Original line number Diff line number Diff line
@@ -51,11 +51,6 @@ struct svc_info {
	u32 iface_id;
};

struct fc_info {
	struct svc_info svc;
	void *dfc_client;
};

struct qos_info {
	u8 mux_id;
	struct net_device *real_dev;
@@ -74,7 +69,7 @@ struct flow_info {
struct qmi_info {
	int flag;
	void *wda_client;
	struct fc_info fc_info[MAX_CLIENT_NUM];
	void *dfc_clients[MAX_CLIENT_NUM];
	unsigned long ps_work_active;
	int ps_enabled;
};
@@ -109,7 +104,7 @@ qmi_rmnet_get_bearer_map(struct qos_info *qos_info, u8 bearer_id);

unsigned int qmi_rmnet_grant_per(unsigned int grant);

int dfc_qmi_client_init(void *port, int index, struct qmi_info *qmi);
int dfc_qmi_client_init(void *port, int index, struct svc_info *psvc);

void dfc_qmi_client_exit(void *dfc_data);

@@ -129,13 +124,13 @@ qmi_rmnet_get_flow_map(struct qos_info *qos_info,
}

static inline struct rmnet_bearer_map *
qmi_rmnet_get_bearer_map(struct qos_info *qos_info, uint8_t bearer_id)
qmi_rmnet_get_bearer_map(struct qos_info *qos_info, u8 bearer_id)
{
	return NULL;
}

static inline int
dfc_qmi_client_init(void *port, int modem, struct qmi_info *qmi)
dfc_qmi_client_init(void *port, int index, struct svc_info *psvc)
{
	return -EINVAL;
}
@@ -157,11 +152,11 @@ dfc_qmi_wq_flush(struct qmi_info *qmi)
#endif

#ifdef CONFIG_QCOM_QMI_POWER_COLLAPSE
int wda_qmi_client_init(void *port, uint32_t instance);
int wda_qmi_client_init(void *port, struct svc_info *psvc);
void wda_qmi_client_exit(void *wda_data);
int wda_set_powersave_mode(void *wda_data, uint8_t enable);
int wda_set_powersave_mode(void *wda_data, u8 enable);
#else
static inline int wda_qmi_client_init(void *port, uint32_t instance)
static inline int wda_qmi_client_init(void *port, struct svc_info *psvc)
{
	return -EINVAL;
}
@@ -170,7 +165,7 @@ static inline void wda_qmi_client_exit(void *wda_data)
{
}

static inline int wda_set_powersave_mode(void *wda_data, uint8_t enable)
static inline int wda_set_powersave_mode(void *wda_data, u8 enable)
{
	return -EINVAL;
}
+21 −14
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 *
 */

#include <linux/rtnetlink.h>
#include <linux/soc/qcom/qmi.h>
#include <soc/qcom/rmnet_qmi.h>
#define CREATE_TRACE_POINTS
@@ -23,6 +24,7 @@ struct wda_qmi_data {
	struct work_struct svc_arrive;
	struct qmi_handle handle;
	struct sockaddr_qrtr ssctl;
	struct svc_info svc;
};

static void wda_svc_config(struct work_struct *work);
@@ -255,8 +257,7 @@ static int wda_set_powersave_mode_req(void *wda_data, uint8_t enable)
	return ret;
}

static int wda_set_powersave_config_req(struct qmi_handle *wda_handle,
					struct qmi_info *qmi)
static int wda_set_powersave_config_req(struct qmi_handle *wda_handle)
{
	struct wda_qmi_data *data = container_of(wda_handle,
						 struct wda_qmi_data, handle);
@@ -283,8 +284,8 @@ static int wda_set_powersave_config_req(struct qmi_handle *wda_handle,
		goto out;
	}

	req->ep_id.ep_type = qmi->fc_info[0].svc.ep_type;
	req->ep_id.iface_id = qmi->fc_info[0].svc.iface_id;
	req->ep_id.ep_type = data->svc.ep_type;
	req->ep_id.iface_id = data->svc.iface_id;
	req->req_data_cfg_valid = 1;
	req->req_data_cfg = WDA_DATA_POWERSAVE_CONFIG_ALL_MASK_V01;
	ret = qmi_send_request(wda_handle, &data->ssctl, &txn,
@@ -319,20 +320,25 @@ static void wda_svc_config(struct work_struct *work)
						 svc_arrive);
	struct qmi_info *qmi;

	qmi = (struct qmi_info *)rmnet_get_qmi_pt(data->rmnet_port);
	if (!qmi)
		goto clean_out;

	if (wda_set_powersave_config_req(&data->handle, qmi) < 0) {
	if (wda_set_powersave_config_req(&data->handle) < 0) {
		pr_err("%s() failed, qmi handle pt: %p\n",
			__func__, &data->handle);
		goto clean_out;
	}

	trace_wda_client_state_up(qmi->fc_info[0].svc.instance,
				  qmi->fc_info[0].svc.ep_type,
				  qmi->fc_info[0].svc.iface_id);
	trace_wda_client_state_up(data->svc.instance,
				  data->svc.ep_type,
				  data->svc.iface_id);
	rtnl_lock();
	qmi = (struct qmi_info *)rmnet_get_qmi_pt(data->rmnet_port);
	if (!qmi) {
		rtnl_unlock();
		goto clean_out;
	}

	qmi->wda_client = (void *)data;
	rtnl_unlock();

	pr_info("Connection established with the WDA Service\n");
	return;

@@ -370,7 +376,7 @@ static struct qmi_ops server_ops = {
	.del_server = wda_svc_exit,
};

int wda_qmi_client_init(void *port, uint32_t instance)
int wda_qmi_client_init(void *port, struct svc_info *psvc)
{
	struct wda_qmi_data *data;
	int rc = 0;
@@ -392,6 +398,7 @@ int wda_qmi_client_init(void *port, uint32_t instance)
	}

	data->rmnet_port = port;
	memcpy(&data->svc, psvc, sizeof(data->svc));
	INIT_WORK(&data->svc_arrive, wda_svc_config);

	rc = qmi_handle_init(&data->handle,
@@ -404,7 +411,7 @@ int wda_qmi_client_init(void *port, uint32_t instance)
	}

	rc = qmi_add_lookup(&data->handle, WDA_SERVICE_ID_V01,
			    WDA_SERVICE_VERS_V01, instance);
			    WDA_SERVICE_VERS_V01, psvc->instance);
	if (rc < 0) {
		pr_err("%s(): Failed qmi_add_lookup, err: %d\n", __func__, rc);
		qmi_handle_release(&data->handle);