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

Commit 682b0d2d authored by Hardik Arya's avatar Hardik Arya
Browse files

diag: Add protection while freeing diag mempool buffer



There is possibility of double free because of protection
is missing while freeing diag hdlc mempool buffer. The patch
adds protection for the same by taking spinlock.

Change-Id: I7d2176e05408df80c839e198994807663ce172e8
Signed-off-by: default avatarHardik Arya <harya@codeaurora.org>
parent 460b918c
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-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
@@ -347,8 +347,8 @@ static void diag_usb_write_done(struct diag_usb_info *ch,
		DIAG_LOG(DIAG_DEBUG_MUX, "partial write_done ref %d\n",
			 atomic_read(&entry->ref_count));
		diag_ws_on_copy_complete(DIAG_WS_MUX);
		spin_unlock_irqrestore(&ch->write_lock, flags);
		diagmem_free(driver, req, ch->mempool);
		spin_unlock_irqrestore(&ch->write_lock, flags);
		return;
	}
	DIAG_LOG(DIAG_DEBUG_MUX, "full write_done, ctxt: %d\n",
@@ -365,8 +365,8 @@ static void diag_usb_write_done(struct diag_usb_info *ch,
	buf = NULL;
	len = 0;
	ctxt = 0;
	spin_unlock_irqrestore(&ch->write_lock, flags);
	diagmem_free(driver, req, ch->mempool);
	spin_unlock_irqrestore(&ch->write_lock, flags);
}

static void diag_usb_notifier(void *priv, unsigned int event,
+1 −0
Original line number Diff line number Diff line
@@ -612,6 +612,7 @@ struct diagchar_dev {
	unsigned int poolsize_hdlc;
	unsigned int poolsize_dci;
	unsigned int poolsize_user;
	spinlock_t diagmem_lock;
	/* Buffers for masks */
	struct mutex diag_cntl_mutex;
	/* Members for Sending response */
+12 −4
Original line number Diff line number Diff line
/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2008-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
@@ -214,17 +214,19 @@ static void drain_timer_func(unsigned long data)
static void diag_drain_apps_data(struct diag_apps_data_t *data)
{
	int err = 0;
	unsigned long flags;

	if (!data || !data->buf)
		return;

	err = diag_mux_write(DIAG_LOCAL_PROC, data->buf, data->len,
			     data->ctxt);
	spin_lock_irqsave(&driver->diagmem_lock, flags);
	if (err)
		diagmem_free(driver, data->buf, POOL_TYPE_HDLC);

	data->buf = NULL;
	data->len = 0;
	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
}

void diag_update_user_client_work_fn(struct work_struct *work)
@@ -295,6 +297,8 @@ static void diag_mempool_init(void)
	diagmem_init(driver, POOL_TYPE_HDLC);
	diagmem_init(driver, POOL_TYPE_USER);
	diagmem_init(driver, POOL_TYPE_DCI);

	spin_lock_init(&driver->diagmem_lock);
}

static void diag_mempool_exit(void)
@@ -2891,6 +2895,7 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
	struct diag_apps_data_t *data = &hdlc_data;
	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
	unsigned long flags;
	/*
	 * The maximum encoded size of the buffer can be atmost twice the length
	 * of the packet. Add three bytes foe footer - 16 bit CRC (2 bytes) +
@@ -2995,10 +3000,11 @@ static int diag_process_apps_data_hdlc(unsigned char *buf, int len,
	return PKT_ALLOC;

fail_free_buf:
	spin_lock_irqsave(&driver->diagmem_lock, flags);
	diagmem_free(driver, data->buf, POOL_TYPE_HDLC);
	data->buf = NULL;
	data->len = 0;

	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
fail_ret:
	return ret;
}
@@ -3010,6 +3016,7 @@ static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
	int ret = PKT_DROP;
	struct diag_pkt_frame_t header;
	struct diag_apps_data_t *data = &non_hdlc_data;
	unsigned long flags;
	/*
	 * The maximum packet size, when the data is non hdlc encoded is equal
	 * to the size of the packet frame header and the length. Add 1 for the
@@ -3074,10 +3081,11 @@ static int diag_process_apps_data_non_hdlc(unsigned char *buf, int len,
	return PKT_ALLOC;

fail_free_buf:
	spin_lock_irqsave(&driver->diagmem_lock, flags);
	diagmem_free(driver, data->buf, POOL_TYPE_HDLC);
	data->buf = NULL;
	data->len = 0;

	spin_unlock_irqrestore(&driver->diagmem_lock, flags);
fail_ret:
	return ret;
}
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2008-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
@@ -1783,9 +1783,11 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt,
			diagfwd_write_done(peripheral, type, num);
			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;
			spin_unlock_irqrestore(&driver->diagmem_lock, flags);
		} else {
			pr_err_ratelimited("diag: Invalid peripheral %d in %s, type: %d\n",
					   peripheral, __func__, type);
+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2008-2014, 2016-2017 The Linux Foundation. All rights reserved.
/* Copyright (c) 2008-2014, 2016-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
@@ -221,7 +221,7 @@ void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type)
			break;
		}
		spin_lock_irqsave(&mempool->lock, flags);
		if (mempool->count > 0) {
		if (mempool->count > 0 && buf) {
			mempool_free(buf, mempool->pool);
			atomic_add(-1, (atomic_t *)&mempool->count);
		} else {