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

Commit b62b1338 authored by Hardik Arya's avatar Hardik Arya
Browse files

diag: Free global buffer properly after receiving write done



Currently diag apps buffer are not free properly after
receiving write done completion, which is leading to use
after free. The patch frees global hdlc and non_hdlc data
buffer from mempool properly after receiving write done.

Change-Id: If60de6be206ddd796a876dcf8060259cfd8a5424
Signed-off-by: default avatarHardik Arya <harya@codeaurora.org>
parent 692654ff
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -800,6 +800,7 @@ struct diagchar_dev {
	unsigned int poolsize_dci;
	unsigned int poolsize_user;
	spinlock_t diagmem_lock;
	wait_queue_head_t hdlc_wait_q;
	/* Buffers for masks */
	struct mutex diag_cntl_mutex;
	/* Members for Sending response */
@@ -907,6 +908,17 @@ extern struct diagchar_dev *driver;
extern int wrap_enabled;
extern uint16_t wrap_count;

struct diag_apps_data_t {
	void *buf;
	uint32_t len;
	int ctxt;
	uint8_t allocated;
	uint8_t flushed;
};

extern struct diag_apps_data_t hdlc_data;
extern struct diag_apps_data_t non_hdlc_data;

void diag_get_timestamp(char *time_str);
void check_drain_timer(void);
int diag_get_remote(int remote_info);
+83 −31
Original line number Diff line number Diff line
@@ -133,14 +133,8 @@ static int timer_in_progress;
static int diag_mask_clear_param = 1;
module_param(diag_mask_clear_param, int, 0644);

struct diag_apps_data_t {
	void *buf;
	uint32_t len;
	int ctxt;
};

static struct diag_apps_data_t hdlc_data;
static struct diag_apps_data_t non_hdlc_data;
struct diag_apps_data_t hdlc_data;
struct diag_apps_data_t non_hdlc_data;
static struct mutex apps_data_mutex;

#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
@@ -202,15 +196,22 @@ static void diag_drain_apps_data(struct diag_apps_data_t *data)
	if (!data || !data->buf)
		return;

	spin_lock_irqsave(&driver->diagmem_lock, flags);
	data->flushed = 1;
	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
			     data->ctxt);

	if (err) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
	if (err)
		diagmem_free(driver, data->buf, POOL_TYPE_HDLC);
		data->buf = NULL;
		data->len = 0;
		data->allocated = 0;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	}
}

void diag_update_user_client_work_fn(struct work_struct *work)
{
@@ -282,6 +283,7 @@ static void diag_mempool_init(void)
	diagmem_init(driver, POOL_TYPE_DCI);

	spin_lock_init(&driver->diagmem_lock);
	init_waitqueue_head(&driver->hdlc_wait_q);
}

static void diag_mempool_exit(void)
@@ -3087,31 +3089,47 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
	send.last = (void *)(buf + len - 1);
	send.terminate = 1;

	if (!data->buf)
	wait_event_interruptible(driver->hdlc_wait_q,
			(data->flushed == 0));
	if (!data->buf) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->buf = diagmem_alloc(driver, DIAG_MAX_HDLC_BUF_SIZE +
					APF_DIAG_PADDING,
					  POOL_TYPE_HDLC);
		if (!data->buf) {
			ret = PKT_DROP;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
			goto fail_ret;
		}
		data->allocated = 1;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	}

	if ((DIAG_MAX_HDLC_BUF_SIZE - data->len) <= max_encoded_size) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->flushed = 1;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
				     data->ctxt);
		if (err) {
			ret = -EIO;
			goto fail_free_buf;
		}
		data->buf = NULL;
		data->len = 0;
		wait_event_interruptible(driver->hdlc_wait_q,
			(data->flushed == 0));
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->buf = diagmem_alloc(driver, DIAG_MAX_HDLC_BUF_SIZE +
					APF_DIAG_PADDING,
					  POOL_TYPE_HDLC);
		if (!data->buf) {
			ret = PKT_DROP;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
			goto fail_ret;
		}
		data->allocated = 1;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	}

	enc.dest = data->buf + data->len;
@@ -3126,21 +3144,29 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
	 */
	if ((uintptr_t)enc.dest >= (uintptr_t)(data->buf +
					       DIAG_MAX_HDLC_BUF_SIZE)) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->flushed = 1;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
				     data->ctxt);
		if (err) {
			ret = -EIO;
			goto fail_free_buf;
		}
		data->buf = NULL;
		data->len = 0;
		wait_event_interruptible(driver->hdlc_wait_q,
			(data->flushed == 0));
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->buf = diagmem_alloc(driver, DIAG_MAX_HDLC_BUF_SIZE +
					APF_DIAG_PADDING,
					 POOL_TYPE_HDLC);
		if (!data->buf) {
			ret = PKT_DROP;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
			goto fail_ret;
		}
		data->allocated = 1;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);

		enc.dest = data->buf + data->len;
		enc.dest_last = (void *)(data->buf + data->len +
@@ -3154,23 +3180,27 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
			DIAG_MAX_HDLC_BUF_SIZE;

	if (pkt_type == DATA_TYPE_RESPONSE) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->flushed = 1;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
				     data->ctxt);
		if (err) {
			ret = -EIO;
			goto fail_free_buf;
		}
		data->buf = NULL;
		data->len = 0;
	}

	return PKT_ALLOC;

fail_free_buf:
	spin_lock_irqsave(&driver->diagmem_lock, flags);
	if (data->allocated)
		diagmem_free(driver, data->buf, POOL_TYPE_HDLC);
	data->buf = NULL;
	data->len = 0;
	data->allocated = 0;
	data->flushed = 0;
	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
fail_ret:
	return ret;
@@ -3196,33 +3226,47 @@ static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
		       __func__, buf, len);
		return -EIO;
	}

	wait_event_interruptible(driver->hdlc_wait_q,
			(data->flushed == 0));
	if (!data->buf) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->buf = diagmem_alloc(driver, DIAG_MAX_HDLC_BUF_SIZE +
					APF_DIAG_PADDING,
					  POOL_TYPE_HDLC);
		if (!data->buf) {
			ret = PKT_DROP;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
			goto fail_ret;
		}
		data->allocated = 1;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	}

	if ((DIAG_MAX_HDLC_BUF_SIZE - data->len) <= max_pkt_size) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->flushed = 1;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
				     data->ctxt);
		if (err) {
			ret = -EIO;
			goto fail_free_buf;
		}
		data->buf = NULL;
		data->len = 0;
		wait_event_interruptible(driver->hdlc_wait_q,
			(data->flushed == 0));

		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->buf = diagmem_alloc(driver, DIAG_MAX_HDLC_BUF_SIZE +
					APF_DIAG_PADDING,
					  POOL_TYPE_HDLC);
		if (!data->buf) {
			ret = PKT_DROP;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
			goto fail_ret;
		}
		data->allocated = 1;
		data->flushed = 0;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
	}

	header.start = CONTROL_CHAR;
@@ -3235,23 +3279,27 @@ static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
	*(uint8_t *)(data->buf + data->len) = CONTROL_CHAR;
	data->len += sizeof(uint8_t);
	if (pkt_type == DATA_TYPE_RESPONSE) {
		spin_lock_irqsave(&driver->diagmem_lock, flags);
		data->flushed = 1;
		spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
				     data->ctxt);
		if (err) {
			ret = -EIO;
			goto fail_free_buf;
		}
		data->buf = NULL;
		data->len = 0;
	}

	return PKT_ALLOC;

fail_free_buf:
	spin_lock_irqsave(&driver->diagmem_lock, flags);
	if (data->allocated)
		diagmem_free(driver, data->buf, POOL_TYPE_HDLC);
	data->buf = NULL;
	data->len = 0;
	data->allocated = 0;
	data->flushed = 0;
	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
fail_ret:
	return ret;
@@ -4358,8 +4406,12 @@ static int __init diagchar_init(void)
	driver->rsp_buf_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, TYPE_CMD);
	hdlc_data.ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_DATA, 1);
	hdlc_data.len = 0;
	hdlc_data.allocated = 0;
	hdlc_data.flushed = 0;
	non_hdlc_data.ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_DATA, 1);
	non_hdlc_data.len = 0;
	non_hdlc_data.allocated = 0;
	non_hdlc_data.flushed = 0;
	mutex_init(&driver->hdlc_disable_mutex);
	mutex_init(&driver->diagchar_mutex);
	mutex_init(&driver->diag_notifier_mutex);
+16 −3
Original line number Diff line number Diff line
@@ -1918,6 +1918,7 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt,
	int peripheral = -1;
	int type = -1;
	int num = -1;
	struct diag_apps_data_t *temp = NULL;

	if (!buf || len < 0)
		return -EINVAL;
@@ -1936,9 +1937,21 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt,
			diag_ws_on_copy(DIAG_WS_MUX);
		} else if (peripheral == APPS_DATA) {
			spin_lock_irqsave(&driver->diagmem_lock, flags);
			diagmem_free(driver, (unsigned char *)buf,
				     POOL_TYPE_HDLC);
			buf = NULL;
			if (hdlc_data.allocated)
				temp = &hdlc_data;
			else if (non_hdlc_data.allocated)
				temp = &non_hdlc_data;
			else
				DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
				"No apps data buffer is allocated to be freed\n");
			if (temp) {
				diagmem_free(driver, temp->buf, POOL_TYPE_HDLC);
				temp->buf = NULL;
				temp->len = 0;
				temp->allocated = 0;
				temp->flushed = 0;
				wake_up_interruptible(&driver->hdlc_wait_q);
			}
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		} else {
			pr_err_ratelimited("diag: Invalid peripheral %d in %s, type: %d\n",